[K2] Properly serialize constant values
#KT-56190 Fixed
This commit is contained in:
@@ -14,6 +14,7 @@ dependencies {
|
||||
api(project(":compiler:fir:tree"))
|
||||
api(project(":compiler:fir:providers"))
|
||||
api(project(":compiler:fir:semantics"))
|
||||
api(project(":compiler:fir:resolve"))
|
||||
|
||||
compileOnly(intellijCore())
|
||||
}
|
||||
|
||||
+3
-1
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.fir.extensions.typeAttributeExtensions
|
||||
import org.jetbrains.kotlin.fir.resolve.*
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.scopes.*
|
||||
import org.jetbrains.kotlin.fir.serialization.constant.*
|
||||
import org.jetbrains.kotlin.fir.serialization.constant.EnumValue
|
||||
import org.jetbrains.kotlin.fir.serialization.constant.IntValue
|
||||
import org.jetbrains.kotlin.fir.serialization.constant.StringValue
|
||||
@@ -375,12 +376,13 @@ class FirElementSerializer private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
val hasConstant = (!property.isVar && property.initializer.hasConstantValue(session)) || property.isConst
|
||||
val flags = Flags.getPropertyFlags(
|
||||
hasAnnotations,
|
||||
ProtoEnumFlags.visibility(normalizeVisibility(property)),
|
||||
ProtoEnumFlags.modality(modality),
|
||||
ProtoBuf.MemberKind.DECLARATION,
|
||||
property.isVar, hasGetter, hasSetter, property.isConst, property.isConst, property.isLateInit,
|
||||
property.isVar, hasGetter, hasSetter, hasConstant, property.isConst, property.isLateInit,
|
||||
property.isExternal, property.delegateFieldSymbol != null, property.isExpect
|
||||
)
|
||||
if (flags != builder.flags) {
|
||||
|
||||
+2
@@ -9,10 +9,12 @@ import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
|
||||
import org.jetbrains.kotlin.fir.serialization.constant.ConstValueProvider
|
||||
import org.jetbrains.kotlin.fir.serialization.constant.buildValueProtoBufIfPropertyHasConst
|
||||
import org.jetbrains.kotlin.fir.types.ConeErrorType
|
||||
import org.jetbrains.kotlin.fir.types.ConeFlexibleType
|
||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
|
||||
import org.jetbrains.kotlin.metadata.deserialization.Flags
|
||||
import org.jetbrains.kotlin.metadata.serialization.MutableVersionRequirementTable
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
|
||||
+4
-4
@@ -9,10 +9,9 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget
|
||||
import org.jetbrains.kotlin.fir.FirAnnotationContainer
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
|
||||
import org.jetbrains.kotlin.fir.serialization.constant.ConstValueProvider
|
||||
import org.jetbrains.kotlin.fir.serialization.constant.buildValueProtoBufIfPropertyIsConst
|
||||
import org.jetbrains.kotlin.library.metadata.KlibMetadataProtoBuf
|
||||
import org.jetbrains.kotlin.fir.serialization.constant.buildValueProtoBufIfPropertyHasConst
|
||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.deserialization.Flags
|
||||
import org.jetbrains.kotlin.metadata.serialization.MutableVersionRequirementTable
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.protobuf.GeneratedMessageLite
|
||||
@@ -81,7 +80,8 @@ abstract class FirSerializerExtensionBase(
|
||||
property.setter?.serializeAnnotations(proto, protocol.propertySetterAnnotation)
|
||||
property.receiverParameter?.serializeAnnotations(proto, protocol.propertyExtensionReceiverAnnotation, property)
|
||||
|
||||
constValueProvider?.buildValueProtoBufIfPropertyIsConst(property, annotationSerializer)?.let { constProtoBuf ->
|
||||
if (!Flags.HAS_CONSTANT.get(proto.flags)) return
|
||||
constValueProvider?.buildValueProtoBufIfPropertyHasConst(property, annotationSerializer)?.let { constProtoBuf ->
|
||||
proto.setExtension(protocol.compileTimeValue, constProtoBuf)
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -12,14 +12,14 @@ import org.jetbrains.kotlin.fir.declarations.FirPropertyAccessor
|
||||
import org.jetbrains.kotlin.fir.declarations.FirReceiverParameter
|
||||
import org.jetbrains.kotlin.fir.declarations.FirValueParameter
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
|
||||
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.serialization.FirAnnotationSerializer
|
||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||
|
||||
abstract class ConstValueProvider {
|
||||
abstract val session: FirSession
|
||||
|
||||
abstract fun getConstantValueForProperty(firProperty: FirProperty): FirConstExpression<*>?
|
||||
abstract fun getConstantValueForProperty(firProperty: FirProperty): FirExpression?
|
||||
|
||||
abstract fun getNewFirAnnotationWithConstantValues(
|
||||
firAnnotationContainer: FirAnnotationContainer,
|
||||
@@ -44,13 +44,13 @@ abstract class ConstValueProvider {
|
||||
firAnnotation: FirAnnotation,
|
||||
): FirAnnotation
|
||||
|
||||
fun FirConstExpression<*>?.toProtoBuf(annotationSerializer: FirAnnotationSerializer): ProtoBuf.Annotation.Argument.Value? {
|
||||
fun FirExpression?.toProtoBuf(annotationSerializer: FirAnnotationSerializer): ProtoBuf.Annotation.Argument.Value? {
|
||||
val constantValue = this?.toConstantValue(session) ?: return null
|
||||
return annotationSerializer.valueProto(constantValue).build()
|
||||
}
|
||||
}
|
||||
|
||||
fun ConstValueProvider.buildValueProtoBufIfPropertyIsConst(
|
||||
fun ConstValueProvider.buildValueProtoBufIfPropertyHasConst(
|
||||
firProperty: FirProperty, annotationSerializer: FirAnnotationSerializer
|
||||
): ProtoBuf.Annotation.Argument.Value? {
|
||||
return getConstantValueForProperty(firProperty).toProtoBuf(annotationSerializer)
|
||||
|
||||
+82
-4
@@ -6,16 +6,15 @@
|
||||
package org.jetbrains.kotlin.fir.serialization.constant
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.containingClassLookupTag
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.declarations.FirEnumEntry
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationArgumentMapping
|
||||
import org.jetbrains.kotlin.fir.expressions.builder.buildAnnotationCall
|
||||
import org.jetbrains.kotlin.fir.references.builder.buildSimpleNamedReference
|
||||
import org.jetbrains.kotlin.fir.render
|
||||
import org.jetbrains.kotlin.fir.resolve.toFirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirArrayOfCallTransformer
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirArrayOfCallTransformer.Companion.isArrayOfCall
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
import org.jetbrains.kotlin.fir.types.classId
|
||||
@@ -28,6 +27,10 @@ internal fun FirExpression.toConstantValue(session: FirSession): ConstantValue<*
|
||||
return accept(FirToConstantValueTransformerUnsafe, session)
|
||||
}
|
||||
|
||||
internal fun FirExpression?.hasConstantValue(session: FirSession): Boolean {
|
||||
return this?.accept(FirToConstantValueChecker, session) == true
|
||||
}
|
||||
|
||||
private object FirToConstantValueTransformerSafe : FirToConstantValueTransformer(failOnNonConst = false)
|
||||
private object FirToConstantValueTransformerUnsafe : FirToConstantValueTransformer(failOnNonConst = true)
|
||||
|
||||
@@ -162,6 +165,9 @@ private abstract class FirToConstantValueTransformer(
|
||||
functionCall: FirFunctionCall,
|
||||
data: FirSession
|
||||
): ConstantValue<*>? {
|
||||
if (functionCall.isArrayOfCall) {
|
||||
return FirArrayOfCallTransformer().transformFunctionCall(functionCall, null).accept(this, data)
|
||||
}
|
||||
return visitQualifiedAccessExpression(functionCall, data)
|
||||
}
|
||||
|
||||
@@ -176,3 +182,75 @@ private abstract class FirToConstantValueTransformer(
|
||||
return namedArgumentExpression.expression.accept(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
internal object FirToConstantValueChecker : FirDefaultVisitor<Boolean, FirSession>() {
|
||||
// `null` value is not treated as a const
|
||||
private val supportedConstKinds = setOf<ConstantValueKind<*>>(
|
||||
ConstantValueKind.Boolean, ConstantValueKind.Char, ConstantValueKind.String, ConstantValueKind.Float, ConstantValueKind.Double,
|
||||
ConstantValueKind.Byte, ConstantValueKind.UnsignedByte, ConstantValueKind.Short, ConstantValueKind.UnsignedShort,
|
||||
ConstantValueKind.Int, ConstantValueKind.UnsignedInt, ConstantValueKind.Long, ConstantValueKind.UnsignedLong,
|
||||
)
|
||||
|
||||
override fun visitElement(element: FirElement, data: FirSession): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun <T> visitConstExpression(
|
||||
constExpression: FirConstExpression<T>,
|
||||
data: FirSession
|
||||
): Boolean {
|
||||
return constExpression.kind in supportedConstKinds
|
||||
}
|
||||
|
||||
override fun visitArrayOfCall(arrayOfCall: FirArrayOfCall, data: FirSession): Boolean {
|
||||
return arrayOfCall.arguments.all { it.accept(this, data) }
|
||||
}
|
||||
|
||||
override fun visitAnnotation(annotation: FirAnnotation, data: FirSession): Boolean = true
|
||||
|
||||
override fun visitAnnotationCall(annotationCall: FirAnnotationCall, data: FirSession): Boolean = true
|
||||
|
||||
override fun visitGetClassCall(getClassCall: FirGetClassCall, data: FirSession): Boolean {
|
||||
return KClassValue.create(getClassCall.argument.typeRef.coneTypeUnsafe()) != null
|
||||
}
|
||||
|
||||
override fun visitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression, data: FirSession): Boolean {
|
||||
val symbol = qualifiedAccessExpression.toResolvedCallableSymbol() ?: return false
|
||||
|
||||
return when {
|
||||
symbol.fir is FirEnumEntry -> symbol.fir.returnTypeRef.coneTypeSafe<ConeClassLikeType>()?.classId != null
|
||||
|
||||
symbol is FirConstructorSymbol -> {
|
||||
symbol.containingClassLookupTag()?.toFirRegularClassSymbol(data)?.classKind == ClassKind.ANNOTATION_CLASS
|
||||
}
|
||||
|
||||
symbol.callableId.packageName.asString() == "kotlin" -> {
|
||||
val dispatchReceiver = qualifiedAccessExpression.dispatchReceiver
|
||||
when (symbol.callableId.callableName.asString()) {
|
||||
in setOf("toByte", "toLong", "toShort", "toFloat", "toDouble", "toChar") -> true
|
||||
"unaryMinus" -> dispatchReceiver.accept(this, data)
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitPropertyAccessExpression(propertyAccessExpression: FirPropertyAccessExpression, data: FirSession): Boolean {
|
||||
return visitQualifiedAccessExpression(propertyAccessExpression, data)
|
||||
}
|
||||
|
||||
override fun visitFunctionCall(functionCall: FirFunctionCall, data: FirSession): Boolean {
|
||||
if (functionCall.isArrayOfCall) return functionCall.arguments.all { it.accept(this, data) }
|
||||
return visitQualifiedAccessExpression(functionCall, data)
|
||||
}
|
||||
|
||||
override fun visitVarargArgumentsExpression(varargArgumentsExpression: FirVarargArgumentsExpression, data: FirSession): Boolean {
|
||||
return varargArgumentsExpression.arguments.all { it.accept(this, data) }
|
||||
}
|
||||
|
||||
override fun visitNamedArgumentExpression(namedArgumentExpression: FirNamedArgumentExpression, data: FirSession): Boolean {
|
||||
return namedArgumentExpression.expression.accept(this, data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,11 +28,11 @@ class ConstValueProviderImpl(
|
||||
) : ConstValueProvider() {
|
||||
override val session: FirSession = components.session
|
||||
|
||||
override fun getConstantValueForProperty(firProperty: FirProperty): FirConstExpression<*>? {
|
||||
val irProperty: IrProperty = components.declarationStorage.getCachedIrProperty(firProperty) ?: return null
|
||||
if (!irProperty.isConst) return null
|
||||
val irConst = irProperty.backingField?.initializer?.expression as? IrConst<*> ?: return null
|
||||
return irConst.toFirConst()
|
||||
override fun getConstantValueForProperty(firProperty: FirProperty): FirExpression? {
|
||||
val firInitializer = firProperty.initializer
|
||||
val irProperty: IrProperty = components.declarationStorage.getCachedIrProperty(firProperty) ?: return firInitializer
|
||||
val irConst = irProperty.backingField?.initializer?.expression as? IrConst<*> ?: return firInitializer
|
||||
return irConst.toFirConst() ?: firInitializer
|
||||
}
|
||||
|
||||
override fun getNewFirAnnotationWithConstantValues(
|
||||
|
||||
+10
-10
@@ -23,16 +23,7 @@ import org.jetbrains.kotlin.fir.visitors.FirDefaultTransformer
|
||||
*
|
||||
* Note that arrayOf() calls only in [FirAnnotation] or the default value of annotation constructor are transformed.
|
||||
*/
|
||||
internal class FirArrayOfCallTransformer : FirDefaultTransformer<Nothing?>() {
|
||||
private val FirFunctionCall.isArrayOfCall: Boolean
|
||||
get() {
|
||||
val function: FirCallableDeclaration = getOriginalFunction() ?: return false
|
||||
return function is FirSimpleFunction &&
|
||||
function.returnTypeRef.isArrayType &&
|
||||
isArrayOf(function, arguments) &&
|
||||
function.receiverParameter == null
|
||||
}
|
||||
|
||||
class FirArrayOfCallTransformer : FirDefaultTransformer<Nothing?>() {
|
||||
private fun toArrayOfCall(functionCall: FirFunctionCall): FirArrayOfCall? {
|
||||
if (!functionCall.isArrayOfCall) {
|
||||
return null
|
||||
@@ -63,6 +54,15 @@ internal class FirArrayOfCallTransformer : FirDefaultTransformer<Nothing?>() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
val FirFunctionCall.isArrayOfCall: Boolean
|
||||
get() {
|
||||
val function: FirCallableDeclaration = getOriginalFunction() ?: return false
|
||||
return function is FirSimpleFunction &&
|
||||
function.returnTypeRef.isArrayType &&
|
||||
isArrayOf(function, arguments) &&
|
||||
function.receiverParameter == null
|
||||
}
|
||||
|
||||
private val arrayOfNames = hashSetOf("kotlin/arrayOf") +
|
||||
hashSetOf(
|
||||
"boolean", "byte", "char", "double", "float", "int", "long", "short",
|
||||
|
||||
+5
-1
@@ -153,7 +153,11 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) :
|
||||
val receiverType = irFunction.dispatchReceiverParameter?.type ?: irFunction.extensionReceiverParameter?.type
|
||||
val argsType = (listOfNotNull(receiverType) + irFunction.valueParameters.map { it.type }).map {
|
||||
// TODO: for consistency with current K/JS implementation Float constant should be treated as a Double (KT-35422)
|
||||
if (environment.configuration.treatFloatInSpecialWay && it.isFloat()) irBuiltIns.doubleType else it
|
||||
if (environment.configuration.treatFloatInSpecialWay && it.makeNotNull().isFloat()) {
|
||||
if (it.isNullable()) irBuiltIns.doubleType.makeNullable() else irBuiltIns.doubleType
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
val argsValues = args.wrap(this, irFunction)
|
||||
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ enum class EvaluationMode(protected val mustCheckBody: Boolean) {
|
||||
private val forbiddenMethodsOnStrings = setOf("subSequence", "hashCode", "<init>")
|
||||
private val allowedExtensionFunctions = setOf(
|
||||
"kotlin.floorDiv", "kotlin.mod", "kotlin.NumbersKt.floorDiv", "kotlin.NumbersKt.mod", "kotlin.<get-code>",
|
||||
"kotlin.internal.ir.EQEQ",
|
||||
"kotlin.internal.ir.EQEQ", "kotlin.internal.ir.ieee754equals",
|
||||
)
|
||||
|
||||
override fun canEvaluateFunction(function: IrFunction, context: IrCall?): Boolean {
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
// KT-56190 K2 does not emit const initializers
|
||||
// MUTED_WHEN: K2
|
||||
// `val b = a + 20` will not be serialized in K2
|
||||
package test
|
||||
|
||||
enum class Weapon {
|
||||
|
||||
+4
-2
@@ -1,5 +1,3 @@
|
||||
// KT-56190 K2 does not emit const initializers
|
||||
// MUTED_WHEN: K2
|
||||
package test
|
||||
|
||||
class ClassA {
|
||||
@@ -18,7 +16,9 @@ class ClassA {
|
||||
inner class classC {
|
||||
val memberFromC: Int = 200
|
||||
}
|
||||
}
|
||||
|
||||
class E {
|
||||
companion object {
|
||||
val stat: Int = 250
|
||||
|
||||
@@ -26,7 +26,9 @@ class ClassA {
|
||||
val memberFromD: Int = 275
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class F {
|
||||
object ObjA {
|
||||
val memberFromObjA: Int = 300
|
||||
}
|
||||
|
||||
+13
-9
@@ -1,13 +1,4 @@
|
||||
class ClassA constructor() {
|
||||
companion object {
|
||||
val stat: Int = 250
|
||||
class D constructor() {
|
||||
val memberFromD: Int = 275
|
||||
}
|
||||
}
|
||||
object ObjA {
|
||||
val memberFromObjA: Int = 300
|
||||
}
|
||||
class classB constructor() {
|
||||
fun memberFromB(): Int
|
||||
class BC constructor() {
|
||||
@@ -20,4 +11,17 @@
|
||||
inner class classC constructor() {
|
||||
val memberFromC: Int = 200
|
||||
}
|
||||
}
|
||||
class E constructor() {
|
||||
companion object {
|
||||
val stat: Int = 250
|
||||
class D constructor() {
|
||||
val memberFromD: Int = 275
|
||||
}
|
||||
}
|
||||
}
|
||||
class F constructor() {
|
||||
object ObjA {
|
||||
val memberFromObjA: Int = 300
|
||||
}
|
||||
}
|
||||
Vendored
-2
@@ -1,5 +1,3 @@
|
||||
// KT-56190 K2 does not emit const initializers
|
||||
// MUTED_WHEN: K2
|
||||
package test
|
||||
|
||||
annotation class Anno(val value: String)
|
||||
|
||||
Reference in New Issue
Block a user