[IR] Create special checker that will analyze name methods
We can insert all this logic into `IrCompileTimeChecker` but it is a little bit specific and looks like it is nicer to just extract it.
This commit is contained in:
+6
@@ -28510,6 +28510,12 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr
|
||||
runTest("compiler/testData/codegen/box/involvesIrInterpreter/intrinsicConst/kCallableName.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kCallableNameWithSideEffect.kt")
|
||||
public void testKCallableNameWithSideEffect() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/involvesIrInterpreter/intrinsicConst/kCallableNameWithSideEffect.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt53272.kt")
|
||||
public void testKt53272() throws Exception {
|
||||
|
||||
+6
@@ -28510,6 +28510,12 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo
|
||||
runTest("compiler/testData/codegen/box/involvesIrInterpreter/intrinsicConst/kCallableName.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kCallableNameWithSideEffect.kt")
|
||||
public void testKCallableNameWithSideEffect() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/involvesIrInterpreter/intrinsicConst/kCallableNameWithSideEffect.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt53272.kt")
|
||||
public void testKt53272() throws Exception {
|
||||
|
||||
+14
-29
@@ -20,10 +20,10 @@ import org.jetbrains.kotlin.name.Name
|
||||
|
||||
enum class EvaluationMode {
|
||||
FULL {
|
||||
override fun canEvaluateFunction(function: IrFunction, context: IrCall?): Boolean = true
|
||||
override fun canEvaluateEnumValue(enumEntry: IrGetEnumValue, context: IrCall?): Boolean = true
|
||||
override fun canEvaluateFunctionExpression(expression: IrFunctionExpression, context: IrCall?): Boolean = true
|
||||
override fun canEvaluateCallableReference(reference: IrCallableReference<*>, context: IrCall?): Boolean = true
|
||||
override fun canEvaluateFunction(function: IrFunction): Boolean = true
|
||||
override fun canEvaluateEnumValue(enumEntry: IrGetEnumValue): Boolean = true
|
||||
override fun canEvaluateFunctionExpression(expression: IrFunctionExpression): Boolean = true
|
||||
override fun canEvaluateCallableReference(reference: IrCallableReference<*>): Boolean = true
|
||||
override fun canEvaluateClassReference(reference: IrDeclarationReference): Boolean = true
|
||||
|
||||
override fun canEvaluateBlock(block: IrBlock): Boolean = true
|
||||
@@ -54,7 +54,7 @@ enum class EvaluationMode {
|
||||
BuiltInOperatorNames.ANDAND, BuiltInOperatorNames.OROR
|
||||
).map { IrBuiltIns.KOTLIN_INTERNAL_IR_FQN.child(Name.identifier(it)).asString() }.toSet()
|
||||
|
||||
override fun canEvaluateFunction(function: IrFunction, context: IrCall?): Boolean {
|
||||
override fun canEvaluateFunction(function: IrFunction): Boolean {
|
||||
if (function.property?.isConst == true) return true
|
||||
|
||||
val returnType = function.returnType
|
||||
@@ -78,10 +78,8 @@ enum class EvaluationMode {
|
||||
},
|
||||
|
||||
ONLY_INTRINSIC_CONST {
|
||||
override fun canEvaluateFunction(function: IrFunction, context: IrCall?): Boolean {
|
||||
return function.isCompileTimePropertyAccessor() ||
|
||||
function.isMarkedAsIntrinsicConstEvaluation() ||
|
||||
context.isIntrinsicConstEvaluationNameProperty()
|
||||
override fun canEvaluateFunction(function: IrFunction): Boolean {
|
||||
return function.isCompileTimePropertyAccessor() || function.isMarkedAsIntrinsicConstEvaluation()
|
||||
}
|
||||
|
||||
private fun IrFunction?.isCompileTimePropertyAccessor(): Boolean {
|
||||
@@ -89,29 +87,14 @@ enum class EvaluationMode {
|
||||
return property.isConst || (property.resolveFakeOverride() ?: property).isMarkedAsIntrinsicConstEvaluation()
|
||||
}
|
||||
|
||||
override fun canEvaluateEnumValue(enumEntry: IrGetEnumValue, context: IrCall?): Boolean {
|
||||
return context.isIntrinsicConstEvaluationNameProperty()
|
||||
}
|
||||
|
||||
override fun canEvaluateCallableReference(reference: IrCallableReference<*>, context: IrCall?): Boolean {
|
||||
return context.isIntrinsicConstEvaluationNameProperty()
|
||||
}
|
||||
|
||||
override fun canEvaluateBlock(block: IrBlock): Boolean = block.origin == IrStatementOrigin.WHEN || block.statements.size == 1
|
||||
override fun canEvaluateExpression(expression: IrExpression): Boolean = expression is IrCall || expression is IrWhen
|
||||
|
||||
private fun IrCall?.isIntrinsicConstEvaluationNameProperty(): Boolean {
|
||||
if (this == null) return false
|
||||
val owner = this.symbol.owner
|
||||
val property = owner.property ?: return false
|
||||
return owner.isCompileTimePropertyAccessor() && property.name.asString() == "name"
|
||||
}
|
||||
};
|
||||
|
||||
open fun canEvaluateFunction(function: IrFunction, context: IrCall? = null): Boolean = false
|
||||
open fun canEvaluateEnumValue(enumEntry: IrGetEnumValue, context: IrCall? = null): Boolean = false
|
||||
open fun canEvaluateFunctionExpression(expression: IrFunctionExpression, context: IrCall? = null): Boolean = false
|
||||
open fun canEvaluateCallableReference(reference: IrCallableReference<*>, context: IrCall? = null): Boolean = false
|
||||
open fun canEvaluateFunction(function: IrFunction): Boolean = false
|
||||
open fun canEvaluateEnumValue(enumEntry: IrGetEnumValue): Boolean = false
|
||||
open fun canEvaluateFunctionExpression(expression: IrFunctionExpression): Boolean = false
|
||||
open fun canEvaluateCallableReference(reference: IrCallableReference<*>): Boolean = false
|
||||
open fun canEvaluateClassReference(reference: IrDeclarationReference): Boolean = false
|
||||
|
||||
open fun canEvaluateBlock(block: IrBlock): Boolean = false
|
||||
@@ -121,7 +104,9 @@ enum class EvaluationMode {
|
||||
|
||||
open fun canEvaluateExpression(expression: IrExpression): Boolean = false
|
||||
|
||||
open fun mustCheckBodyOf(function: IrFunction): Boolean = false
|
||||
open fun mustCheckBodyOf(function: IrFunction): Boolean {
|
||||
return function.property != null
|
||||
}
|
||||
|
||||
protected fun IrDeclaration.isMarkedAsIntrinsicConstEvaluation() = isMarkedWith(intrinsicConstEvaluationAnnotation)
|
||||
|
||||
|
||||
+15
-24
@@ -24,7 +24,6 @@ class IrCompileTimeChecker(
|
||||
private val mode: EvaluationMode,
|
||||
private val interpreterConfiguration: IrInterpreterConfiguration,
|
||||
) : IrElementVisitor<Boolean, Nothing?> {
|
||||
private var contextExpression: IrCall? = null
|
||||
private val visitedStack = mutableListOf<IrElement>().apply { if (containingDeclaration != null) add(containingDeclaration) }
|
||||
|
||||
private inline fun IrElement.asVisited(crossinline block: () -> Boolean): Boolean {
|
||||
@@ -34,12 +33,6 @@ class IrCompileTimeChecker(
|
||||
return result
|
||||
}
|
||||
|
||||
private inline fun <R> IrCall.saveContext(crossinline block: () -> R): R {
|
||||
val oldContext = contextExpression
|
||||
contextExpression = this
|
||||
return block().apply { contextExpression = oldContext }
|
||||
}
|
||||
|
||||
override fun visitElement(element: IrElement, data: Nothing?) = false
|
||||
|
||||
private fun IrDeclarationParent.getInnerDeclarations(): List<IrStatement> {
|
||||
@@ -53,7 +46,7 @@ class IrCompileTimeChecker(
|
||||
private fun visitConstructor(expression: IrFunctionAccessExpression): Boolean {
|
||||
val constructor = expression.symbol.owner
|
||||
|
||||
if (!mode.canEvaluateFunction(constructor, contextExpression)) return false
|
||||
if (!mode.canEvaluateFunction(constructor)) return false
|
||||
if (!visitValueArguments(expression, null)) return false
|
||||
return constructor.visitBodyIfNeeded() &&
|
||||
constructor.parentAsClass.declarations.filterIsInstance<IrAnonymousInitializer>().all { it.accept(this, null) }
|
||||
@@ -72,7 +65,7 @@ class IrCompileTimeChecker(
|
||||
if (!mode.canEvaluateExpression(expression)) return false
|
||||
|
||||
val owner = expression.symbol.owner
|
||||
if (!mode.canEvaluateFunction(owner, expression)) return false
|
||||
if (!mode.canEvaluateFunction(owner)) return false
|
||||
|
||||
// We disable `toFloat` folding on K/JS till `toFloat` is fixed (KT-35422)
|
||||
// This check must be placed here instead of CallInterceptor because we still
|
||||
@@ -81,17 +74,15 @@ class IrCompileTimeChecker(
|
||||
return super.visitCall(expression, data)
|
||||
}
|
||||
|
||||
return expression.saveContext {
|
||||
if (expression.dispatchReceiver.isAccessToNotNullableObject()) {
|
||||
return@saveContext expression.isGetterToConstVal()
|
||||
}
|
||||
|
||||
val dispatchReceiverComputable = expression.dispatchReceiver?.accept(this, null) ?: true
|
||||
val extensionReceiverComputable = expression.extensionReceiver?.accept(this, null) ?: true
|
||||
if (!visitValueArguments(expression, null)) return@saveContext false
|
||||
val bodyComputable = owner.visitBodyIfNeeded()
|
||||
return@saveContext dispatchReceiverComputable && extensionReceiverComputable && bodyComputable
|
||||
if (expression.dispatchReceiver.isAccessToNotNullableObject()) {
|
||||
return expression.isGetterToConstVal()
|
||||
}
|
||||
|
||||
val dispatchReceiverComputable = expression.dispatchReceiver?.accept(this, null) ?: true
|
||||
val extensionReceiverComputable = expression.extensionReceiver?.accept(this, null) ?: true
|
||||
if (!visitValueArguments(expression, null)) return false
|
||||
val bodyComputable = owner.visitBodyIfNeeded()
|
||||
return dispatchReceiverComputable && extensionReceiverComputable && bodyComputable
|
||||
}
|
||||
|
||||
override fun visitVariable(declaration: IrVariable, data: Nothing?): Boolean {
|
||||
@@ -173,7 +164,7 @@ class IrCompileTimeChecker(
|
||||
}
|
||||
|
||||
override fun visitGetEnumValue(expression: IrGetEnumValue, data: Nothing?): Boolean {
|
||||
if (!mode.canEvaluateEnumValue(expression, contextExpression)) return false
|
||||
if (!mode.canEvaluateEnumValue(expression)) return false
|
||||
|
||||
// we want to avoid recursion in cases like "enum class E(val srt: String) { OK(OK.name) }"
|
||||
if (visitedStack.contains(expression)) return true
|
||||
@@ -253,13 +244,13 @@ class IrCompileTimeChecker(
|
||||
}
|
||||
|
||||
override fun visitFunctionReference(expression: IrFunctionReference, data: Nothing?): Boolean {
|
||||
if (!mode.canEvaluateCallableReference(expression, contextExpression)) return false
|
||||
if (!mode.canEvaluateCallableReference(expression)) return false
|
||||
|
||||
val owner = expression.symbol.owner
|
||||
val dispatchReceiverComputable = expression.dispatchReceiver?.accept(this, null) ?: true
|
||||
val extensionReceiverComputable = expression.extensionReceiver?.accept(this, null) ?: true
|
||||
|
||||
if (!mode.canEvaluateFunction(owner, contextExpression)) return false
|
||||
if (!mode.canEvaluateFunction(owner)) return false
|
||||
|
||||
val bodyComputable = owner.visitBodyIfNeeded()
|
||||
return dispatchReceiverComputable && extensionReceiverComputable && bodyComputable
|
||||
@@ -331,12 +322,12 @@ class IrCompileTimeChecker(
|
||||
}
|
||||
|
||||
override fun visitPropertyReference(expression: IrPropertyReference, data: Nothing?): Boolean {
|
||||
if (!mode.canEvaluateCallableReference(expression, contextExpression)) return false
|
||||
if (!mode.canEvaluateCallableReference(expression)) return false
|
||||
|
||||
val dispatchReceiverComputable = expression.dispatchReceiver?.accept(this, null) ?: true
|
||||
val extensionReceiverComputable = expression.extensionReceiver?.accept(this, null) ?: true
|
||||
|
||||
val getterIsComputable = expression.getter?.let { mode.canEvaluateFunction(it.owner, contextExpression) } ?: true
|
||||
val getterIsComputable = expression.getter?.let { mode.canEvaluateFunction(it.owner) } ?: true
|
||||
return dispatchReceiverComputable && extensionReceiverComputable && getterIsComputable
|
||||
}
|
||||
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.ir.interpreter.checker
|
||||
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||
|
||||
class IrCompileTimeNameChecker(
|
||||
private val mode: EvaluationMode
|
||||
) : IrElementVisitor<Boolean, Nothing?> {
|
||||
private fun IrCall.isIntrinsicConstEvaluationNameProperty(): Boolean {
|
||||
val owner = this.symbol.owner
|
||||
if (owner.extensionReceiverParameter != null || owner.valueParameters.isNotEmpty()) return false
|
||||
val property = (owner as? IrSimpleFunction)?.correspondingPropertySymbol?.owner ?: return false
|
||||
return mode.canEvaluateFunction(owner) && property.name.asString() == "name"
|
||||
}
|
||||
|
||||
override fun visitElement(element: IrElement, data: Nothing?) = false
|
||||
|
||||
override fun visitCall(expression: IrCall, data: Nothing?): Boolean {
|
||||
if (!expression.isIntrinsicConstEvaluationNameProperty()) return false
|
||||
return when (val receiver = expression.dispatchReceiver) {
|
||||
is IrCallableReference<*> -> (receiver.dispatchReceiver == null || receiver.dispatchReceiver is IrGetObjectValue) && receiver.extensionReceiver == null
|
||||
is IrGetEnumValue -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitStringConcatenation(expression: IrStringConcatenation, data: Nothing?): Boolean {
|
||||
val possibleNameCall = expression.arguments.singleOrNull() as? IrCall ?: return false
|
||||
return possibleNameCall.accept(this, data)
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.ir.interpreter.IrInterpreter
|
||||
import org.jetbrains.kotlin.ir.interpreter.IrInterpreterConfiguration
|
||||
import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode
|
||||
import org.jetbrains.kotlin.ir.interpreter.checker.IrCompileTimeChecker
|
||||
import org.jetbrains.kotlin.ir.interpreter.checker.IrCompileTimeNameChecker
|
||||
import org.jetbrains.kotlin.ir.interpreter.toConstantValue
|
||||
import org.jetbrains.kotlin.ir.util.dump
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
|
||||
@@ -80,7 +81,8 @@ internal abstract class IrConstTransformer(
|
||||
configuration: IrInterpreterConfiguration = interpreter.environment.configuration
|
||||
): Boolean {
|
||||
return try {
|
||||
this.accept(IrCompileTimeChecker(containingDeclaration, mode, configuration), null)
|
||||
this.accept(IrCompileTimeChecker(containingDeclaration, mode, configuration), null) ||
|
||||
this.accept(IrCompileTimeNameChecker(mode), null)
|
||||
} catch (e: Throwable) {
|
||||
if (suppressExceptions) {
|
||||
return false
|
||||
|
||||
Vendored
+36
@@ -0,0 +1,36 @@
|
||||
// !LANGUAGE: +IntrinsicConstEvaluation
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
// WITH_STDLIB
|
||||
|
||||
fun <T> T.id() = this
|
||||
|
||||
class A {
|
||||
val a = ""
|
||||
fun b() = ""
|
||||
|
||||
init {
|
||||
println("A init")
|
||||
}
|
||||
|
||||
fun test() {
|
||||
val a = A::a.<!EVALUATED("a")!>name<!>
|
||||
val b = A::b.<!EVALUATED("b")!>name<!>
|
||||
|
||||
val c = ::A.<!EVALUATED("<init>")!>name<!>
|
||||
val d = this::a.name
|
||||
|
||||
val e = A()::b.name
|
||||
val f = getA()::b.name
|
||||
|
||||
val temp = A()
|
||||
val g = temp::b.name
|
||||
}
|
||||
|
||||
fun getA(): A = A()
|
||||
}
|
||||
|
||||
// STOP_EVALUATION_CHECKS
|
||||
fun box(): String {
|
||||
A().test()
|
||||
return "OK"
|
||||
}
|
||||
+6
@@ -28510,6 +28510,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/involvesIrInterpreter/intrinsicConst/kCallableName.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kCallableNameWithSideEffect.kt")
|
||||
public void testKCallableNameWithSideEffect() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/involvesIrInterpreter/intrinsicConst/kCallableNameWithSideEffect.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt53272.kt")
|
||||
public void testKt53272() throws Exception {
|
||||
|
||||
+6
@@ -28510,6 +28510,12 @@ public class IrBlackBoxCodegenWithIrInlinerTestGenerated extends AbstractIrBlack
|
||||
runTest("compiler/testData/codegen/box/involvesIrInterpreter/intrinsicConst/kCallableName.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kCallableNameWithSideEffect.kt")
|
||||
public void testKCallableNameWithSideEffect() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/involvesIrInterpreter/intrinsicConst/kCallableNameWithSideEffect.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt53272.kt")
|
||||
public void testKt53272() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user