From 55cac9caa67b6d39b61995250c4a584e07e4c374 Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Wed, 19 Apr 2023 11:31:30 +0200 Subject: [PATCH] [IR] Separate `IrConstTransformer` into two distinct classes The first one `IrConstExpressionTransformer` is responsible for transforming ordinary expressions and the second one `IrConstAnnotationTransformer` is responsible for annotations. #KT-57812 --- .../kotlin/fir/backend/Fir2IrConverter.kt | 9 +- .../common/lower/ConstEvaluationLowering.kt | 10 +- .../checker/IrConstAnnotationTransformer.kt | 87 +++++++++ .../checker/IrConstExpressionTransformer.kt | 98 ++++++++++ .../interpreter/checker/IrConstTransformer.kt | 176 ++++-------------- 5 files changed, 224 insertions(+), 156 deletions(-) create mode 100644 compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrConstAnnotationTransformer.kt create mode 100644 compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrConstExpressionTransformer.kt diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrConverter.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrConverter.kt index dae0367a799..0a7415c1094 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrConverter.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrConverter.kt @@ -32,7 +32,7 @@ import org.jetbrains.kotlin.ir.interpreter.IrInterpreter import org.jetbrains.kotlin.ir.interpreter.IrInterpreterConfiguration import org.jetbrains.kotlin.ir.interpreter.IrInterpreterEnvironment import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode -import org.jetbrains.kotlin.ir.interpreter.checker.IrConstTransformer +import org.jetbrains.kotlin.ir.interpreter.checker.transformConst import org.jetbrains.kotlin.ir.util.KotlinMangler import org.jetbrains.kotlin.ir.util.NaiveSourceBasedFileEntryImpl import org.jetbrains.kotlin.ir.visitors.acceptVoid @@ -431,12 +431,7 @@ class Fir2IrConverter( val interpreter = IrInterpreter(IrInterpreterEnvironment(irModuleFragment.irBuiltins, configuration)) val mode = if (intrinsicConstEvaluation) EvaluationMode.ONLY_INTRINSIC_CONST else EvaluationMode.ONLY_BUILTINS irModuleFragment.files.forEach { - val transformer = IrConstTransformer( - interpreter, it, - mode = mode, - evaluatedConstTracker = fir2IrConfiguration.evaluatedConstTracker - ) - it.transformChildren(transformer, null) + it.transformConst(interpreter, mode = mode, evaluatedConstTracker = fir2IrConfiguration.evaluatedConstTracker) } } diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ConstEvaluationLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ConstEvaluationLowering.kt index 584e197c252..3261c7b997a 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ConstEvaluationLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ConstEvaluationLowering.kt @@ -15,7 +15,7 @@ import org.jetbrains.kotlin.ir.interpreter.IrInterpreter import org.jetbrains.kotlin.ir.interpreter.IrInterpreterConfiguration import org.jetbrains.kotlin.ir.interpreter.IrInterpreterEnvironment import org.jetbrains.kotlin.ir.interpreter.checker.EvaluationMode -import org.jetbrains.kotlin.ir.interpreter.checker.IrConstTransformer +import org.jetbrains.kotlin.ir.interpreter.checker.transformConst class ConstEvaluationLowering( val context: CommonBackendContext, @@ -28,13 +28,9 @@ class ConstEvaluationLowering( private val evaluatedConstTracker = context.configuration[CommonConfigurationKeys.EVALUATED_CONST_TRACKER] override fun lower(irFile: IrFile) { - val transformer = IrConstTransformer( - interpreter, irFile, - mode = EvaluationMode.ONLY_INTRINSIC_CONST, - evaluatedConstTracker, - onWarning, onError, suppressErrors + irFile.transformConst( + interpreter, mode = EvaluationMode.ONLY_INTRINSIC_CONST, evaluatedConstTracker, onWarning, onError, suppressErrors ) - irFile.transformChildren(transformer, null) } } diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrConstAnnotationTransformer.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrConstAnnotationTransformer.kt new file mode 100644 index 00000000000..0c203e2db70 --- /dev/null +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrConstAnnotationTransformer.kt @@ -0,0 +1,87 @@ +/* + * 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.constant.EvaluatedConstTracker +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer +import org.jetbrains.kotlin.ir.declarations.IrDeclarationBase +import org.jetbrains.kotlin.ir.declarations.IrField +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl +import org.jetbrains.kotlin.ir.interpreter.IrInterpreter +import org.jetbrains.kotlin.ir.interpreter.isPrimitiveArray +import org.jetbrains.kotlin.ir.interpreter.toIrConst +import org.jetbrains.kotlin.ir.types.* + +internal class IrConstAnnotationTransformer( + interpreter: IrInterpreter, + irFile: IrFile, + mode: EvaluationMode, + evaluatedConstTracker: EvaluatedConstTracker?, + onWarning: (IrFile, IrElement, IrErrorExpression) -> Unit, + onError: (IrFile, IrElement, IrErrorExpression) -> Unit, + suppressExceptions: Boolean, +) : IrConstTransformer(interpreter, irFile, mode, evaluatedConstTracker, onWarning, onError, suppressExceptions) { + override fun visitField(declaration: IrField): IrStatement { + transformAnnotations(declaration) + return super.visitField(declaration) + } + + override fun visitDeclaration(declaration: IrDeclarationBase): IrStatement { + transformAnnotations(declaration) + return super.visitDeclaration(declaration) + } + + private fun transformAnnotations(annotationContainer: IrAnnotationContainer) { + annotationContainer.annotations.forEach { annotation -> + transformAnnotation(annotation) + } + } + + private fun transformAnnotation(annotation: IrConstructorCall) { + for (i in 0 until annotation.valueArgumentsCount) { + val arg = annotation.getValueArgument(i) ?: continue + when (arg) { + is IrVararg -> annotation.putValueArgument(i, arg.transformVarArg()) + else -> annotation.putValueArgument(i, arg.transformSingleArg(annotation.symbol.owner.valueParameters[i].type)) + } + } + } + + private fun IrVararg.transformVarArg(): IrVararg { + if (elements.isEmpty()) return this + val newIrVararg = IrVarargImpl(this.startOffset, this.endOffset, this.type, this.varargElementType) + for (element in this.elements) { + when (val arg = (element as? IrSpreadElement)?.expression ?: element) { + is IrVararg -> arg.transformVarArg().elements.forEach { newIrVararg.addElement(it) } + is IrExpression -> newIrVararg.addElement(arg.transformSingleArg(this.varargElementType)) + else -> newIrVararg.addElement(arg) + } + } + return newIrVararg + } + + private fun IrExpression.transformSingleArg(expectedType: IrType): IrExpression { + if (this.canBeInterpreted(configuration = interpreter.environment.configuration.copy(treatFloatInSpecialWay = false))) { + return this.interpret(failAsError = true).convertToConstIfPossible(expectedType) + } else if (this is IrConstructorCall) { + transformAnnotation(this) + } + return this + } + + private fun IrExpression.convertToConstIfPossible(type: IrType): IrExpression { + return when { + this !is IrConst<*> || type is IrErrorType -> this + type.isArray() -> this.convertToConstIfPossible((type as IrSimpleType).arguments.single().typeOrNull!!) + type.isPrimitiveArray() -> this.convertToConstIfPossible(this.type) + else -> this.value.toIrConst(type, this.startOffset, this.endOffset) + } + } +} diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrConstExpressionTransformer.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrConstExpressionTransformer.kt new file mode 100644 index 00000000000..0f5e4763841 --- /dev/null +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrConstExpressionTransformer.kt @@ -0,0 +1,98 @@ +/* + * 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.constant.EvaluatedConstTracker +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.declarations.IrField +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrStringConcatenationImpl +import org.jetbrains.kotlin.ir.interpreter.IrInterpreter +import kotlin.math.max +import kotlin.math.min + +internal class IrConstExpressionTransformer( + interpreter: IrInterpreter, + irFile: IrFile, + mode: EvaluationMode, + evaluatedConstTracker: EvaluatedConstTracker?, + onWarning: (IrFile, IrElement, IrErrorExpression) -> Unit, + onError: (IrFile, IrElement, IrErrorExpression) -> Unit, + suppressExceptions: Boolean, +) : IrConstTransformer(interpreter, irFile, mode, evaluatedConstTracker, onWarning, onError, suppressExceptions) { + override fun visitCall(expression: IrCall): IrExpression { + if (expression.canBeInterpreted()) { + return expression.interpret(failAsError = false) + } + return super.visitCall(expression) + } + + override fun visitField(declaration: IrField): IrStatement { + val initializer = declaration.initializer + val expression = initializer?.expression ?: return declaration + val isConst = declaration.correspondingPropertySymbol?.owner?.isConst == true + if (!isConst) return super.visitField(declaration) + + if (expression.canBeInterpreted(declaration, interpreter.environment.configuration.copy(treatFloatInSpecialWay = false))) { + initializer.expression = expression.interpret(failAsError = true) + } + + return super.visitField(declaration) + } + + override fun visitStringConcatenation(expression: IrStringConcatenation): IrExpression { + fun IrExpression.wrapInStringConcat(): IrExpression = IrStringConcatenationImpl( + this.startOffset, this.endOffset, expression.type, listOf(this@wrapInStringConcat) + ) + + fun IrExpression.wrapInToStringConcatAndInterpret(): IrExpression = wrapInStringConcat().interpret(failAsError = false) + + // here `StringBuilder`'s list is used to optimize memory, everything works without it + val folded = mutableListOf() + val buildersList = mutableListOf() + for (next in expression.arguments) { + val last = folded.lastOrNull() + when { + !next.wrapInStringConcat().canBeInterpreted() -> { + folded += next + buildersList.add(StringBuilder()) + } + last == null || !last.wrapInStringConcat().canBeInterpreted() -> { + val result = next.wrapInToStringConcatAndInterpret() + folded += result + buildersList.add(StringBuilder((result as? IrConst<*>)?.value?.toString() ?: "")) + } + else -> { + val nextAsConst = next.wrapInToStringConcatAndInterpret() + if (nextAsConst !is IrConst<*>) { + folded += next + buildersList.add(StringBuilder()) + } else { + folded[folded.size - 1] = IrConstImpl.string( + // Inlined strings may have `last.startOffset > next.endOffset` + min(last.startOffset, next.startOffset), max(last.endOffset, next.endOffset), expression.type, "" + ) + buildersList.last().append(nextAsConst.value.toString()) + } + } + } + } + + val foldedConst = folded.singleOrNull() as? IrConst<*> + if (foldedConst != null) { + return IrConstImpl.string(expression.startOffset, expression.endOffset, expression.type, buildersList.single().toString()) + } + + folded.zip(buildersList).forEach { + @Suppress("UNCHECKED_CAST") + (it.first as? IrConst)?.value = it.second.toString() + } + return IrStringConcatenationImpl(expression.startOffset, expression.endOffset, expression.type, folded) + } +} diff --git a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrConstTransformer.kt b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrConstTransformer.kt index 1ea6a8b7716..7251b23c5b1 100644 --- a/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrConstTransformer.kt +++ b/compiler/ir/ir.interpreter/src/org/jetbrains/kotlin/ir/interpreter/checker/IrConstTransformer.kt @@ -8,29 +8,45 @@ package org.jetbrains.kotlin.ir.interpreter.checker import org.jetbrains.kotlin.constant.ErrorValue import org.jetbrains.kotlin.constant.EvaluatedConstTracker import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.IrStatement -import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.name +import org.jetbrains.kotlin.ir.expressions.IrConst +import org.jetbrains.kotlin.ir.expressions.IrErrorExpression +import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrStringConcatenationImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrVarargImpl -import org.jetbrains.kotlin.ir.interpreter.* -import org.jetbrains.kotlin.ir.interpreter.isPrimitiveArray -import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.interpreter.IrInterpreter +import org.jetbrains.kotlin.ir.interpreter.IrInterpreterConfiguration +import org.jetbrains.kotlin.ir.interpreter.toConstantValue import org.jetbrains.kotlin.ir.util.dump import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.name.Name -import kotlin.math.max -import kotlin.math.min -class IrConstTransformer( - private val interpreter: IrInterpreter, +fun IrFile.transformConst( + interpreter: IrInterpreter, + mode: EvaluationMode, + evaluatedConstTracker: EvaluatedConstTracker?, + onWarning: (IrFile, IrElement, IrErrorExpression) -> Unit = { _, _, _ -> }, + onError: (IrFile, IrElement, IrErrorExpression) -> Unit = { _, _, _ -> }, + suppressExceptions: Boolean = false, +) { + val irConstExpressionTransformer = IrConstExpressionTransformer( + interpreter, this, mode, evaluatedConstTracker, onWarning, onError, suppressExceptions + ) + val irConstAnnotationTransformer = IrConstAnnotationTransformer( + interpreter, this, mode, evaluatedConstTracker, onWarning, onError, suppressExceptions + ) + this.transform(irConstExpressionTransformer, null) + this.transform(irConstAnnotationTransformer, null) +} + +internal abstract class IrConstTransformer( + protected val interpreter: IrInterpreter, private val irFile: IrFile, private val mode: EvaluationMode, private val evaluatedConstTracker: EvaluatedConstTracker? = null, - private val onWarning: (IrFile, IrElement, IrErrorExpression) -> Unit = { _, _, _ -> }, - private val onError: (IrFile, IrElement, IrErrorExpression) -> Unit = { _, _, _ -> }, - private val suppressExceptions: Boolean = false, + private val onWarning: (IrFile, IrElement, IrErrorExpression) -> Unit, + private val onError: (IrFile, IrElement, IrErrorExpression) -> Unit, + private val suppressExceptions: Boolean, ) : IrElementTransformerVoid() { private fun IrExpression.warningIfError(original: IrExpression): IrExpression { if (this is IrErrorExpression) { @@ -52,7 +68,7 @@ class IrConstTransformer( return this } - private fun IrExpression.canBeInterpreted( + protected fun IrExpression.canBeInterpreted( containingDeclaration: IrElement? = null, configuration: IrInterpreterConfiguration = interpreter.environment.configuration ): Boolean { @@ -66,7 +82,7 @@ class IrConstTransformer( } } - private fun IrExpression.interpret(failAsError: Boolean): IrExpression { + protected fun IrExpression.interpret(failAsError: Boolean): IrExpression { val result = try { interpreter.interpret(this, irFile) } catch (e: Throwable) { @@ -83,128 +99,4 @@ class IrConstTransformer( ) return if (failAsError) result.reportIfError(this) else result.warningIfError(this) } - - override fun visitCall(expression: IrCall): IrExpression { - if (expression.canBeInterpreted()) { - return expression.interpret(failAsError = false) - } - return super.visitCall(expression) - } - - override fun visitField(declaration: IrField): IrStatement { - transformAnnotations(declaration) - - val initializer = declaration.initializer - val expression = initializer?.expression ?: return declaration - val isConst = declaration.correspondingPropertySymbol?.owner?.isConst == true - if (!isConst) return super.visitField(declaration) - - if (expression.canBeInterpreted(declaration, interpreter.environment.configuration.copy(treatFloatInSpecialWay = false))) { - initializer.expression = expression.interpret(failAsError = true) - } - - return super.visitField(declaration) - } - - override fun visitStringConcatenation(expression: IrStringConcatenation): IrExpression { - fun IrExpression.wrapInStringConcat(): IrExpression = IrStringConcatenationImpl( - this.startOffset, this.endOffset, expression.type, listOf(this@wrapInStringConcat) - ) - - fun IrExpression.wrapInToStringConcatAndInterpret(): IrExpression = wrapInStringConcat().interpret(failAsError = false) - - // here `StringBuilder`'s list is used to optimize memory, everything works without it - val folded = mutableListOf() - val buildersList = mutableListOf() - for (next in expression.arguments) { - val last = folded.lastOrNull() - when { - !next.wrapInStringConcat().canBeInterpreted() -> { - folded += next - buildersList.add(StringBuilder()) - } - last == null || !last.wrapInStringConcat().canBeInterpreted() -> { - val result = next.wrapInToStringConcatAndInterpret() - folded += result - buildersList.add(StringBuilder((result as? IrConst<*>)?.value?.toString() ?: "")) - } - else -> { - val nextAsConst = next.wrapInToStringConcatAndInterpret() - if (nextAsConst !is IrConst<*>) { - folded += next - buildersList.add(StringBuilder()) - } else { - folded[folded.size - 1] = IrConstImpl.string( - // Inlined strings may have `last.startOffset > next.endOffset` - min(last.startOffset, next.startOffset), max(last.endOffset, next.endOffset), expression.type, "" - ) - buildersList.last().append(nextAsConst.value.toString()) - } - } - } - } - - val foldedConst = folded.singleOrNull() as? IrConst<*> - if (foldedConst != null) { - return IrConstImpl.string(expression.startOffset, expression.endOffset, expression.type, buildersList.single().toString()) - } - - folded.zip(buildersList).forEach { - @Suppress("UNCHECKED_CAST") - (it.first as? IrConst)?.value = it.second.toString() - } - return IrStringConcatenationImpl(expression.startOffset, expression.endOffset, expression.type, folded) - } - - override fun visitDeclaration(declaration: IrDeclarationBase): IrStatement { - transformAnnotations(declaration) - return super.visitDeclaration(declaration) - } - - private fun transformAnnotations(annotationContainer: IrAnnotationContainer) { - annotationContainer.annotations.forEach { annotation -> - transformAnnotation(annotation) - } - } - - private fun transformAnnotation(annotation: IrConstructorCall) { - for (i in 0 until annotation.valueArgumentsCount) { - val arg = annotation.getValueArgument(i) ?: continue - when (arg) { - is IrVararg -> annotation.putValueArgument(i, arg.transformVarArg()) - else -> annotation.putValueArgument(i, arg.transformSingleArg(annotation.symbol.owner.valueParameters[i].type)) - } - } - } - - private fun IrVararg.transformVarArg(): IrVararg { - if (elements.isEmpty()) return this - val newIrVararg = IrVarargImpl(this.startOffset, this.endOffset, this.type, this.varargElementType) - for (element in this.elements) { - when (val arg = (element as? IrSpreadElement)?.expression ?: element) { - is IrVararg -> arg.transformVarArg().elements.forEach { newIrVararg.addElement(it) } - is IrExpression -> newIrVararg.addElement(arg.transformSingleArg(this.varargElementType)) - else -> newIrVararg.addElement(arg) - } - } - return newIrVararg - } - - private fun IrExpression.transformSingleArg(expectedType: IrType): IrExpression { - if (this.canBeInterpreted(configuration = interpreter.environment.configuration.copy(treatFloatInSpecialWay = false))) { - return this.interpret(failAsError = true).convertToConstIfPossible(expectedType) - } else if (this is IrConstructorCall) { - transformAnnotation(this) - } - return this - } - - private fun IrExpression.convertToConstIfPossible(type: IrType): IrExpression { - return when { - this !is IrConst<*> || type is IrErrorType -> this - type.isArray() -> this.convertToConstIfPossible((type as IrSimpleType).arguments.single().typeOrNull!!) - type.isPrimitiveArray() -> this.convertToConstIfPossible(this.type) - else -> this.value.toIrConst(type, this.startOffset, this.endOffset) - } - } -} \ No newline at end of file +}