From 02277d02939787d5cbe79ec93580b12f512363be Mon Sep 17 00:00:00 2001 From: Svyatoslav Kuzmich Date: Tue, 2 Oct 2018 15:57:30 +0300 Subject: [PATCH] [JS IR BE] ExpectDeclarationsRemoving lowering * Copy lowering from konan to common * Keep actual default parameters when both actual and expect default parameters are present * Run lowering before inline in JS IR BE to fix box/multiplatform/defaultArguments/inlineFunctionWithDefaultLambda.kt --- .../lower/ExpectDeclarationsRemoving.kt | 143 ++++++++++++++++++ .../kotlin/ir/backend/js/compiler.kt | 2 + .../defaultArguments/constructor.kt | 1 - .../defaultArguments/function.kt | 1 - .../inheritedFromExpectedClass.kt | 1 - .../inheritedFromExpectedMethod.kt | 1 - .../inheritedInExpectedDeclarations.kt | 1 - .../inlineFunctionWithDefaultLambda.kt | 1 - .../multiplatform/defaultArguments/kt23239.kt | 1 - 9 files changed, 145 insertions(+), 7 deletions(-) create mode 100644 compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ExpectDeclarationsRemoving.kt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ExpectDeclarationsRemoving.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ExpectDeclarationsRemoving.kt new file mode 100644 index 00000000000..d164a83adf2 --- /dev/null +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/ExpectDeclarationsRemoving.kt @@ -0,0 +1,143 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. 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.descriptors.MemberDescriptor +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrGetValue +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol +import org.jetbrains.kotlin.ir.symbols.IrValueSymbol +import org.jetbrains.kotlin.ir.util.referenceFunction +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.ir.visitors.acceptVoid +import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker +import org.jetbrains.kotlin.resolve.descriptorUtil.module +import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver + +/** + * This pass removes all declarations with `isExpect == true`. + */ +class ExpectDeclarationsRemoving(val context: BackendContext) : FileLoweringPass { + + override fun lower(irFile: IrFile) { + // All declarations with `isExpect == true` are nested into a top-level declaration with `isExpect == true`. + irFile.declarations.removeAll { + val descriptor = it.descriptor + if (descriptor is MemberDescriptor && descriptor.isExpect) { + copyDefaultArgumentsFromExpectToActual(it) + true + } else { + false + } + } + } + + private fun copyDefaultArgumentsFromExpectToActual(declaration: IrDeclaration) { + declaration.acceptVoid(object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitValueParameter(declaration: IrValueParameter) { + super.visitValueParameter(declaration) + + val defaultValue = declaration.defaultValue ?: return + val function = declaration.parent as IrFunction + + val index = declaration.index + assert(function.valueParameters[index] == declaration) + + if (function is IrConstructor && + ExpectedActualDeclarationChecker.isOptionalAnnotationClass( + function.descriptor.constructedClass + ) + ) { + return + } + + val actualParameter = function.findActualForExpected().valueParameters[index] + + // Keep actual default value if present. They are generally not allowed but can be suppressed with + // @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") + if (actualParameter.defaultValue != null) + return + + actualParameter.defaultValue = defaultValue.also { + it.expression = it.expression.remapExpectValueSymbols() + } + } + }) + } + + private fun IrFunction.findActualForExpected(): IrFunction = + context.ir.symbols.externalSymbolTable.referenceFunction(descriptor.findActualForExpect()).owner + + private fun IrClass.findActualForExpected(): IrClass = + context.ir.symbols.externalSymbolTable.referenceClass(descriptor.findActualForExpect()).owner + + private inline fun T.findActualForExpect() = with(ExpectedActualResolver) { + val descriptor = this@findActualForExpect + + if (!descriptor.isExpect) error(this) + + findCompatibleActualForExpected(descriptor.module).singleOrNull() ?: error(descriptor) + } as T + + private fun IrExpression.remapExpectValueSymbols(): IrExpression { + return this.transform(object : IrElementTransformerVoid() { + + override fun visitGetValue(expression: IrGetValue): IrExpression { + expression.transformChildrenVoid() + val newValue = remapExpectValue(expression.symbol) + ?: return expression + + return IrGetValueImpl( + expression.startOffset, + expression.endOffset, + newValue.type, + newValue.symbol, + expression.origin + ) + } + }, data = null) + } + + private fun remapExpectValue(symbol: IrValueSymbol): IrValueParameter? { + if (symbol !is IrValueParameterSymbol) { + return null + } + + val parameter = symbol.owner + val parent = parameter.parent + + return when (parent) { + is IrClass -> { + assert(parameter == parent.thisReceiver) + parent.findActualForExpected().thisReceiver!! + } + + is IrFunction -> when (parameter) { + parent.dispatchReceiverParameter -> + parent.findActualForExpected().dispatchReceiverParameter!! + parent.extensionReceiverParameter -> + parent.findActualForExpected().extensionReceiverParameter!! + else -> { + assert(parent.valueParameters[parameter.index] == parameter) + parent.findActualForExpected().valueParameters[parameter.index] + } + } + + else -> error(parent) + } + } +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt index 99f7e739f50..e0700c925fd 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/compiler.kt @@ -76,6 +76,8 @@ fun compile( MoveExternalDeclarationsToSeparatePlace().lower(moduleFragment.files) + moduleFragment.files.forEach(ExpectDeclarationsRemoving(context)::lower) + moduleFragment.files.forEach(CoroutineIntrinsicLowering(context)::lower) moduleFragment.files.forEach { ArrayInlineConstructorLowering(context).lower(it) } diff --git a/compiler/testData/codegen/box/multiplatform/defaultArguments/constructor.kt b/compiler/testData/codegen/box/multiplatform/defaultArguments/constructor.kt index 5f2ccb04fc2..3d18adacce5 100644 --- a/compiler/testData/codegen/box/multiplatform/defaultArguments/constructor.kt +++ b/compiler/testData/codegen/box/multiplatform/defaultArguments/constructor.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +MultiPlatformProjects -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // FILE: common.kt diff --git a/compiler/testData/codegen/box/multiplatform/defaultArguments/function.kt b/compiler/testData/codegen/box/multiplatform/defaultArguments/function.kt index 704d33e4fa6..53f7f987aea 100644 --- a/compiler/testData/codegen/box/multiplatform/defaultArguments/function.kt +++ b/compiler/testData/codegen/box/multiplatform/defaultArguments/function.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +MultiPlatformProjects -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // FILE: common.kt diff --git a/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedFromExpectedClass.kt b/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedFromExpectedClass.kt index d3bc14ac9fe..23aae686691 100644 --- a/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedFromExpectedClass.kt +++ b/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedFromExpectedClass.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +MultiPlatformProjects -// IGNORE_BACKEND: JS_IR // IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // FILE: common.kt diff --git a/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedFromExpectedMethod.kt b/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedFromExpectedMethod.kt index 2567df20cd5..c4ea69ccf78 100644 --- a/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedFromExpectedMethod.kt +++ b/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedFromExpectedMethod.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +MultiPlatformProjects -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME // IGNORE_BACKEND: JVM_IR // FILE: common.kt diff --git a/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedInExpectedDeclarations.kt b/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedInExpectedDeclarations.kt index 3c1a684d0be..abe6f5ca198 100644 --- a/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedInExpectedDeclarations.kt +++ b/compiler/testData/codegen/box/multiplatform/defaultArguments/inheritedInExpectedDeclarations.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +MultiPlatformProjects -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME // IGNORE_BACKEND: JVM_IR // FILE: common.kt diff --git a/compiler/testData/codegen/box/multiplatform/defaultArguments/inlineFunctionWithDefaultLambda.kt b/compiler/testData/codegen/box/multiplatform/defaultArguments/inlineFunctionWithDefaultLambda.kt index 66987be0a2c..98df3a07d06 100644 --- a/compiler/testData/codegen/box/multiplatform/defaultArguments/inlineFunctionWithDefaultLambda.kt +++ b/compiler/testData/codegen/box/multiplatform/defaultArguments/inlineFunctionWithDefaultLambda.kt @@ -1,6 +1,5 @@ // !LANGUAGE: +MultiPlatformProjects // IGNORE_BACKEND: JVM_IR -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME // FILE: common.kt diff --git a/compiler/testData/codegen/box/multiplatform/defaultArguments/kt23239.kt b/compiler/testData/codegen/box/multiplatform/defaultArguments/kt23239.kt index f3b2c59a0f3..55334ec1fda 100644 --- a/compiler/testData/codegen/box/multiplatform/defaultArguments/kt23239.kt +++ b/compiler/testData/codegen/box/multiplatform/defaultArguments/kt23239.kt @@ -1,5 +1,4 @@ // !LANGUAGE: +MultiPlatformProjects -// IGNORE_BACKEND: JS_IR // WITH_RUNTIME // IGNORE_BACKEND: JVM_IR // FILE: common.kt