[IR] Combine IrInterpreterNameCheck with common one

This way we achieve faster compilation time. We want to traverse
IR tree as little as possible. If we add new checker than the time
to evaluate constants basically doubles.

#KT-58923
This commit is contained in:
Ivan Kylchik
2023-05-25 01:38:29 +02:00
committed by Space Team
parent e5ed1714d1
commit 8067df3c94
6 changed files with 50 additions and 80 deletions
@@ -12,6 +12,8 @@ import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.interpreter.accessesTopLevelOrObjectField
import org.jetbrains.kotlin.ir.interpreter.fqName
import org.jetbrains.kotlin.ir.interpreter.isAccessToNotNullableObject
import org.jetbrains.kotlin.ir.interpreter.preprocessor.IrInterpreterKCallableNamePreprocessor.Companion.isEnumName
import org.jetbrains.kotlin.ir.interpreter.preprocessor.IrInterpreterKCallableNamePreprocessor.Companion.isKCallableNameCall
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.constructors
import org.jetbrains.kotlin.ir.util.parentAsClass
@@ -61,6 +63,8 @@ class IrInterpreterCommonChecker : IrInterpreterChecker {
val owner = expression.symbol.owner
if (!data.mode.canEvaluateFunction(owner)) return false
if (expression.isKCallableNameCall(data.irBuiltIns) || expression.isEnumName()) return true
if (expression.dispatchReceiver.isAccessToNotNullableObject()) {
return expression.isGetterToConstVal()
}
@@ -1,59 +0,0 @@
/*
* 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.IrBuiltIns
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrCallableReference
import org.jetbrains.kotlin.ir.expressions.IrGetEnumValue
import org.jetbrains.kotlin.ir.expressions.IrStringConcatenation
import org.jetbrains.kotlin.ir.interpreter.property
import org.jetbrains.kotlin.ir.util.isSubclassOf
class IrInterpreterNameChecker : IrInterpreterChecker {
override fun visitElement(element: IrElement, data: IrInterpreterCheckerData) = false
override fun visitCall(expression: IrCall, data: IrInterpreterCheckerData): Boolean {
val owner = expression.symbol.owner
if (!data.mode.canEvaluateFunction(owner)) return false
return expression.isKCallableNameCall(data.irBuiltIns) || expression.isEnumName()
}
override fun visitStringConcatenation(expression: IrStringConcatenation, data: IrInterpreterCheckerData): Boolean {
val possibleNameCall = expression.arguments.singleOrNull() as? IrCall ?: return false
return possibleNameCall.accept(this, data)
}
companion object {
fun IrCall.isKCallableNameCall(irBuiltIns: IrBuiltIns): Boolean {
if (this.dispatchReceiver !is IrCallableReference<*>) return false
val directMember = this.symbol.owner.let { it.property ?: it }
val irClass = directMember.parent as? IrClass ?: return false
if (!irClass.isSubclassOf(irBuiltIns.kCallableClass.owner)) return false
val name = when (directMember) {
is IrSimpleFunction -> directMember.name
is IrProperty -> directMember.name
else -> throw AssertionError("Should be IrSimpleFunction or IrProperty, got $directMember")
}
return name.asString() == "name"
}
private fun IrCall.isEnumName(): Boolean {
val owner = this.symbol.owner
if (owner.extensionReceiverParameter != null || owner.valueParameters.isNotEmpty()) return false
val property = owner.property ?: return false
return this.dispatchReceiver is IrGetEnumValue && property.name.asString() == "name"
}
}
}
@@ -5,12 +5,18 @@
package org.jetbrains.kotlin.ir.interpreter.preprocessor
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrCallableReference
import org.jetbrains.kotlin.ir.expressions.IrGetEnumValue
import org.jetbrains.kotlin.ir.expressions.IrGetValue
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
import org.jetbrains.kotlin.ir.interpreter.checker.IrInterpreterNameChecker.Companion.isKCallableNameCall
import org.jetbrains.kotlin.ir.interpreter.property
import org.jetbrains.kotlin.ir.util.isSubclassOf
import org.jetbrains.kotlin.name.SpecialNames
// Note: this class still will not allow us to evaluate things like `A()::a.name + `A()::b.name`.
@@ -33,4 +39,29 @@ class IrInterpreterKCallableNamePreprocessor : IrInterpreterPreprocessor {
expression.startOffset, expression.endOffset, expression.type, origin = null, statements = listOf(receiver, expression)
)
}
companion object {
fun IrCall.isKCallableNameCall(irBuiltIns: IrBuiltIns): Boolean {
if (this.dispatchReceiver !is IrCallableReference<*>) return false
val directMember = this.symbol.owner.let { it.property ?: it }
val irClass = directMember.parent as? IrClass ?: return false
if (!irClass.isSubclassOf(irBuiltIns.kCallableClass.owner)) return false
val name = when (directMember) {
is IrSimpleFunction -> directMember.name
is IrProperty -> directMember.name
else -> throw AssertionError("Should be IrSimpleFunction or IrProperty, got $directMember")
}
return name.asString() == "name"
}
fun IrCall.isEnumName(): Boolean {
val owner = this.symbol.owner
if (owner.extensionReceiverParameter != null || owner.valueParameters.isNotEmpty()) return false
val property = owner.property ?: return false
return this.dispatchReceiver is IrGetEnumValue && property.name.asString() == "name"
}
}
}
@@ -38,25 +38,19 @@ fun IrFile.transformConst(
preprocessor.preprocess(file, IrInterpreterPreprocessorData(mode, interpreter.irBuiltIns))
}
val checkers = setOf(
IrInterpreterNameChecker(),
IrInterpreterCommonChecker(),
val checker = IrInterpreterCommonChecker()
val irConstExpressionTransformer = IrConstExpressionTransformer(
interpreter, preprocessedFile, mode, checker, evaluatedConstTracker, inlineConstTracker, onWarning, onError, suppressExceptions
)
checkers.fold(preprocessedFile) { file, checker ->
val irConstExpressionTransformer = IrConstExpressionTransformer(
interpreter, file, mode, checker, evaluatedConstTracker, inlineConstTracker, onWarning, onError, suppressExceptions
)
val irConstDeclarationAnnotationTransformer = IrConstDeclarationAnnotationTransformer(
interpreter, file, mode, checker, evaluatedConstTracker, inlineConstTracker, onWarning, onError, suppressExceptions
)
val irConstTypeAnnotationTransformer = IrConstTypeAnnotationTransformer(
interpreter, file, mode, checker, evaluatedConstTracker, inlineConstTracker, onWarning, onError, suppressExceptions
)
file.transform(irConstExpressionTransformer, null)
file.transform(irConstDeclarationAnnotationTransformer, null)
file.transform(irConstTypeAnnotationTransformer, null)
}
val irConstDeclarationAnnotationTransformer = IrConstDeclarationAnnotationTransformer(
interpreter, preprocessedFile, mode, checker, evaluatedConstTracker, inlineConstTracker, onWarning, onError, suppressExceptions
)
val irConstTypeAnnotationTransformer = IrConstTypeAnnotationTransformer(
interpreter, preprocessedFile, mode, checker, evaluatedConstTracker, inlineConstTracker, onWarning, onError, suppressExceptions
)
preprocessedFile.transform(irConstExpressionTransformer, null)
preprocessedFile.transform(irConstDeclarationAnnotationTransformer, null)
preprocessedFile.transform(irConstTypeAnnotationTransformer, null)
}
// Note: We are using `IrElementTransformer` here instead of `IrElementTransformerVoid` to avoid conflicts with `IrTypeVisitorVoid`
@@ -15,7 +15,7 @@ const val methodName = A::foo.<!EVALUATED("foo")!>name<!>
const val suspendMethodName = A::bar.<!EVALUATED("bar")!>name<!>
const val className = ::A.<!EVALUATED("<init>")!>name<!>
const val topLevelPropName = ::topLevelProp.<!EVALUATED("topLevelProp")!>name<!>
const val nameInComplexExpression = A::OK.<!EVALUATED("OK")!>name<!> <!EVALUATED("OK!")!>+ "!"<!>
const val nameInComplexExpression = A::OK.name <!EVALUATED("OK!")!>+ "!"<!>
// STOP_EVALUATION_CHECKS
fun box(): String {
@@ -27,7 +27,7 @@ class A {
val insideStringConcat = "${temp::b.<!EVALUATED("b")!>name<!>}"
val complexExpression1 = A()::a.<!EVALUATED("a")!>name<!> + A()::b.<!EVALUATED("b")!>name<!>
val complexExpression2 = A::a.<!EVALUATED("a")!>name<!> <!EVALUATED("ab")!>+ A::b.<!EVALUATED("b")!>name<!><!>
val complexExpression2 = A::a.name <!EVALUATED("ab")!>+ A::b.name<!>
var recursive = ::test.<!EVALUATED("test")!>name<!>
}