From 7c9e5a1a4b6576a156ffbb30651a90223c07cddb Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Wed, 20 Dec 2023 20:48:16 +0100 Subject: [PATCH] [FIR] Move const check utils functions into `FirConstCheckVisitor` This is the first step of moving all const check code into a visitor class. --- .../fir/analysis/checkers/FirConstChecks.kt | 133 ++++++++++-------- 1 file changed, 73 insertions(+), 60 deletions(-) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirConstChecks.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirConstChecks.kt index d32a2cb75e8..24212e1e503 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirConstChecks.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirConstChecks.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.fir.analysis.checkers import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.declarations.hasAnnotation import org.jetbrains.kotlin.fir.declarations.utils.isConst @@ -25,6 +26,7 @@ import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.unwrapFakeOverrides +import org.jetbrains.kotlin.fir.visitors.FirVisitor import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.StandardClassIds import org.jetbrains.kotlin.util.OperatorNameConventions @@ -40,8 +42,13 @@ internal fun checkConstantArguments( session: FirSession, ): ConstantArgumentKind { if (expression == null) return ConstantArgumentKind.VALID_CONST + + val firConstCheckVisitor = FirConstCheckVisitor() + val expressionSymbol = expression.toReference()?.toResolvedCallableSymbol(discardErrorReference = true) - val classKindOfParent = (expressionSymbol?.getReferencedClassSymbol(session) as? FirRegularClassSymbol)?.classKind + val classKindOfParent = with(firConstCheckVisitor) { + (expressionSymbol?.getReferencedClassSymbol(session) as? FirRegularClassSymbol)?.classKind + } val intrinsicConstEvaluation = session.languageVersionSettings.supportsFeature(LanguageFeature.IntrinsicConstEvaluation) fun FirBasedSymbol<*>.canBeEvaluated(): Boolean { @@ -163,7 +170,7 @@ internal fun checkConstantArguments( if (calleeReference !is FirResolvedNamedReference) return ConstantArgumentKind.NOT_CONST val symbol = calleeReference.resolvedSymbol as? FirNamedFunctionSymbol ?: return ConstantArgumentKind.NOT_CONST - if (!symbol.canBeEvaluated() && !expression.isCompileTimeBuiltinCall(session)) { + if (!symbol.canBeEvaluated() && !with(firConstCheckVisitor) { expression.isCompileTimeBuiltinCall(session) }) { return ConstantArgumentKind.NOT_CONST } @@ -192,7 +199,7 @@ internal fun checkConstantArguments( val propertySymbol = expressionSymbol as? FirPropertySymbol ?: return ConstantArgumentKind.NOT_CONST when { - propertySymbol.unwrapFakeOverrides().canBeEvaluated() || propertySymbol.isCompileTimeBuiltinProperty(session) -> { + propertySymbol.unwrapFakeOverrides().canBeEvaluated() || with(firConstCheckVisitor) { propertySymbol.isCompileTimeBuiltinProperty(session) } -> { val receiver = listOf(expression.dispatchReceiver, expression.extensionReceiver).single { it != null }!! return checkConstantArguments(receiver, session) } @@ -215,63 +222,6 @@ internal fun checkConstantArguments( return ConstantArgumentKind.VALID_CONST } -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(session: FirSession): 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 receiverClassId = this.dispatchReceiver?.resolvedType?.fullyExpandedClassId(session) - - 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 FirPropertySymbol.isCompileTimeBuiltinProperty(session: FirSession): Boolean { - val receiverType = dispatchReceiverType ?: receiverParameter?.typeRef?.coneTypeSafe() ?: return false - val receiverClassId = receiverType.fullyExpandedClassId(session) ?: 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<*>? = - this?.resolvedReturnTypeRef - ?.coneTypeSafe() - ?.lookupTag - ?.toSymbol(session) - internal enum class ConstantArgumentKind { VALID_CONST, NOT_CONST, @@ -286,3 +236,66 @@ internal enum class ConstantArgumentKind { } } } + +private class FirConstCheckVisitor() : FirVisitor() { + 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) } + + override fun visitElement(element: FirElement, data: Nothing?): ConstantArgumentKind { + return ConstantArgumentKind.NOT_CONST + } + + fun FirFunctionCall.isCompileTimeBuiltinCall(session: FirSession): 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 receiverClassId = this.dispatchReceiver?.resolvedType?.fullyExpandedClassId(session) + + 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 + } + + fun FirPropertySymbol.isCompileTimeBuiltinProperty(session: FirSession): Boolean { + val receiverType = dispatchReceiverType ?: receiverParameter?.typeRef?.coneTypeSafe() ?: return false + val receiverClassId = receiverType.fullyExpandedClassId(session) ?: 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" + } + + fun FirCallableSymbol<*>?.getReferencedClassSymbol(session: FirSession): FirBasedSymbol<*>? = + this?.resolvedReturnTypeRef + ?.coneTypeSafe() + ?.lookupTag + ?.toSymbol(session) +} \ No newline at end of file