[Native] Support evaluation of const intrinsics for K2

#KT-56023
#KT-55469 Fixed
This commit is contained in:
Ivan Kylchik
2023-02-03 14:53:15 +01:00
committed by Space Team
parent 5d5582d201
commit e981b1f958
34 changed files with 264 additions and 60 deletions
@@ -19,10 +19,7 @@ import org.jetbrains.kotlin.ir.interpreter.intrinsics.IntrinsicEvaluator
import org.jetbrains.kotlin.ir.interpreter.proxy.wrap
import org.jetbrains.kotlin.ir.interpreter.stack.CallStack
import org.jetbrains.kotlin.ir.interpreter.state.*
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.isArray
import org.jetbrains.kotlin.ir.types.isUnsignedType
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.name.FqName
@@ -91,7 +88,9 @@ internal class DefaultCallInterceptor(override val interpreter: IrInterpreter) :
verify(handleIntrinsicMethods(irConstructor)) { "Unsupported intrinsic constructor: ${irConstructor.render()}" }
}
irClass.defaultType.isUnsignedType() -> {
val propertySymbol = irClass.declarations.single { it is IrProperty }.symbol
// Check for type is a hack needed for Native;
// in UInt, for example, we may have (after lowerings, I guess) additional property "$companion".
val propertySymbol = irClass.declarations.single { it is IrProperty && it.getter?.returnType?.isPrimitiveType() == true }.symbol
callStack.pushState(receiver.apply { this.setField(propertySymbol, args.single()) })
}
else -> defaultAction()
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.util.OperatorNameConventions
class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal val bodyMap: Map<IdSignature, IrBody>) {
class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal val bodyMap: Map<IdSignature, IrBody> = emptyMap()) {
val irBuiltIns: IrBuiltIns
get() = environment.irBuiltIns
private val callStack: CallStack
@@ -239,7 +239,7 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal
constructor.valueParameters.forEachIndexed { i, param -> callStack.storeState(param.symbol, valueArguments[i]) }
callStack.storeState(constructor.symbol, KTypeState(returnType, environment.kTypeClass.owner))
val superReceiver = when (val irStatement = constructor.body?.statements?.get(0)) {
val superReceiver = when (val irStatement = constructor.body?.statements?.getOrNull(0)) {
null -> null // for jvm
is IrTypeOperatorCall -> (irStatement.argument as IrFunctionAccessExpression).getThisReceiver() // for enums
is IrFunctionAccessExpression -> irStatement.getThisReceiver()
@@ -255,14 +255,38 @@ internal fun interpretBinaryFunction(name: String, typeA: String, typeB: String,
"Comparable" -> if (typeB == "T") return (a as Comparable<Any?>).compareTo(b)
}
"equals" -> when (typeA) {
"Boolean" -> if (typeB == "Any?") return (a as Boolean).equals(b)
"Char" -> if (typeB == "Any?") return (a as Char).equals(b)
"Byte" -> if (typeB == "Any?") return (a as Byte).equals(b)
"Short" -> if (typeB == "Any?") return (a as Short).equals(b)
"Int" -> if (typeB == "Any?") return (a as Int).equals(b)
"Float" -> if (typeB == "Any?") return (a as Float).equals(b)
"Long" -> if (typeB == "Any?") return (a as Long).equals(b)
"Double" -> if (typeB == "Any?") return (a as Double).equals(b)
"Boolean" -> when (typeB) {
"Any?" -> return (a as Boolean).equals(b)
"Boolean" -> return (a as Boolean).equals(b as Boolean)
}
"Char" -> when (typeB) {
"Any?" -> return (a as Char).equals(b)
"Char" -> return (a as Char).equals(b as Char)
}
"Byte" -> when (typeB) {
"Any?" -> return (a as Byte).equals(b)
"Byte" -> return (a as Byte).equals(b as Byte)
}
"Short" -> when (typeB) {
"Any?" -> return (a as Short).equals(b)
"Short" -> return (a as Short).equals(b as Short)
}
"Int" -> when (typeB) {
"Any?" -> return (a as Int).equals(b)
"Int" -> return (a as Int).equals(b as Int)
}
"Float" -> when (typeB) {
"Any?" -> return (a as Float).equals(b)
"Float" -> return (a as Float).equals(b as Float)
}
"Long" -> when (typeB) {
"Any?" -> return (a as Long).equals(b)
"Long" -> return (a as Long).equals(b as Long)
}
"Double" -> when (typeB) {
"Any?" -> return (a as Double).equals(b)
"Double" -> return (a as Double).equals(b as Double)
}
"String" -> if (typeB == "Any?") return (a as String).equals(b)
"Any" -> if (typeB == "Any?") return (a as Any).equals(b)
}
@@ -50,7 +50,10 @@ enum class EvaluationMode(protected val mustCheckBody: Boolean) {
ONLY_BUILTINS(mustCheckBody = false) {
private val forbiddenMethodsOnPrimitives = setOf("inc", "dec", "rangeTo", "rangeUntil", "hashCode")
private val forbiddenMethodsOnStrings = setOf("subSequence", "hashCode", "<init>")
private val allowedExtensionFunctions = setOf("kotlin.floorDiv", "kotlin.mod", "kotlin.NumbersKt.floorDiv", "kotlin.NumbersKt.mod")
private val allowedExtensionFunctions = setOf(
"kotlin.floorDiv", "kotlin.mod", "kotlin.NumbersKt.floorDiv", "kotlin.NumbersKt.mod", "kotlin.<get-code>",
"kotlin.internal.ir.EQEQ",
)
override fun canEvaluateFunction(function: IrFunction, context: IrCall?): Boolean {
if ((function as? IrSimpleFunction)?.correspondingPropertySymbol?.owner?.isConst == true) return true
@@ -99,6 +99,13 @@ class IrCompileTimeChecker(
if (mode == EvaluationMode.ONLY_INTRINSIC_CONST && expression.origin == IrStatementOrigin.WHEN) {
return expression.statements.all { it.accept(this, null) }
}
// `IrReturnableBlock` will be created from IrCall after inline. We should do basically the same check as for IrCall.
if (expression is IrReturnableBlock) {
// TODO after JVM inline MR 8122 will be pushed check original IrCall.
TODO("Interpretation of `IrReturnableBlock` is not implemented")
}
return visitStatements(expression.statements)
}