[K2] Return from toConstantValue only if expected type match

There was a bug with early return from `toConstantValue`
method when we found any const value in `ConstValueProvider`.
Actually we should return only if expected type match. This error can
occur if annotation and constant inside it are fakes and
have the same offset.

#KT-57812
This commit is contained in:
Ivan Kylchik
2023-04-21 16:53:21 +02:00
committed by Space Team
parent 01749a910b
commit 846ad95c9e
5 changed files with 20 additions and 16 deletions
@@ -25,7 +25,7 @@ class FirAnnotationSerializer(
) { ) {
fun serializeAnnotation(annotation: FirAnnotation): ProtoBuf.Annotation { fun serializeAnnotation(annotation: FirAnnotation): ProtoBuf.Annotation {
// TODO this logic can be significantly simplified if we will find the way to convert `IrAnnotation` to `AnnotationValue` // TODO this logic can be significantly simplified if we will find the way to convert `IrAnnotation` to `AnnotationValue`
val annotationValue = annotation.toConstantValue(session, constValueProvider) as? AnnotationValue val annotationValue = annotation.toConstantValue<AnnotationValue>(session, constValueProvider)
?: error("Cannot serialize annotation ${annotation.render()}") ?: error("Cannot serialize annotation ${annotation.render()}")
return serializeAnnotation(annotationValue) return serializeAnnotation(annotationValue)
} }
@@ -1091,8 +1091,7 @@ class FirElementSerializer private constructor(
private fun serializeVersionRequirementFromRequireKotlin(annotation: FirAnnotation): ProtoBuf.VersionRequirement.Builder? { private fun serializeVersionRequirementFromRequireKotlin(annotation: FirAnnotation): ProtoBuf.VersionRequirement.Builder? {
val argumentMapping = annotation.argumentMapping.mapping val argumentMapping = annotation.argumentMapping.mapping
val versionString = val versionString = argumentMapping[RequireKotlinConstants.VERSION]?.toConstantValue<StringValue>(session)?.value ?: return null
(argumentMapping[RequireKotlinConstants.VERSION]?.toConstantValue(session) as? StringValue)?.value ?: return null
val matchResult = RequireKotlinConstants.VERSION_REGEX.matchEntire(versionString) ?: return null val matchResult = RequireKotlinConstants.VERSION_REGEX.matchEntire(versionString) ?: return null
val major = matchResult.groupValues.getOrNull(1)?.toIntOrNull() ?: return null val major = matchResult.groupValues.getOrNull(1)?.toIntOrNull() ?: return null
@@ -1105,12 +1104,12 @@ class FirElementSerializer private constructor(
writeVersionFull = { proto.versionFull = it } writeVersionFull = { proto.versionFull = it }
) )
val message = (argumentMapping[RequireKotlinConstants.MESSAGE]?.toConstantValue(session) as? StringValue)?.value val message = argumentMapping[RequireKotlinConstants.MESSAGE]?.toConstantValue<StringValue>(session)?.value
if (message != null) { if (message != null) {
proto.message = stringTable.getStringIndex(message) proto.message = stringTable.getStringIndex(message)
} }
when ((argumentMapping[RequireKotlinConstants.LEVEL]?.toConstantValue(session) as? EnumValue)?.enumEntryName?.asString()) { when (argumentMapping[RequireKotlinConstants.LEVEL]?.toConstantValue<EnumValue>(session)?.enumEntryName?.asString()) {
DeprecationLevel.ERROR.name -> { DeprecationLevel.ERROR.name -> {
// ERROR is the default level // ERROR is the default level
} }
@@ -1118,7 +1117,7 @@ class FirElementSerializer private constructor(
DeprecationLevel.HIDDEN.name -> proto.level = ProtoBuf.VersionRequirement.Level.HIDDEN DeprecationLevel.HIDDEN.name -> proto.level = ProtoBuf.VersionRequirement.Level.HIDDEN
} }
when ((argumentMapping[RequireKotlinConstants.VERSION_KIND]?.toConstantValue(session) as? EnumValue)?.enumEntryName?.asString()) { when (argumentMapping[RequireKotlinConstants.VERSION_KIND]?.toConstantValue<EnumValue>(session)?.enumEntryName?.asString()) {
ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION.name -> { ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION.name -> {
// LANGUAGE_VERSION is the default kind // LANGUAGE_VERSION is the default kind
} }
@@ -1128,7 +1127,7 @@ class FirElementSerializer private constructor(
proto.versionKind = ProtoBuf.VersionRequirement.VersionKind.API_VERSION proto.versionKind = ProtoBuf.VersionRequirement.VersionKind.API_VERSION
} }
val errorCode = (argumentMapping[RequireKotlinConstants.ERROR_CODE]?.toConstantValue(session) as? IntValue)?.value val errorCode = argumentMapping[RequireKotlinConstants.ERROR_CODE]?.toConstantValue<IntValue>(session)?.value
if (errorCode != null && errorCode != -1) { if (errorCode != null && errorCode != -1) {
proto.errorCode = errorCode proto.errorCode = errorCode
} }
@@ -30,11 +30,12 @@ abstract class FirSerializerExtension {
@OptIn(ConstValueProviderInternals::class) @OptIn(ConstValueProviderInternals::class)
internal inline fun <T> processFile(firFile: FirFile, action: () -> T): T { internal inline fun <T> processFile(firFile: FirFile, action: () -> T): T {
val previousFile = constValueProvider?.processingFirFile
constValueProvider?.processingFirFile = firFile constValueProvider?.processingFirFile = firFile
return try { return try {
action() action()
} finally { } finally {
constValueProvider?.processingFirFile = null constValueProvider?.processingFirFile = previousFile
} }
} }
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.fir.serialization package org.jetbrains.kotlin.fir.serialization
import org.jetbrains.kotlin.constant.ConstantValue
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
import org.jetbrains.kotlin.fir.FirAnnotationContainer import org.jetbrains.kotlin.fir.FirAnnotationContainer
import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.*
@@ -79,7 +80,7 @@ abstract class FirSerializerExtensionBase(
property.receiverParameter?.serializeAnnotations(proto, protocol.propertyExtensionReceiverAnnotation) property.receiverParameter?.serializeAnnotations(proto, protocol.propertyExtensionReceiverAnnotation)
if (!Flags.HAS_CONSTANT.get(proto.flags)) return if (!Flags.HAS_CONSTANT.get(proto.flags)) return
property.initializer?.toConstantValue(session, constValueProvider)?.let { property.initializer?.toConstantValue<ConstantValue<*>>(session, constValueProvider)?.let {
proto.setExtension(protocol.compileTimeValue, annotationSerializer.valueProto(it).build()) proto.setExtension(protocol.compileTimeValue, annotationSerializer.valueProto(it).build())
} }
} }
@@ -25,25 +25,28 @@ import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor import org.jetbrains.kotlin.fir.visitors.FirDefaultVisitor
import org.jetbrains.kotlin.types.ConstantValueKind import org.jetbrains.kotlin.types.ConstantValueKind
internal fun FirExpression.toConstantValue(session: FirSession, constValueProvider: ConstValueProvider? = null): ConstantValue<*>? { internal inline fun <reified T : ConstantValue<*>> FirExpression.toConstantValue(
constValueProvider?.findConstantValueFor(this)?.let { return it } session: FirSession,
return accept(FirToConstantValueTransformerUnsafe(), FirToConstantValueTransformerData(session, constValueProvider)) constValueProvider: ConstValueProvider? = null
): T? {
return constValueProvider?.findConstantValueFor(this) as? T
?: accept(FirToConstantValueTransformerUnsafe(), FirToConstantValueTransformerData(session, constValueProvider)) as? T
} }
internal fun FirExpression?.hasConstantValue(session: FirSession): Boolean { internal fun FirExpression?.hasConstantValue(session: FirSession): Boolean {
return this?.accept(FirToConstantValueChecker, session) == true return this?.accept(FirToConstantValueChecker, session) == true
} }
private class FirToConstantValueTransformerSafe : FirToConstantValueTransformer(failOnNonConst = false) internal class FirToConstantValueTransformerSafe : FirToConstantValueTransformer(failOnNonConst = false)
private class FirToConstantValueTransformerUnsafe : FirToConstantValueTransformer(failOnNonConst = true) internal class FirToConstantValueTransformerUnsafe : FirToConstantValueTransformer(failOnNonConst = true)
private data class FirToConstantValueTransformerData( internal data class FirToConstantValueTransformerData(
val session: FirSession, val session: FirSession,
val constValueProvider: ConstValueProvider?, val constValueProvider: ConstValueProvider?,
) )
private abstract class FirToConstantValueTransformer( internal abstract class FirToConstantValueTransformer(
private val failOnNonConst: Boolean, private val failOnNonConst: Boolean,
) : FirDefaultVisitor<ConstantValue<*>?, FirToConstantValueTransformerData>() { ) : FirDefaultVisitor<ConstantValue<*>?, FirToConstantValueTransformerData>() {
override fun visitElement( override fun visitElement(