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 d29c47e1145..d37879bf0f7 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 @@ -240,6 +240,10 @@ class Fir2IrConverter( descriptor.owner.parent = parentClass ?: continue } + irModuleFragment.files.forEach { + it.transformChildren(IrConstTransformer(irModuleFragment), null) + } + return Fir2IrResult(irModuleFragment, symbolTable, sourceManager, components) } } diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/IrConstTransformer.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/IrConstTransformer.kt new file mode 100644 index 00000000000..84b36bad79f --- /dev/null +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/IrConstTransformer.kt @@ -0,0 +1,73 @@ +/* + * Copyright 2010-2020 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.fir.backend + +import org.jetbrains.kotlin.backend.common.interpreter.EvaluationMode +import org.jetbrains.kotlin.backend.common.interpreter.IrCompileTimeChecker +import org.jetbrains.kotlin.backend.common.interpreter.IrInterpreter +import org.jetbrains.kotlin.backend.common.interpreter.toIrConst +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrField +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.expressions.IrCall +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.types.IrSimpleType +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.isArray +import org.jetbrains.kotlin.ir.types.typeOrNull +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid + +class IrConstTransformer(irModuleFragment: IrModuleFragment) : IrElementTransformerVoid() { + private val interpreter = IrInterpreter(irModuleFragment) + + private fun IrExpression.report(original: IrExpression): IrExpression { + if (this == original) return this + return if (this !is IrErrorExpression) this else original + } + + override fun visitCall(expression: IrCall): IrExpression { + if (expression.accept(IrCompileTimeChecker(mode = EvaluationMode.ONLY_BUILTINS), null)) { + return interpreter.interpret(expression).report(expression) + } + return super.visitCall(expression) + } + + override fun visitField(declaration: IrField): IrStatement { + val initializer = declaration.initializer + val expression = initializer?.expression ?: return declaration + if (expression is IrConst<*>) return declaration + val isCompileTimeComputable = expression.accept(IrCompileTimeChecker(declaration, mode = EvaluationMode.ONLY_BUILTINS), null) + val isConst = declaration.correspondingPropertySymbol?.owner?.isConst == true + if (isConst && !isCompileTimeComputable) { + //throw AssertionError("Const property is used only with functions annotated as CompileTimeCalculation: " + declaration.dump()) + } else if (isCompileTimeComputable) { + initializer.expression = interpreter.interpret(expression).report(expression) + } + return declaration + } + + override fun visitClass(declaration: IrClass): IrStatement { + declaration.annotations.forEach { + for (i in 0 until it.valueArgumentsCount) { + val arg = it.getValueArgument(i) ?: continue + if (arg.accept(IrCompileTimeChecker(mode = EvaluationMode.ONLY_BUILTINS), null)) { + val const = interpreter.interpret(arg).report(arg) + it.putValueArgument(i, const.convertToConstIfPossible(it.symbol.owner.valueParameters[i].type)) + } + } + } + return super.visitClass(declaration) + } + + private fun IrExpression.convertToConstIfPossible(type: IrType): IrExpression { + if (this !is IrConst<*>) return this + if (type.isArray()) return this.convertToConstIfPossible((type as IrSimpleType).arguments.single().typeOrNull!!) + return this.value.toIrConst(type, this.startOffset, this.endOffset) + } +} \ No newline at end of file diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrCompileTimeChecker.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrCompileTimeChecker.kt index b4ec87fd993..efd532a1ec5 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrCompileTimeChecker.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/interpreter/IrCompileTimeChecker.kt @@ -13,6 +13,8 @@ import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.types.classifierOrNull +import org.jetbrains.kotlin.ir.types.isPrimitiveType +import org.jetbrains.kotlin.ir.types.isString import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.name.FqName @@ -32,9 +34,21 @@ class IrCompileTimeChecker( private fun IrDeclaration.isContract() = isMarkedWith(contractsDslAnnotation) private fun IrDeclaration.isMarkedAsEvaluateIntrinsic() = isMarkedWith(evaluateIntrinsicAnnotation) - private fun IrDeclaration.isMarkedAsCompileTime(): Boolean = isMarkedWith(compileTimeAnnotation) || - (this is IrSimpleFunction && this.isFakeOverride && this.overriddenSymbols.any { it.owner.isMarkedAsCompileTime() }) || - (this.parent as? IrClass)?.fqNameWhenAvailable?.asString() in compileTimeTypeAliases + private fun IrDeclaration.isMarkedAsCompileTime(): Boolean { + if (mode == EvaluationMode.FULL) + return isMarkedWith(compileTimeAnnotation) || + (this is IrSimpleFunction && this.isFakeOverride && this.overriddenSymbols.any { it.owner.isMarkedAsCompileTime() }) || + this.parentClassOrNull?.fqNameWhenAvailable?.asString() in compileTimeTypeAliases + + val parent = this.parentClassOrNull + val parentType = parent?.defaultType + return when { + parentType?.isPrimitiveType() == true -> (this as IrFunction).name.asString() !in setOf("inc", "dec", "rangeTo", "hashCode") + parentType?.isString() == true -> (this as IrDeclarationWithName).name.asString() !in setOf("subSequence", "hashCode") + parent?.isCompanion == true -> parent.parentClassOrNull?.defaultType?.let { it.isPrimitiveType() || it.isUnsigned() } == true + else -> false + } + } private fun IrDeclaration.isMarkedWith(annotation: FqName): Boolean { if (this is IrClass && this.isCompanion) return false