Add new IntrinsicConstEvaluation language feature
This feature will allow to evaluate function marked with `IntrinsicConstEvaluation` annotation and so use such function in `const val`.
This commit is contained in:
+68
-8
@@ -5,10 +5,12 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.fir.analysis.checkers
|
package org.jetbrains.kotlin.fir.analysis.checkers
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
import org.jetbrains.kotlin.fir.*
|
||||||
import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId
|
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
||||||
|
import org.jetbrains.kotlin.fir.declarations.hasAnnotation
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.isConst
|
import org.jetbrains.kotlin.fir.declarations.utils.isConst
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.isStatic
|
import org.jetbrains.kotlin.fir.declarations.utils.isStatic
|
||||||
import org.jetbrains.kotlin.fir.declarations.utils.modality
|
import org.jetbrains.kotlin.fir.declarations.utils.modality
|
||||||
@@ -22,8 +24,9 @@ import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
|||||||
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
import org.jetbrains.kotlin.fir.symbols.SymbolInternals
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||||
import org.jetbrains.kotlin.fir.types.*
|
import org.jetbrains.kotlin.fir.types.*
|
||||||
import org.jetbrains.kotlin.fir.unwrapFakeOverrides
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.name.StandardClassIds
|
import org.jetbrains.kotlin.name.StandardClassIds
|
||||||
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
|
||||||
fun ConeKotlinType.canBeUsedForConstVal(): Boolean = with(lowerBoundIfFlexible()) { isPrimitive || isString || isUnsignedType }
|
fun ConeKotlinType.canBeUsedForConstVal(): Boolean = with(lowerBoundIfFlexible()) { isPrimitive || isString || isUnsignedType }
|
||||||
|
|
||||||
@@ -33,6 +36,11 @@ internal fun checkConstantArguments(
|
|||||||
): ConstantArgumentKind? {
|
): ConstantArgumentKind? {
|
||||||
val expressionSymbol = expression.toReference()?.toResolvedCallableSymbol(discardErrorReference = true)
|
val expressionSymbol = expression.toReference()?.toResolvedCallableSymbol(discardErrorReference = true)
|
||||||
val classKindOfParent = (expressionSymbol?.getReferencedClassSymbol(session) as? FirRegularClassSymbol)?.classKind
|
val classKindOfParent = (expressionSymbol?.getReferencedClassSymbol(session) as? FirRegularClassSymbol)?.classKind
|
||||||
|
val intrinsicConstEvaluation = session.languageVersionSettings.supportsFeature(LanguageFeature.IntrinsicConstEvaluation)
|
||||||
|
|
||||||
|
fun FirBasedSymbol<*>.canBeEvaluated(): Boolean {
|
||||||
|
return intrinsicConstEvaluation && this.hasAnnotation(INTRINSIC_CONST_EVALUATION_ANNOTATION, session)
|
||||||
|
}
|
||||||
|
|
||||||
when {
|
when {
|
||||||
expression is FirNamedArgumentExpression -> {
|
expression is FirNamedArgumentExpression -> {
|
||||||
@@ -40,7 +48,7 @@ internal fun checkConstantArguments(
|
|||||||
}
|
}
|
||||||
expression is FirTypeOperatorCall -> if (expression.operation == FirOperation.AS) return ConstantArgumentKind.NOT_CONST
|
expression is FirTypeOperatorCall -> if (expression.operation == FirOperation.AS) return ConstantArgumentKind.NOT_CONST
|
||||||
expression is FirWhenExpression -> {
|
expression is FirWhenExpression -> {
|
||||||
if (!expression.isProperlyExhaustive) {
|
if (!expression.isProperlyExhaustive || !intrinsicConstEvaluation) {
|
||||||
return ConstantArgumentKind.NOT_CONST
|
return ConstantArgumentKind.NOT_CONST
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,8 +133,7 @@ internal fun checkConstantArguments(
|
|||||||
if (calleeReference !is FirResolvedNamedReference) return ConstantArgumentKind.NOT_CONST
|
if (calleeReference !is FirResolvedNamedReference) return ConstantArgumentKind.NOT_CONST
|
||||||
val symbol = calleeReference.resolvedSymbol as? FirNamedFunctionSymbol ?: return ConstantArgumentKind.NOT_CONST
|
val symbol = calleeReference.resolvedSymbol as? FirNamedFunctionSymbol ?: return ConstantArgumentKind.NOT_CONST
|
||||||
|
|
||||||
@OptIn(SymbolInternals::class)
|
if (!symbol.canBeEvaluated() && !expression.isCompileTimeBuiltinCall()) {
|
||||||
if (symbol.fir.getAnnotationByClassId(INTRINSIC_CONST_EVALUATION_ANNOTATION, session) == null) {
|
|
||||||
return ConstantArgumentKind.NOT_CONST
|
return ConstantArgumentKind.NOT_CONST
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,8 +161,9 @@ internal fun checkConstantArguments(
|
|||||||
@OptIn(SymbolInternals::class)
|
@OptIn(SymbolInternals::class)
|
||||||
val property = propertySymbol.fir
|
val property = propertySymbol.fir
|
||||||
when {
|
when {
|
||||||
property.unwrapFakeOverrides().getAnnotationByClassId(INTRINSIC_CONST_EVALUATION_ANNOTATION, session) != null -> {
|
property.unwrapFakeOverrides().symbol.canBeEvaluated() || property.isCompileTimeBuiltinProperty() -> {
|
||||||
return checkConstantArguments(expression.dispatchReceiver, session)
|
val receiver = listOf(expression.dispatchReceiver, expression.extensionReceiver).single { it != FirNoReceiverExpression }
|
||||||
|
return checkConstantArguments(receiver, session)
|
||||||
}
|
}
|
||||||
propertySymbol.isLocal || propertySymbol.callableId.className?.isRoot == false -> return ConstantArgumentKind.NOT_CONST
|
propertySymbol.isLocal || propertySymbol.callableId.className?.isRoot == false -> return ConstantArgumentKind.NOT_CONST
|
||||||
expressionType.classId == StandardClassIds.KClass -> return ConstantArgumentKind.NOT_KCLASS_LITERAL
|
expressionType.classId == StandardClassIds.KClass -> return ConstantArgumentKind.NOT_KCLASS_LITERAL
|
||||||
@@ -177,6 +185,58 @@ internal fun checkConstantArguments(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val compileTimeFunctions = setOf(
|
||||||
|
*OperatorNameConventions.BINARY_OPERATION_NAMES.toTypedArray(), *OperatorNameConventions.UNARY_OPERATION_NAMES.toTypedArray(),
|
||||||
|
OperatorNameConventions.SHL, OperatorNameConventions.SHR, OperatorNameConventions.USHR,
|
||||||
|
OperatorNameConventions.OR, OperatorNameConventions.AND, OperatorNameConventions.XOR,
|
||||||
|
OperatorNameConventions.COMPARE_TO
|
||||||
|
)
|
||||||
|
|
||||||
|
private val compileTimeExtensionFunctions = listOf("floorDiv", "mod", "code").mapTo(hashSetOf()) { Name.identifier(it) }
|
||||||
|
|
||||||
|
private val compileTimeConversionFunctions = listOf(
|
||||||
|
"toInt", "toLong", "toShort", "toByte", "toFloat", "toDouble", "toChar", "toBoolean"
|
||||||
|
).mapTo(hashSetOf()) { Name.identifier(it) }
|
||||||
|
|
||||||
|
private fun FirFunctionCall.isCompileTimeBuiltinCall(): Boolean {
|
||||||
|
val calleeReference = this.calleeReference
|
||||||
|
if (calleeReference !is FirResolvedNamedReference) return false
|
||||||
|
|
||||||
|
val name = calleeReference.name
|
||||||
|
val symbol = calleeReference.resolvedSymbol as? FirCallableSymbol
|
||||||
|
if (!symbol.fromKotlin()) return false
|
||||||
|
|
||||||
|
val coneType = this.dispatchReceiver.typeRef.coneTypeSafe<ConeKotlinType>()
|
||||||
|
val receiverClassId = coneType?.lowerBoundIfFlexible()?.classId
|
||||||
|
|
||||||
|
if (receiverClassId in StandardClassIds.unsignedTypes) return false
|
||||||
|
|
||||||
|
if (
|
||||||
|
name in compileTimeFunctions ||
|
||||||
|
name in compileTimeExtensionFunctions ||
|
||||||
|
name == OperatorNameConventions.TO_STRING ||
|
||||||
|
name in compileTimeConversionFunctions
|
||||||
|
) return true
|
||||||
|
|
||||||
|
if (calleeReference.name == OperatorNameConventions.GET && receiverClassId == StandardClassIds.String) return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun FirProperty.isCompileTimeBuiltinProperty(): Boolean {
|
||||||
|
val receiverType = dispatchReceiverType ?: receiverParameter?.typeRef?.coneTypeSafe<ConeKotlinType>() ?: return false
|
||||||
|
val receiverClassId = receiverType.lowerBoundIfFlexible().classId ?: return false
|
||||||
|
return when (name.asString()) {
|
||||||
|
"length" -> receiverClassId == StandardClassIds.String
|
||||||
|
"code" -> receiverClassId == StandardClassIds.Char
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun FirCallableSymbol<*>?.fromKotlin(): Boolean {
|
||||||
|
return this?.callableId?.packageName?.asString() == "kotlin"
|
||||||
|
}
|
||||||
|
|
||||||
private fun FirCallableSymbol<*>?.getReferencedClassSymbol(session: FirSession): FirBasedSymbol<*>? =
|
private fun FirCallableSymbol<*>?.getReferencedClassSymbol(session: FirSession): FirBasedSymbol<*>? =
|
||||||
this?.resolvedReturnTypeRef
|
this?.resolvedReturnTypeRef
|
||||||
?.coneTypeSafe<ConeLookupTagBasedType>()
|
?.coneTypeSafe<ConeLookupTagBasedType>()
|
||||||
|
|||||||
@@ -333,6 +333,7 @@ enum class LanguageFeature(
|
|||||||
ContractSyntaxV2(sinceVersion = null, kind = UNSTABLE_FEATURE), // KT-56127
|
ContractSyntaxV2(sinceVersion = null, kind = UNSTABLE_FEATURE), // KT-56127
|
||||||
ImplicitSignedToUnsignedIntegerConversion(sinceVersion = null), // KT-56583
|
ImplicitSignedToUnsignedIntegerConversion(sinceVersion = null), // KT-56583
|
||||||
ForbidInferringTypeVariablesIntoEmptyIntersection(sinceVersion = null, kind = BUG_FIX), // KT-51221
|
ForbidInferringTypeVariablesIntoEmptyIntersection(sinceVersion = null, kind = BUG_FIX), // KT-51221
|
||||||
|
IntrinsicConstEvaluation(sinceVersion = null, kind = UNSTABLE_FEATURE), // KT-49303
|
||||||
;
|
;
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
|||||||
Reference in New Issue
Block a user