diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/KCallableNamePropertyLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/KCallableNamePropertyLowering.kt deleted file mode 100644 index 065962cca4b..00000000000 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/KCallableNamePropertyLowering.kt +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2010-2022 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.backend.common.lower - -import org.jetbrains.kotlin.backend.common.BackendContext -import org.jetbrains.kotlin.backend.common.FileLoweringPass -import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase -import org.jetbrains.kotlin.ir.declarations.IrClass -import org.jetbrains.kotlin.ir.declarations.IrFile -import org.jetbrains.kotlin.ir.declarations.IrProperty -import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction -import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl -import org.jetbrains.kotlin.ir.util.isSubclassOf -import org.jetbrains.kotlin.ir.util.render -import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid -import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid -import org.jetbrains.kotlin.name.Name - -val kCallableNamePropertyPhase = makeIrFilePhase( - ::KCallableNamePropertyLowering, - name = "KCallableNameProperty", - description = "Replace name references for callables with constants" -) - -private class KCallableNamePropertyLowering(val context: BackendContext) : FileLoweringPass { - override fun lower(irFile: IrFile) { - irFile.transformChildrenVoid(KCallableNamePropertyTransformer(this)) - } -} - -private class KCallableNamePropertyTransformer(val lower: KCallableNamePropertyLowering) : IrElementTransformerVoid() { - private fun nameForCallableMember(reference: IrCallableReference<*>): Name { - return when (reference) { - is IrFunctionReference -> reference.symbol.owner.name - is IrPropertyReference -> reference.symbol.owner.name - is IrLocalDelegatedPropertyReference -> reference.symbol.owner.name - else -> error("Unexpected callable reference type ${reference.render()}") - } - } - - override fun visitCall(expression: IrCall): IrExpression { - val callableReference = expression.dispatchReceiver as? IrCallableReference<*> - ?: return super.visitCall(expression) - - val directMember = expression.symbol.owner.let { - (it as? IrSimpleFunction)?.correspondingPropertySymbol?.owner ?: it - } - - val irClass = directMember.parent as? IrClass - ?: return super.visitCall(expression) - if (!irClass.isSubclassOf(lower.context.irBuiltIns.kCallableClass.owner)) { - return super.visitCall(expression) - } - - val name = when (directMember) { - is IrSimpleFunction -> directMember.name - is IrProperty -> directMember.name - else -> throw AssertionError("Should be IrSimpleFunction or IrProperty, got $directMember") - } - if (name.asString() != "name") return expression - - val receiver = callableReference.dispatchReceiver ?: callableReference.extensionReceiver - - return IrCompositeImpl(expression.startOffset, expression.endOffset, lower.context.irBuiltIns.stringType).apply { - receiver?.let { - //put receiver for bound callable reference - statements.add(it) - } - - statements.add( - IrConstImpl.string( - expression.startOffset, - expression.endOffset, - lower.context.irBuiltIns.stringType, - nameForCallableMember(callableReference).asString() - ) - ) - } - } -} diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 01b8f8a584b..852c8351078 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -9,7 +9,8 @@ import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.checkDeclarationParents import org.jetbrains.kotlin.backend.common.lower.* -import org.jetbrains.kotlin.backend.common.lower.inline.* +import org.jetbrains.kotlin.backend.common.lower.inline.FunctionInlining +import org.jetbrains.kotlin.backend.common.lower.inline.InlineFunctionResolver import org.jetbrains.kotlin.backend.common.lower.loops.forLoopsPhase import org.jetbrains.kotlin.backend.common.phaser.* import org.jetbrains.kotlin.backend.jvm.ir.constantValue @@ -341,7 +342,6 @@ private val jvmFilePhases = listOf( jvmOverloadsAnnotationPhase, mainMethodGenerationPhase, - kCallableNamePropertyPhase, annotationPhase, annotationImplementationPhase, polymorphicSignaturePhase, diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt index 0fab7c84c55..881069190cc 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/InstructionsUnfolder.kt @@ -125,7 +125,6 @@ private fun unfoldValueParameters(expression: IrFunctionAccessExpression, enviro callStack.pushState(enumEntry.toState(environment.irBuiltIns)) return } - // TODO do the same thing but for "KCallable.name" with "this" as receiver val hasDefaults = (0 until expression.valueArgumentsCount).any { expression.getValueArgument(it) == null } if (hasDefaults) { diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrInterpreterChecker.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrInterpreterChecker.kt index f01a0f83d5a..4f158c7904d 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrInterpreterChecker.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrInterpreterChecker.kt @@ -5,6 +5,7 @@ package org.jetbrains.kotlin.ir.interpreter.checker +import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.interpreter.IrInterpreterConfiguration import org.jetbrains.kotlin.ir.visitors.IrElementVisitor @@ -13,5 +14,6 @@ interface IrInterpreterChecker : IrElementVisitor -> (receiver.dispatchReceiver == null || receiver.dispatchReceiver is IrGetObjectValue) && receiver.extensionReceiver == null - is IrGetEnumValue -> true - else -> false - } + val owner = expression.symbol.owner + if (!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 as? IrSimpleFunction)?.correspondingPropertySymbol?.owner ?: 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 as? IrSimpleFunction)?.correspondingPropertySymbol?.owner ?: return false + return this.dispatchReceiver is IrGetEnumValue && property.name.asString() == "name" + } + } } diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/preprocessor/IrInterpreterKCallableNamePreprocessor.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/preprocessor/IrInterpreterKCallableNamePreprocessor.kt new file mode 100644 index 00000000000..cd9536dd10c --- /dev/null +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/preprocessor/IrInterpreterKCallableNamePreprocessor.kt @@ -0,0 +1,32 @@ +/* + * 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.preprocessor + +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrCallableReference +import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl +import org.jetbrains.kotlin.ir.interpreter.checker.IrInterpreterNameChecker.Companion.isKCallableNameCall + +// Note: this class still will not allow us to evaluate things like `A()::a.name + `A()::b.name`. +// This code will be optimized but not completely turned into "ab" result. +class IrInterpreterKCallableNamePreprocessor : IrInterpreterPreprocessor { + override fun visitCall(expression: IrCall, data: IrInterpreterPreprocessorData): IrElement { + if (!data.mode.canEvaluateFunction(expression.symbol.owner)) return super.visitCall(expression, data) + if (!expression.isKCallableNameCall(data.irBuiltIns)) return super.visitCall(expression, data) + + val callableReference = expression.dispatchReceiver as? IrCallableReference<*> ?: return super.visitCall(expression, data) + + // receiver is needed for bound callable reference + val receiver = callableReference.dispatchReceiver ?: callableReference.extensionReceiver ?: return expression + callableReference.dispatchReceiver = null + callableReference.extensionReceiver = null + + return IrCompositeImpl( + expression.startOffset, expression.endOffset, expression.type, origin = null, statements = listOf(receiver, expression) + ) + } +} \ No newline at end of file diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/preprocessor/IrInterpreterPreprocessor.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/preprocessor/IrInterpreterPreprocessor.kt new file mode 100644 index 00000000000..6235d7638f4 --- /dev/null +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/preprocessor/IrInterpreterPreprocessor.kt @@ -0,0 +1,22 @@ +/* + * 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.preprocessor + +import org.jetbrains.kotlin.ir.IrBuiltIns +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode +import org.jetbrains.kotlin.ir.visitors.IrElementTransformer + +interface IrInterpreterPreprocessor: IrElementTransformer { + fun preprocess(file: IrFile, data: IrInterpreterPreprocessorData): IrFile { + return file.transform(this, data) + } +} + +class IrInterpreterPreprocessorData( + val mode: EvaluationMode, + val irBuiltIns: IrBuiltIns +) \ No newline at end of file diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/transformer/IrConstTransformer.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/transformer/IrConstTransformer.kt index dfb5548305a..7433894e5bc 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/transformer/IrConstTransformer.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/transformer/IrConstTransformer.kt @@ -16,11 +16,18 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl import org.jetbrains.kotlin.ir.interpreter.IrInterpreter import org.jetbrains.kotlin.ir.interpreter.IrInterpreterConfiguration -import org.jetbrains.kotlin.ir.interpreter.checker.* +import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode +import org.jetbrains.kotlin.ir.interpreter.checker.IrInterpreterCheckerData +import org.jetbrains.kotlin.ir.interpreter.checker.IrInterpreterCommonChecker +import org.jetbrains.kotlin.ir.interpreter.checker.IrInterpreterNameChecker +import org.jetbrains.kotlin.ir.interpreter.preprocessor.IrInterpreterKCallableNamePreprocessor +import org.jetbrains.kotlin.ir.interpreter.preprocessor.IrInterpreterPreprocessorData import org.jetbrains.kotlin.ir.interpreter.toConstantValue import org.jetbrains.kotlin.ir.util.dump import org.jetbrains.kotlin.ir.visitors.IrElementTransformer +private val preprocessors = setOf(IrInterpreterKCallableNamePreprocessor()) + fun IrFile.transformConst( interpreter: IrInterpreter, mode: EvaluationMode, @@ -29,18 +36,22 @@ fun IrFile.transformConst( onError: (IrFile, IrElement, IrErrorExpression) -> Unit = { _, _, _ -> }, suppressExceptions: Boolean = false, ) { + val preprocessedFile = preprocessors.fold(this) { acc, preprocessor -> + preprocessor.preprocess(acc, IrInterpreterPreprocessorData(mode, interpreter.irBuiltIns)) + } + val irConstExpressionTransformer = IrConstExpressionTransformer( - interpreter, this, mode, evaluatedConstTracker, onWarning, onError, suppressExceptions + interpreter, preprocessedFile, mode, evaluatedConstTracker, onWarning, onError, suppressExceptions ) val irConstDeclarationAnnotationTransformer = IrConstDeclarationAnnotationTransformer( - interpreter, this, mode, evaluatedConstTracker, onWarning, onError, suppressExceptions + interpreter, preprocessedFile, mode, evaluatedConstTracker, onWarning, onError, suppressExceptions ) val irConstTypeAnnotationTransformer = IrConstTypeAnnotationTransformer( - interpreter, this, mode, evaluatedConstTracker, onWarning, onError, suppressExceptions + interpreter, preprocessedFile, mode, evaluatedConstTracker, onWarning, onError, suppressExceptions ) - this.transform(irConstExpressionTransformer, null) - this.transform(irConstDeclarationAnnotationTransformer, null) - this.transform(irConstTypeAnnotationTransformer, null) + 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` @@ -83,7 +94,7 @@ internal abstract class IrConstTransformer( configuration: IrInterpreterConfiguration = interpreter.environment.configuration ): Boolean { return try { - checkers.any { this.accept(it, IrInterpreterCheckerData(configuration)) } + checkers.any { this.accept(it, IrInterpreterCheckerData(interpreter.irBuiltIns, configuration)) } } catch (e: Throwable) { if (suppressExceptions) { return false diff --git a/compiler/testData/codegen/box/involvesIrInterpreter/intrinsicConst/kCallableName.kt b/compiler/testData/codegen/box/involvesIrInterpreter/intrinsicConst/kCallableName.kt index f02191efd72..41eb7be4069 100644 --- a/compiler/testData/codegen/box/involvesIrInterpreter/intrinsicConst/kCallableName.kt +++ b/compiler/testData/codegen/box/involvesIrInterpreter/intrinsicConst/kCallableName.kt @@ -15,6 +15,7 @@ const val methodName = A::foo.name const val suspendMethodName = A::bar.name const val className = ::A.")!>name const val topLevelPropName = ::topLevelProp.name +const val nameInComplexExpression = A::OK.name + "!" // STOP_EVALUATION_CHECKS fun box(): String { @@ -24,5 +25,6 @@ fun box(): String { if (suspendMethodName.id() != "bar") return "Fail 3.2" if (className.id() != "") return "Fail 4" if (topLevelPropName.id() != "topLevelProp") return "Fail 5" + if (nameInComplexExpression.id() != "OK!") return "Fail 5" return "OK" } diff --git a/compiler/testData/codegen/box/involvesIrInterpreter/intrinsicConst/kCallableNameWithSideEffect.kt b/compiler/testData/codegen/box/involvesIrInterpreter/intrinsicConst/kCallableNameWithSideEffect.kt index 9bdd01e2621..fe84c33dc6d 100644 --- a/compiler/testData/codegen/box/involvesIrInterpreter/intrinsicConst/kCallableNameWithSideEffect.kt +++ b/compiler/testData/codegen/box/involvesIrInterpreter/intrinsicConst/kCallableNameWithSideEffect.kt @@ -17,13 +17,16 @@ class A { val b = A::b.name val c = ::A.")!>name - val d = this::a.name + val d = this::a.name - val e = A()::b.name - val f = getA()::b.name + val e = A()::b.name + val f = getA()::b.name val temp = A() - val g = temp::b.name + val g = temp::b.name + + val complexExpression1 = A()::a.name + A()::b.name + val complexExpression2 = A::a.name + A::b.name } fun getA(): A = A()