Change ir const checker to match new rules of IntrinsicConstEvaluation
This commit is contained in:
+8
-12
@@ -358,13 +358,7 @@ private fun unfoldStringConcatenation(expression: IrStringConcatenation, environ
|
||||
// TODO this check can be dropped after serialization introduction
|
||||
// for now declarations in unsigned class don't have bodies and must be treated separately
|
||||
if (state.irClass.defaultType.isUnsigned()) {
|
||||
val result = when (val value = (state.fields.values.single() as Primitive<*>).value) {
|
||||
is Byte -> value.toUByte().toString()
|
||||
is Short -> value.toUShort().toString()
|
||||
is Int -> value.toUInt().toString()
|
||||
else -> (value as Number).toLong().toULong().toString()
|
||||
}
|
||||
return callStack.pushState(environment.convertToState(result, environment.irBuiltIns.stringType))
|
||||
return callStack.pushState(environment.convertToState(state.unsignedToString(), environment.irBuiltIns.stringType))
|
||||
}
|
||||
val toStringCall = state.createToStringIrCall()
|
||||
callStack.pushSimpleInstruction(toStringCall)
|
||||
@@ -398,14 +392,16 @@ private fun unfoldFunctionReference(reference: IrFunctionReference, callStack: C
|
||||
}
|
||||
|
||||
private fun unfoldPropertyReference(propertyReference: IrPropertyReference, callStack: CallStack) {
|
||||
val getter = propertyReference.getter!!.owner
|
||||
callStack.pushSimpleInstruction(propertyReference)
|
||||
|
||||
propertyReference.dispatchReceiver?.let { callStack.pushSimpleInstruction(getter.dispatchReceiverParameter!!) }
|
||||
propertyReference.extensionReceiver?.let { callStack.pushSimpleInstruction(getter.extensionReceiverParameter!!) }
|
||||
val getter = propertyReference.getter?.owner
|
||||
if (getter != null) {
|
||||
propertyReference.dispatchReceiver?.let { callStack.pushSimpleInstruction(getter.dispatchReceiverParameter!!) }
|
||||
propertyReference.extensionReceiver?.let { callStack.pushSimpleInstruction(getter.extensionReceiverParameter!!) }
|
||||
|
||||
propertyReference.extensionReceiver?.let { callStack.pushCompoundInstruction(it) }
|
||||
propertyReference.dispatchReceiver?.let { callStack.pushCompoundInstruction(it) }
|
||||
propertyReference.extensionReceiver?.let { callStack.pushCompoundInstruction(it) }
|
||||
propertyReference.dispatchReceiver?.let { callStack.pushCompoundInstruction(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun unfoldClassReference(classReference: IrClassReference, callStack: CallStack) {
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ internal val TEMP_FUNCTION_FOR_INTERPRETER = object : IrDeclarationOriginImpl("T
|
||||
fun Any?.toIrConstOrNull(irType: IrType, startOffset: Int = SYNTHETIC_OFFSET, endOffset: Int = SYNTHETIC_OFFSET): IrConst<*>? {
|
||||
if (this == null) return IrConstImpl.constNull(startOffset, endOffset, irType)
|
||||
|
||||
val constType = irType.makeNotNull()
|
||||
val constType = irType.makeNotNull().removeAnnotations()
|
||||
return when (irType.getPrimitiveType()) {
|
||||
PrimitiveType.BOOLEAN -> IrConstImpl.boolean(startOffset, endOffset, constType, this as Boolean)
|
||||
PrimitiveType.CHAR -> IrConstImpl.char(startOffset, endOffset, constType, this as Char)
|
||||
|
||||
@@ -305,3 +305,12 @@ internal fun IrGetValue.isAccessToObject(): Boolean {
|
||||
internal fun IrFunction.isAccessorOfPropertyWithBackingField(): Boolean {
|
||||
return this is IrSimpleFunction && this.correspondingPropertySymbol?.owner?.backingField?.initializer != null
|
||||
}
|
||||
|
||||
internal fun State.unsignedToString(): String {
|
||||
return when (val value = (this.fields.values.single() as Primitive<*>).value) {
|
||||
is Byte -> value.toUByte().toString()
|
||||
is Short -> value.toUShort().toString()
|
||||
is Int -> value.toUInt().toString()
|
||||
else -> (value as Number).toLong().toULong().toString()
|
||||
}
|
||||
}
|
||||
|
||||
+38
-18
@@ -7,10 +7,7 @@ package org.jetbrains.kotlin.ir.interpreter.checker
|
||||
|
||||
import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetObjectValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.interpreter.*
|
||||
import org.jetbrains.kotlin.ir.interpreter.hasAnnotation
|
||||
import org.jetbrains.kotlin.ir.types.isAny
|
||||
@@ -22,21 +19,21 @@ import org.jetbrains.kotlin.name.FqName
|
||||
|
||||
enum class EvaluationMode(protected val mustCheckBody: Boolean) {
|
||||
FULL(mustCheckBody = true) {
|
||||
override fun canEvaluateFunction(function: IrFunction, expression: IrCall?): Boolean {
|
||||
return true
|
||||
}
|
||||
override fun canEvaluateFunction(function: IrFunction, context: IrCall?): Boolean = true
|
||||
override fun canEvaluateEnumValue(enumEntry: IrGetEnumValue, context: IrCall?): Boolean = true
|
||||
override fun canEvaluateReference(reference: IrCallableReference<*>, context: IrCall?): Boolean = true
|
||||
},
|
||||
|
||||
WITH_ANNOTATIONS(mustCheckBody = false) {
|
||||
override fun canEvaluateFunction(function: IrFunction, expression: IrCall?): Boolean {
|
||||
if (function.isCompileTimeProperty()) return true
|
||||
override fun canEvaluateFunction(function: IrFunction, context: IrCall?): Boolean {
|
||||
if (function.isCompileTimePropertyAccessor()) return true
|
||||
return function.isMarkedAsCompileTime() || function.origin == IrBuiltIns.BUILTIN_OPERATOR ||
|
||||
(function is IrSimpleFunction && function.isOperator && function.name.asString() == "invoke") ||
|
||||
(function is IrSimpleFunction && function.isFakeOverride && function.overriddenSymbols.any { canEvaluateFunction(it.owner) }) ||
|
||||
function.isCompileTimeTypeAlias()
|
||||
}
|
||||
|
||||
private fun IrDeclaration?.isCompileTimeProperty(): Boolean {
|
||||
private fun IrFunction?.isCompileTimePropertyAccessor(): Boolean {
|
||||
val property = (this as? IrSimpleFunction)?.correspondingPropertySymbol?.owner ?: return false
|
||||
if (property.isConst) return true
|
||||
if (property.isMarkedAsCompileTime() || property.isCompileTimeTypeAlias()) return true
|
||||
@@ -45,6 +42,9 @@ enum class EvaluationMode(protected val mustCheckBody: Boolean) {
|
||||
val backingFieldExpression = backingField?.initializer?.expression as? IrGetValue
|
||||
return backingFieldExpression?.origin == IrStatementOrigin.INITIALIZE_PROPERTY_FROM_PARAMETER
|
||||
}
|
||||
|
||||
override fun canEvaluateEnumValue(enumEntry: IrGetEnumValue, context: IrCall?): Boolean = true
|
||||
override fun canEvaluateReference(reference: IrCallableReference<*>, context: IrCall?): Boolean = true
|
||||
},
|
||||
|
||||
ONLY_BUILTINS(mustCheckBody = false) {
|
||||
@@ -52,7 +52,7 @@ enum class EvaluationMode(protected val mustCheckBody: Boolean) {
|
||||
private val forbiddenMethodsOnStrings = setOf("subSequence", "hashCode", "<init>")
|
||||
private val allowedExtensionFunctions = setOf("kotlin.floorDiv", "kotlin.mod", "kotlin.NumbersKt.floorDiv", "kotlin.NumbersKt.mod")
|
||||
|
||||
override fun canEvaluateFunction(function: IrFunction, expression: IrCall?): Boolean {
|
||||
override fun canEvaluateFunction(function: IrFunction, context: IrCall?): Boolean {
|
||||
if ((function as? IrSimpleFunction)?.correspondingPropertySymbol?.owner?.isConst == true) return true
|
||||
|
||||
val fqName = function.fqNameWhenAvailable?.asString()
|
||||
@@ -62,26 +62,46 @@ enum class EvaluationMode(protected val mustCheckBody: Boolean) {
|
||||
parentType == null -> fqName in allowedExtensionFunctions
|
||||
parentType.isPrimitiveType() -> function.name.asString() !in forbiddenMethodsOnPrimitives
|
||||
parentType.isString() -> function.name.asString() !in forbiddenMethodsOnStrings
|
||||
parentType.isAny() -> function.name.asString() == "toString" && expression?.dispatchReceiver !is IrGetObjectValue
|
||||
parentType.isAny() -> function.name.asString() == "toString" && context?.dispatchReceiver !is IrGetObjectValue
|
||||
parent.isObject -> parent.parentClassOrNull?.defaultType?.let { it.isPrimitiveType() || it.isUnsigned() } == true
|
||||
parentType.isUnsignedType() && function is IrConstructor -> true
|
||||
else -> fqName in allowedExtensionFunctions
|
||||
}
|
||||
}
|
||||
|
||||
override fun canEvaluateEnumValue(enumEntry: IrGetEnumValue, context: IrCall?): Boolean = false
|
||||
override fun canEvaluateReference(reference: IrCallableReference<*>, context: IrCall?): Boolean = false
|
||||
},
|
||||
|
||||
ONLY_INTRINSIC_CONST(mustCheckBody = false) {
|
||||
override fun canEvaluateFunction(function: IrFunction, expression: IrCall?): Boolean {
|
||||
return function.isCompileTimeProperty() || function.isMarkedAsIntrinsicConst()
|
||||
override fun canEvaluateFunction(function: IrFunction, context: IrCall?): Boolean {
|
||||
return function.isCompileTimePropertyAccessor() || function.isMarkedAsIntrinsicConstEvaluation() || context.isIntrinsicConstEvaluationNameProperty()
|
||||
}
|
||||
|
||||
private fun IrDeclaration?.isCompileTimeProperty(): Boolean {
|
||||
private fun IrFunction?.isCompileTimePropertyAccessor(): Boolean {
|
||||
val property = (this as? IrSimpleFunction)?.correspondingPropertySymbol?.owner ?: return false
|
||||
return property.isConst || property.isMarkedAsIntrinsicConst()
|
||||
return property.isConst || (property.resolveFakeOverride() ?: property).isMarkedAsIntrinsicConstEvaluation()
|
||||
}
|
||||
|
||||
override fun canEvaluateEnumValue(enumEntry: IrGetEnumValue, context: IrCall?): Boolean {
|
||||
return context.isIntrinsicConstEvaluationNameProperty()
|
||||
}
|
||||
|
||||
override fun canEvaluateReference(reference: IrCallableReference<*>, context: IrCall?): Boolean {
|
||||
return context.isIntrinsicConstEvaluationNameProperty()
|
||||
}
|
||||
|
||||
private fun IrCall?.isIntrinsicConstEvaluationNameProperty(): Boolean {
|
||||
if (this == null) return false
|
||||
val owner = this.symbol.owner
|
||||
val property = (owner as? IrSimpleFunction)?.correspondingPropertySymbol?.owner ?: return false
|
||||
return owner.isCompileTimePropertyAccessor() && property.name.asString() == "name"
|
||||
}
|
||||
};
|
||||
|
||||
abstract fun canEvaluateFunction(function: IrFunction, expression: IrCall? = null): Boolean
|
||||
abstract fun canEvaluateFunction(function: IrFunction, context: IrCall? = null): Boolean
|
||||
abstract fun canEvaluateEnumValue(enumEntry: IrGetEnumValue, context: IrCall? = null): Boolean
|
||||
abstract fun canEvaluateReference(reference: IrCallableReference<*>, context: IrCall? = null): Boolean
|
||||
|
||||
fun canEvaluateBody(function: IrFunction): Boolean {
|
||||
if (function is IrSimpleFunction && function.correspondingPropertySymbol != null) return true
|
||||
@@ -93,7 +113,7 @@ enum class EvaluationMode(protected val mustCheckBody: Boolean) {
|
||||
)
|
||||
|
||||
fun IrDeclaration.isMarkedAsCompileTime() = isMarkedWith(compileTimeAnnotation)
|
||||
protected fun IrDeclaration.isMarkedAsIntrinsicConst() = isMarkedWith(intrinsicConstEvaluationAnnotation)
|
||||
protected fun IrDeclaration.isMarkedAsIntrinsicConstEvaluation() = isMarkedWith(intrinsicConstEvaluationAnnotation)
|
||||
private fun IrDeclaration.isContract() = isMarkedWith(contractsDslAnnotation)
|
||||
private fun IrDeclaration.isMarkedAsEvaluateIntrinsic() = isMarkedWith(evaluateIntrinsicAnnotation)
|
||||
protected fun IrDeclaration.isCompileTimeTypeAlias() = this.parentClassOrNull?.fqName in compileTimeTypeAliases
|
||||
|
||||
+36
-25
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||
class IrCompileTimeChecker(
|
||||
containingDeclaration: IrElement? = null, private val mode: EvaluationMode = EvaluationMode.WITH_ANNOTATIONS
|
||||
) : IrElementVisitor<Boolean, Nothing?> {
|
||||
private var contextExpression: IrCall? = null
|
||||
private val visitedStack = mutableListOf<IrElement>().apply { if (containingDeclaration != null) add(containingDeclaration) }
|
||||
|
||||
private fun IrElement.asVisited(block: () -> Boolean): Boolean {
|
||||
@@ -29,24 +30,32 @@ class IrCompileTimeChecker(
|
||||
return result
|
||||
}
|
||||
|
||||
private fun <R> IrCall.saveContext(block: () -> R): R {
|
||||
contextExpression = this
|
||||
return block().apply { contextExpression = null }
|
||||
}
|
||||
|
||||
override fun visitElement(element: IrElement, data: Nothing?) = false
|
||||
|
||||
private fun IrDeclarationParent.getInnerDeclarations(): List<IrStatement> {
|
||||
return (this as? IrDeclarationContainer)?.declarations ?: (this as? IrStatementContainer)?.statements ?: emptyList()
|
||||
}
|
||||
|
||||
private fun visitStatements(statements: List<IrStatement>, data: Nothing?): Boolean {
|
||||
if (mode == EvaluationMode.ONLY_BUILTINS || mode == EvaluationMode.ONLY_INTRINSIC_CONST) {
|
||||
val statement = statements.singleOrNull() ?: return false
|
||||
return statement.accept(this, data)
|
||||
private fun visitStatements(statements: List<IrStatement>): Boolean {
|
||||
when (mode) {
|
||||
EvaluationMode.ONLY_BUILTINS, EvaluationMode.ONLY_INTRINSIC_CONST -> {
|
||||
val statement = statements.singleOrNull() ?: return false
|
||||
return statement.accept(this, null)
|
||||
}
|
||||
else -> return statements.all { it.accept(this, null) }
|
||||
}
|
||||
return statements.all { it.accept(this, data) }
|
||||
}
|
||||
|
||||
private fun visitConstructor(expression: IrFunctionAccessExpression): Boolean {
|
||||
return when {
|
||||
!visitValueParameters(expression, null) || !mode.canEvaluateFunction(expression.symbol.owner) -> false
|
||||
else -> if (mode.canEvaluateBody(expression.symbol.owner)) expression.symbol.owner.body?.accept(this, null) != false else true
|
||||
!visitValueParameters(expression, null) || !mode.canEvaluateFunction(expression.symbol.owner, contextExpression) -> false
|
||||
mode.canEvaluateBody(expression.symbol.owner) -> expression.symbol.owner.body?.accept(this, null) != false
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,12 +63,13 @@ class IrCompileTimeChecker(
|
||||
val owner = expression.symbol.owner
|
||||
if (!mode.canEvaluateFunction(owner, expression)) return false
|
||||
|
||||
val dispatchReceiverComputable = expression.dispatchReceiver?.accept(this, null) ?: true
|
||||
val extensionReceiverComputable = expression.extensionReceiver?.accept(this, null) ?: true
|
||||
if (!visitValueParameters(expression, null)) return false
|
||||
val bodyComputable = owner.asVisited { if (mode.canEvaluateBody(owner)) owner.body?.accept(this, null) ?: true else true }
|
||||
|
||||
return dispatchReceiverComputable && extensionReceiverComputable && bodyComputable
|
||||
return expression.saveContext {
|
||||
val dispatchReceiverComputable = expression.dispatchReceiver?.accept(this, null) ?: true
|
||||
val extensionReceiverComputable = expression.extensionReceiver?.accept(this, null) ?: true
|
||||
if (!visitValueParameters(expression, null)) return@saveContext false
|
||||
val bodyComputable = owner.asVisited { if (mode.canEvaluateBody(owner)) owner.body?.accept(this, null) ?: true else true }
|
||||
return@saveContext dispatchReceiverComputable && extensionReceiverComputable && bodyComputable
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitVariable(declaration: IrVariable, data: Nothing?): Boolean {
|
||||
@@ -73,7 +83,7 @@ class IrCompileTimeChecker(
|
||||
}
|
||||
|
||||
override fun visitBody(body: IrBody, data: Nothing?): Boolean {
|
||||
return visitStatements(body.statements, data)
|
||||
return visitStatements(body.statements)
|
||||
}
|
||||
|
||||
// We need this separate method to explicitly indicate that IrExpressionBody can be interpreted in any evaluation mode
|
||||
@@ -82,7 +92,10 @@ class IrCompileTimeChecker(
|
||||
}
|
||||
|
||||
override fun visitBlock(expression: IrBlock, data: Nothing?): Boolean {
|
||||
return visitStatements(expression.statements, data)
|
||||
if (mode == EvaluationMode.ONLY_INTRINSIC_CONST && expression.origin == IrStatementOrigin.WHEN) {
|
||||
return expression.statements.all { it.accept(this, null) }
|
||||
}
|
||||
return visitStatements(expression.statements)
|
||||
}
|
||||
|
||||
override fun visitSyntheticBody(body: IrSyntheticBody, data: Nothing?): Boolean {
|
||||
@@ -107,7 +120,7 @@ class IrCompileTimeChecker(
|
||||
|
||||
override fun visitComposite(expression: IrComposite, data: Nothing?): Boolean {
|
||||
if (expression.origin == IrStatementOrigin.DESTRUCTURING_DECLARATION || expression.origin == null) {
|
||||
return visitStatements(expression.statements, data)
|
||||
return visitStatements(expression.statements)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -122,6 +135,7 @@ class IrCompileTimeChecker(
|
||||
}
|
||||
|
||||
override fun visitGetEnumValue(expression: IrGetEnumValue, data: Nothing?): Boolean {
|
||||
if (!mode.canEvaluateEnumValue(expression, contextExpression)) return false
|
||||
return expression.symbol.owner.initializerExpression?.accept(this, data) == true
|
||||
}
|
||||
|
||||
@@ -196,15 +210,13 @@ class IrCompileTimeChecker(
|
||||
}
|
||||
|
||||
override fun visitFunctionReference(expression: IrFunctionReference, data: Nothing?): Boolean {
|
||||
if (!mode.canEvaluateReference(expression, contextExpression)) 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 == EvaluationMode.ONLY_INTRINSIC_CONST) {
|
||||
return dispatchReceiverComputable && extensionReceiverComputable
|
||||
} else if (!mode.canEvaluateFunction(owner)) {
|
||||
return false
|
||||
}
|
||||
if (!mode.canEvaluateFunction(owner, contextExpression)) return false
|
||||
|
||||
val bodyComputable = owner.asVisited { if (mode.canEvaluateBody(owner)) owner.body?.accept(this, null) ?: true else true }
|
||||
return dispatchReceiverComputable && extensionReceiverComputable && bodyComputable
|
||||
@@ -273,13 +285,12 @@ class IrCompileTimeChecker(
|
||||
}
|
||||
|
||||
override fun visitPropertyReference(expression: IrPropertyReference, data: Nothing?): Boolean {
|
||||
if (!mode.canEvaluateReference(expression, contextExpression)) return false
|
||||
|
||||
val dispatchReceiverComputable = expression.dispatchReceiver?.accept(this, null) ?: true
|
||||
val extensionReceiverComputable = expression.extensionReceiver?.accept(this, null) ?: true
|
||||
|
||||
if (mode == EvaluationMode.ONLY_INTRINSIC_CONST) {
|
||||
return dispatchReceiverComputable && extensionReceiverComputable
|
||||
}
|
||||
val getterIsComputable = expression.getter?.let { mode.canEvaluateFunction(it.owner) } ?: false
|
||||
val getterIsComputable = expression.getter?.let { mode.canEvaluateFunction(it.owner, contextExpression) } ?: true
|
||||
return dispatchReceiverComputable && extensionReceiverComputable && getterIsComputable
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ class IrConstTransformer(
|
||||
initializer.expression = if (isConst) result.reportIfError(expression) else result.replaceIfError(expression)
|
||||
}
|
||||
|
||||
return declaration
|
||||
return super.visitField(declaration)
|
||||
}
|
||||
|
||||
override fun visitDeclaration(declaration: IrDeclarationBase): IrStatement {
|
||||
|
||||
+7
@@ -10,7 +10,9 @@ import org.jetbrains.kotlin.ir.interpreter.*
|
||||
import org.jetbrains.kotlin.ir.interpreter.CallInterceptor
|
||||
import org.jetbrains.kotlin.ir.interpreter.getDispatchReceiver
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.Common
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.Primitive
|
||||
import org.jetbrains.kotlin.ir.interpreter.state.State
|
||||
import org.jetbrains.kotlin.ir.util.defaultType
|
||||
import org.jetbrains.kotlin.ir.util.isFakeOverriddenFromAny
|
||||
|
||||
internal class CommonProxy private constructor(override val state: Common, override val callInterceptor: CallInterceptor) : Proxy {
|
||||
@@ -53,6 +55,11 @@ internal class CommonProxy private constructor(override val state: Common, overr
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
// TODO this check can be dropped after serialization introduction
|
||||
// for now declarations in unsigned class don't have bodies and must be treated separately
|
||||
if (state.irClass.defaultType.isUnsigned()) {
|
||||
return state.unsignedToString()
|
||||
}
|
||||
val valueArguments = mutableListOf<State>()
|
||||
val toStringFun = state.getToStringFunction()
|
||||
if (toStringFun.isFakeOverriddenFromAny() || toStringFun.wasAlreadyCalled()) return defaultToString()
|
||||
|
||||
Reference in New Issue
Block a user