[JS IR BE] Unit Materialization lowering

Fix unit-related things across backend
This commit is contained in:
romanart
2018-09-21 19:15:02 +03:00
parent 57ae60e9dc
commit 9b40981368
7 changed files with 76 additions and 24 deletions
@@ -0,0 +1,39 @@
/*
* 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.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.types.classifierOrFail
import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
class UnitMaterializationLowering(context: BackendContext): FileLoweringPass {
private val unitType = context.irBuiltIns.unitType
private val unitValue = IrGetObjectValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, unitType, unitType.classifierOrFail as IrClassSymbol)
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
override fun visitCall(expression: IrCall): IrExpression {
expression.transformChildrenVoid(this)
return if (expression.type.isUnit()) expression.let {
IrCompositeImpl(it.startOffset, it.endOffset, it.type, it.origin, listOf(it, unitValue))
}
else expression
}
})
}
}
@@ -22,15 +22,6 @@ import org.jetbrains.kotlin.ir.backend.js.lower.inline.RemoveInlineFunctionsWith
import org.jetbrains.kotlin.ir.backend.js.lower.inline.ReturnableBlockLowering import org.jetbrains.kotlin.ir.backend.js.lower.inline.ReturnableBlockLowering
import org.jetbrains.kotlin.ir.backend.js.lower.inline.replaceUnboundSymbols import org.jetbrains.kotlin.ir.backend.js.lower.inline.replaceUnboundSymbols
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.IrModuleToJsTransformer
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.expressions.IrBlock
import org.jetbrains.kotlin.ir.expressions.IrReturnableBlock
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
@@ -84,7 +75,6 @@ fun compile(
MoveExternalDeclarationsToSeparatePlace().lower(moduleFragment.files) MoveExternalDeclarationsToSeparatePlace().lower(moduleFragment.files)
moduleFragment.files.forEach(CoroutineIntrinsicLowering(context)::lower) moduleFragment.files.forEach(CoroutineIntrinsicLowering(context)::lower)
moduleFragment.files.forEach { ArrayInlineConstructorLowering(context).lower(it) } moduleFragment.files.forEach { ArrayInlineConstructorLowering(context).lower(it) }
@@ -111,6 +101,7 @@ private fun JsIrBackendContext.performInlining(moduleFragment: IrModuleFragment)
} }
private fun JsIrBackendContext.lower(moduleFragment: IrModuleFragment, dependencies: List<IrModuleFragment>) { private fun JsIrBackendContext.lower(moduleFragment: IrModuleFragment, dependencies: List<IrModuleFragment>) {
moduleFragment.files.forEach(UnitMaterializationLowering(this)::lower)
moduleFragment.files.forEach(VarargLowering(this)::lower) moduleFragment.files.forEach(VarargLowering(this)::lower)
moduleFragment.files.forEach(LateinitLowering(this, true)::lower) moduleFragment.files.forEach(LateinitLowering(this, true)::lower)
moduleFragment.files.forEach(DefaultArgumentStubGenerator(this)::runOnFilePostfix) moduleFragment.files.forEach(DefaultArgumentStubGenerator(this)::runOnFilePostfix)
@@ -528,7 +528,7 @@ class BlockDecomposerTransformer(context: JsIrBackendContext) : IrElementTransfo
override fun visitContainerExpression(expression: IrContainerExpression): IrExpression { override fun visitContainerExpression(expression: IrContainerExpression): IrExpression {
expression.run { if (statements.isEmpty()) return IrCompositeImpl(startOffset, endOffset, type, origin, emptyList()) } expression.run { if (statements.isEmpty()) return IrCompositeImpl(startOffset, endOffset, type, origin, listOf(unitValue)) }
val newStatements = mutableListOf<IrStatement>() val newStatements = mutableListOf<IrStatement>()
@@ -103,6 +103,8 @@ class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass {
} }
} }
private val unitValue = JsIrBuilder.buildGetObjectValue(context.irBuiltIns.unitType, context.irBuiltIns.unitClass)
// Ported from from jvm.lower.BridgeLowering // Ported from from jvm.lower.BridgeLowering
private fun createBridge( private fun createBridge(
function: IrSimpleFunction, function: IrSimpleFunction,
@@ -146,7 +148,19 @@ class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass {
irFunction.valueParameters.subList(0, toTake).mapIndexed { i, valueParameter -> irFunction.valueParameters.subList(0, toTake).mapIndexed { i, valueParameter ->
call.putValueArgument(i, irCastIfNeeded(irGet(valueParameter), delegateTo.valueParameters[i].type)) call.putValueArgument(i, irCastIfNeeded(irGet(valueParameter), delegateTo.valueParameters[i].type))
} }
+irReturn(call)
// This is required for Unit materialization
// TODO: generalize for boxed types and inline classes
// TODO: use return type in signature too
val returnValue = if (delegateTo.returnType.isUnit() && !function.returnType.isUnit()) {
irComposite(resultType = irFunction.returnType) {
+call
+unitValue
}
} else {
call
}
+irReturn(returnValue)
}.apply { }.apply {
irFunction.body = this irFunction.body = this
} }
@@ -156,7 +170,7 @@ class BridgesConstruction(val context: JsIrBackendContext) : ClassLoweringPass {
// TODO: get rid of Unit check // TODO: get rid of Unit check
private fun IrBlockBodyBuilder.irCastIfNeeded(argument: IrExpression, type: IrType): IrExpression = private fun IrBlockBodyBuilder.irCastIfNeeded(argument: IrExpression, type: IrType): IrExpression =
if (argument.type.classifierOrNull == type.classifierOrNull || type.isUnit()) argument else irAs(argument, type) if (argument.type.classifierOrNull == type.classifierOrNull) argument else irAs(argument, type)
} }
// Handle for common.bridges // Handle for common.bridges
@@ -18,7 +18,10 @@ import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrVariable import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.visitors.* import org.jetbrains.kotlin.ir.visitors.*
@@ -412,6 +415,12 @@ class StateMachineBuilder(
transformLastExpression { expression.apply { value = it } } transformLastExpression { expression.apply { value = it } }
} }
override fun visitTypeOperator(expression: IrTypeOperatorCall) {
if (expression !in suspendableNodes) return addStatement(expression)
expression.acceptChildrenVoid(this)
transformLastExpression { expression.apply { argument = it } }
}
override fun visitVariable(declaration: IrVariable) { override fun visitVariable(declaration: IrVariable) {
if (declaration !in suspendableNodes) return addStatement(declaration) if (declaration !in suspendableNodes) return addStatement(declaration)
declaration.acceptChildrenVoid(this) declaration.acceptChildrenVoid(this)
@@ -542,7 +551,7 @@ class StateMachineBuilder(
currentState.successors += catchBlockStack.peek()!! currentState.successors += catchBlockStack.peek()!!
} }
private fun hasResultingValue(expression: IrExpression) = expression.type.run { !isUnit() && !isNothing() } private fun hasResultingValue(expression: IrExpression) = !expression.type.isNothing()
override fun visitThrow(expression: IrThrow) { override fun visitThrow(expression: IrThrow) {
expression.acceptChildrenVoid(this) expression.acceptChildrenVoid(this)
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.ir.util.isFunctionTypeOrSubtype import org.jetbrains.kotlin.ir.util.isFunctionTypeOrSubtype
import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
@@ -70,13 +69,9 @@ class IrElementToJsExpressionTransformer : BaseIrElementToJsNodeTransformer<JsEx
override fun visitGetObjectValue(expression: IrGetObjectValue, context: JsGenerationContext) = when (expression.symbol.owner.kind) { override fun visitGetObjectValue(expression: IrGetObjectValue, context: JsGenerationContext) = when (expression.symbol.owner.kind) {
ClassKind.OBJECT -> { ClassKind.OBJECT -> {
// TODO: return unit instance instead of null val className = context.getNameForSymbol(expression.symbol)
if (expression.type.isUnit()) JsNullLiteral() val getInstanceName = className.ident + "_getInstance"
else { JsInvocation(JsNameRef(getInstanceName))
val className = context.getNameForSymbol(expression.symbol)
val getInstanceName = className.ident + "_getInstance"
JsInvocation(JsNameRef(getInstanceName))
}
} }
else -> TODO() else -> TODO()
} }
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.js.naming.isES5IdentifierPart
import org.jetbrains.kotlin.js.naming.isES5IdentifierStart import org.jetbrains.kotlin.js.naming.isES5IdentifierStart
import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
// TODO: this class has to be reimplemented soon // TODO: this class has to be reimplemented soon
class SimpleNameGenerator : NameGenerator { class SimpleNameGenerator : NameGenerator {
@@ -147,7 +148,10 @@ class SimpleNameGenerator : NameGenerator {
if (declaration.kind == ClassKind.OBJECT || declaration.name.isSpecial || declaration.visibility == Visibilities.LOCAL) { if (declaration.kind == ClassKind.OBJECT || declaration.name.isSpecial || declaration.visibility == Visibilities.LOCAL) {
nameDeclarator = context.staticContext.rootScope::declareFreshName if (declaration.descriptor !is DeserializedClassDescriptor) {
// TODO: temporary workaround for Unit instance
nameDeclarator = context.staticContext.rootScope::declareFreshName
}
val parent = declaration.parent val parent = declaration.parent
when (parent) { when (parent) {
is IrDeclaration -> nameBuilder.append(getNameForDeclaration(parent, context)) is IrDeclaration -> nameBuilder.append(getNameForDeclaration(parent, context))