AA: move logic to evaluate constant property initializer

so as to handle composite expressions w/ constant property
This commit is contained in:
Jinseong Jeon
2022-03-10 00:54:19 -08:00
committed by Ilya Kirillov
parent 606033e1e6
commit a7fcbfb717
11 changed files with 75 additions and 68 deletions
@@ -15,16 +15,10 @@ import org.jetbrains.kotlin.analysis.api.withValidityAssertion
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.getOrBuildFir
import org.jetbrains.kotlin.analysis.low.level.api.fir.api.throwUnexpectedFirElementError
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.declarations.utils.isConst
import org.jetbrains.kotlin.fir.declarations.utils.referredPropertySymbol
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression
import org.jetbrains.kotlin.fir.expressions.FirWhenBranch
import org.jetbrains.kotlin.fir.references.FirNamedReference
import org.jetbrains.kotlin.fir.resolvedSymbol
import org.jetbrains.kotlin.fir.symbols.ensureResolved
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.psi.KtExpression
internal class KtFirCompileTimeConstantProvider(
@@ -45,22 +39,15 @@ internal class KtFirCompileTimeConstantProvider(
mode: KtConstantEvaluationMode,
): KtConstantValue? = withValidityAssertion {
when (fir) {
is FirPropertyAccessExpression -> {
fir.referredPropertySymbol?.toKtConstantValue(context, mode)
}
is FirExpression -> {
is FirPropertyAccessExpression,
is FirExpression,
is FirNamedReference -> {
try {
FirCompileTimeConstantEvaluator.evaluateAsKtConstantExpression(fir)
FirCompileTimeConstantEvaluator.evaluateAsKtConstantValue(fir, mode)
} catch (e: ArithmeticException) {
KtConstantValue.KtErrorConstantValue(e.localizedMessage, context)
}
}
is FirNamedReference -> {
when (val resolvedSymbol = fir.resolvedSymbol) {
is FirPropertySymbol -> resolvedSymbol.toKtConstantValue(context, mode)
else -> null
}
}
// For invalid code like the following,
// ```
// when {
@@ -73,19 +60,4 @@ internal class KtFirCompileTimeConstantProvider(
else -> throwUnexpectedFirElementError(fir, context)
}
}
private fun FirPropertySymbol.toKtConstantValue(
context: KtExpression,
mode: KtConstantEvaluationMode,
): KtConstantValue? = withValidityAssertion {
if (isVal && hasInitializer) {
if (mode == KtConstantEvaluationMode.CONSTANT_EXPRESSION_EVALUATION && !isConst) {
null
} else {
// NB: the initializer could be [FirLazyExpression] in [BodyBuildingMode.LAZY_BODIES].
this.ensureResolved(FirResolvePhase.BODY_RESOLVE) // to unwrap lazy body
evaluateFir(fir.initializer, context, mode)
}
} else null
}
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.analysis.api.fir.evaluate
import org.jetbrains.kotlin.analysis.api.annotations.*
import org.jetbrains.kotlin.analysis.api.base.KtConstantValueFactory
import org.jetbrains.kotlin.analysis.api.components.KtConstantEvaluationMode
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClassSymbol
@@ -145,6 +146,7 @@ internal object FirAnnotationValueConverter {
}
}
else -> null
} ?: FirCompileTimeConstantEvaluator.evaluate(this)?.convertConstantExpression()
} ?: FirCompileTimeConstantEvaluator.evaluate(this, KtConstantEvaluationMode.CONSTANT_EXPRESSION_EVALUATION)
?.convertConstantExpression()
}
}
@@ -8,16 +8,22 @@ package org.jetbrains.kotlin.analysis.api.fir.evaluate
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.analysis.api.base.KtConstantValue
import org.jetbrains.kotlin.analysis.api.base.KtConstantValueFactory
import org.jetbrains.kotlin.analysis.api.components.KtConstantEvaluationMode
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.declarations.FirCallableDeclaration
import org.jetbrains.kotlin.fir.declarations.FirResolvePhase
import org.jetbrains.kotlin.fir.declarations.FirSimpleFunction
import org.jetbrains.kotlin.fir.expressions.FirConstExpression
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.expressions.FirFunctionCall
import org.jetbrains.kotlin.fir.expressions.argument
import org.jetbrains.kotlin.fir.declarations.utils.isConst
import org.jetbrains.kotlin.fir.declarations.utils.referredPropertySymbol
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.builder.buildConstExpression
import org.jetbrains.kotlin.fir.psi
import org.jetbrains.kotlin.fir.references.FirNamedReference
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolvedSymbol
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.ensureResolved
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.fir.types.impl.*
import org.jetbrains.kotlin.psi.KtElement
@@ -31,16 +37,49 @@ import org.jetbrains.kotlin.types.ConstantValueKind
* and the argument, are compile-time constant as well.
*/
internal object FirCompileTimeConstantEvaluator {
// TODO: Handle boolean operators, const property loading, class reference, array, annotation values, etc.
fun evaluate(expression: FirExpression): FirConstExpression<*>? =
when (expression) {
is FirConstExpression<*> -> expression.adaptToConstKind()
is FirFunctionCall -> evaluate(expression)
// TODO: Handle boolean operators, class reference, array, annotation values, etc.
fun evaluate(
fir: FirElement?,
mode: KtConstantEvaluationMode,
): FirConstExpression<*>? =
when (fir) {
is FirPropertyAccessExpression -> {
fir.referredPropertySymbol?.toConstExpression(mode)
}
is FirConstExpression<*> -> {
fir.adaptToConstKind()
}
is FirFunctionCall -> {
evaluateFunctionCall(fir, mode)
}
is FirNamedReference -> {
when (val resolvedSymbol = fir.resolvedSymbol) {
is FirPropertySymbol -> resolvedSymbol.toConstExpression(mode)
else -> null
}
}
else -> null
}
fun evaluateAsKtConstantExpression(expression: FirExpression): KtConstantValue? {
val evaluated = evaluate(expression) ?: return null
private fun FirPropertySymbol.toConstExpression(
mode: KtConstantEvaluationMode,
): FirConstExpression<*>? {
return when {
mode == KtConstantEvaluationMode.CONSTANT_EXPRESSION_EVALUATION && !isConst -> null
isVal && hasInitializer -> {
// NB: the initializer could be [FirLazyExpression] in [BodyBuildingMode.LAZY_BODIES].
this.ensureResolved(FirResolvePhase.BODY_RESOLVE) // to unwrap lazy body
evaluate(fir.initializer, mode)
}
else -> null
}
}
fun evaluateAsKtConstantValue(
fir: FirElement,
mode: KtConstantEvaluationMode,
): KtConstantValue? {
val evaluated = evaluate(fir, mode) ?: return null
val ktConstantValue = KtConstantValueFactory.createConstantValue(evaluated.value, evaluated.psi as? KtElement) ?: return null
check(ktConstantValue.constantValueKind == evaluated.kind) {
@@ -56,18 +95,18 @@ internal object FirCompileTimeConstantEvaluator {
)
}
// TODO: Rework to handle nested expressions
// This is no longer used during FIR2IR where an inner expression is recursively rewritten to ConstExpression if possible.
// Maybe rewrite this to a recursive version with caching either here or in provider.
private fun evaluate(functionCall: FirFunctionCall): FirConstExpression<*>? {
private fun evaluateFunctionCall(
functionCall: FirFunctionCall,
mode: KtConstantEvaluationMode,
): FirConstExpression<*>? {
val function = functionCall.getOriginalFunction() as? FirSimpleFunction ?: return null
val opr1 = functionCall.explicitReceiver as? FirConstExpression<*> ?: return null
val opr1 = evaluate(functionCall.explicitReceiver, mode) ?: return null
opr1.evaluate(function)?.let {
return it.adjustType(functionCall.typeRef)
}
val opr2 = functionCall.argument as? FirConstExpression<*> ?: return null
val opr2 = evaluate(functionCall.argument, mode) ?: return null
opr1.evaluate(function, opr2)?.let {
return it.adjustType(functionCall.typeRef)
}
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.analysis.api.fir.evaluate.FirCompileTimeConstantEval
import org.jetbrains.kotlin.analysis.api.fir.evaluate.FirAnnotationValueConverter
import org.jetbrains.kotlin.analysis.api.annotations.KtUnsupportedAnnotationValue
import org.jetbrains.kotlin.analysis.api.annotations.renderAsSourceCode
import org.jetbrains.kotlin.analysis.api.components.KtConstantEvaluationMode
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.toAnnotationClassId
@@ -60,7 +61,7 @@ private fun renderAndSortAnnotationArguments(descriptor: FirAnnotation, session:
}
private fun renderConstant(value: FirExpression, useSiteSession: FirSession): String {
val evaluated = FirCompileTimeConstantEvaluator.evaluate(value)
val evaluated = FirCompileTimeConstantEvaluator.evaluate(value, KtConstantEvaluationMode.CONSTANT_EXPRESSION_EVALUATION)
val constantValue = FirAnnotationValueConverter.toConstantValue(evaluated ?: value, useSiteSession)
?: KtUnsupportedAnnotationValue
@@ -8,6 +8,7 @@ import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.analysis.api.KtConstantInitializerValue
import org.jetbrains.kotlin.analysis.api.KtInitializerValue
import org.jetbrains.kotlin.analysis.api.KtNonConstantInitializerValue
import org.jetbrains.kotlin.analysis.api.components.KtConstantEvaluationMode
import org.jetbrains.kotlin.analysis.api.fir.evaluate.FirCompileTimeConstantEvaluator
import org.jetbrains.kotlin.analysis.api.fir.getCandidateSymbols
import org.jetbrains.kotlin.analysis.api.types.KtTypeNullability
@@ -28,7 +29,6 @@ import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
import org.jetbrains.kotlin.fir.types.ConeErrorType
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.ConeNullability
import org.jetbrains.kotlin.fir.types.coneType
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.*
@@ -91,7 +91,9 @@ internal fun FirCallableSymbol<*>.computeImportableName(useSiteSession: FirSessi
internal fun FirExpression.asKtInitializerValue(): KtInitializerValue {
val ktExpression = psi as? KtExpression
return when (val evaluated = FirCompileTimeConstantEvaluator.evaluateAsKtConstantExpression(this)) {
val evaluated =
FirCompileTimeConstantEvaluator.evaluateAsKtConstantValue(this, KtConstantEvaluationMode.CONSTANT_EXPRESSION_EVALUATION)
return when (evaluated) {
null -> KtNonConstantInitializerValue(ktExpression)
else -> KtConstantInitializerValue(evaluated, ktExpression)
}
@@ -1,3 +0,0 @@
expression: 42 / d
constant: 42
constantValueKind: Int
@@ -1,3 +1,3 @@
expression: 42 / d
constant: NOT_EVALUATED
constantValueKind: NOT_EVALUATED
constant: 42
constantValueKind: Int
@@ -1,3 +0,0 @@
expression: 42 / d
constant: 42
constantValueKind: Int
@@ -1,3 +1,3 @@
expression: 42 / d
constant: NOT_EVALUATED
constantValueKind: NOT_EVALUATED
constant: 42
constantValueKind: Int
@@ -1,3 +0,0 @@
expression: 42 / d
constant: 42
constantValueKind: Int
@@ -1,3 +1,3 @@
expression: 42 / d
constant: NOT_EVALUATED
constantValueKind: NOT_EVALUATED
constant: 42
constantValueKind: Int