Supported common lateinit lowering

This commit is contained in:
Igor Chevdar
2019-01-22 14:21:20 +03:00
parent 1dc8494824
commit ac2cf0eedb
6 changed files with 14 additions and 162 deletions
@@ -339,8 +339,8 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
}
fun printDescriptors() {
// A workaround to check if the lateinit field is assigned, see KT-9327
try { moduleDescriptor } catch (e: UninitializedPropertyAccessException) { return }
if (!::moduleDescriptor.isInitialized)
return
separator("Descriptors after: ${phase?.description}")
moduleDescriptor.deepPrint()
@@ -12,7 +12,6 @@ import org.jetbrains.kotlin.backend.konan.lower.*
import org.jetbrains.kotlin.backend.konan.lower.ExpectDeclarationsRemoving
import org.jetbrains.kotlin.backend.konan.lower.FinallyBlocksLowering
import org.jetbrains.kotlin.backend.konan.lower.InitializersLowering
import org.jetbrains.kotlin.backend.konan.lower.LateinitLowering
import org.jetbrains.kotlin.backend.konan.lower.VarargInjectionLowering
import org.jetbrains.kotlin.backend.konan.lower.loops.ForLoopsLowering
import org.jetbrains.kotlin.ir.declarations.IrFile
@@ -125,13 +125,16 @@ internal class KonanIr(context: Context, irModule: IrModuleFragment): Ir<Context
}
internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val lazySymbolTable: ReferenceSymbolTable): Symbols<Context>(context, lazySymbolTable) {
/**
* @note:
* [lateinitIsInitializedPropertyGetter] is used in [org.jetbrains.kotlin.backend.common.lower.LateinitLowering] and
* it's irrelevant for [org.jetbrains.kotlin.backend.konan.lower.LateinitLowering].
*/
private val isInitializedPropertyDescriptor = builtInsPackage("kotlin")
.getContributedVariables(Name.identifier("isInitialized"), NoLookupLocation.FROM_BACKEND).single {
it.extensionReceiverParameter.let {
it != null && TypeUtils.getClassDescriptor(it.type) == context.reflectionTypes.kProperty0
} && !it.isExpect
}
override val lateinitIsInitializedPropertyGetter: IrSimpleFunctionSymbol
get() = TODO("unimplemented")
= symbolTable.referenceSimpleFunction(isInitializedPropertyDescriptor.getter!!)
val entryPoint = findMainEntryPoint(context)?.let { symbolTable.referenceSimpleFunction(it) }
@@ -450,15 +453,6 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable, val
val refClass = symbolTable.referenceClass(context.getInternalClass("Ref"))
val isInitializedPropertyDescriptor = builtInsPackage("kotlin")
.getContributedVariables(Name.identifier("isInitialized"), NoLookupLocation.FROM_BACKEND).single {
it.extensionReceiverParameter.let {
it != null && TypeUtils.getClassDescriptor(it.type) == context.reflectionTypes.kProperty0
} && !it.isExpect
}
val isInitializedGetter = symbolTable.referenceSimpleFunction(isInitializedPropertyDescriptor.getter!!)
val kFunctionImpl = symbolTable.referenceClass(context.reflectionTypes.kFunctionImpl)
val kMutableProperty0 = symbolTable.referenceClass(context.reflectionTypes.kMutableProperty0)
@@ -52,7 +52,7 @@ internal class FunctionInlining(val context: Context) : IrElementTransformerVoid
val callSite = super.visitCall(expression) as IrCall
if (!callSite.descriptor.needsInlining)
return callSite
if (callSite.symbol == context.ir.symbols.isInitializedGetter)
if (callSite.symbol == context.ir.symbols.lateinitIsInitializedPropertyGetter)
return callSite
val callee = getFunctionDeclaration(callSite.symbol)
@@ -1,141 +0,0 @@
/*
* 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 file.
*/
package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.lower.*
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.descriptors.resolveFakeOverride
import org.jetbrains.kotlin.backend.konan.irasdescriptors.isReal
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrCall
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrGetValue
import org.jetbrains.kotlin.ir.expressions.IrPropertyReference
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
internal class LateinitLowering(
val context: Context,
private val generateParameterNameInAssertion: Boolean = false
) : FileLoweringPass {
private val isInitializedGetter = context.ir.symbols.isInitializedGetter
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(object : IrBuildingTransformer(context) {
override fun visitVariable(declaration: IrVariable): IrStatement {
declaration.transformChildrenVoid(this)
if (!declaration.isLateinit) return declaration
assert(declaration.initializer == null) {
"'lateinit' modifier is not allowed for variables with initializer"
}
builder.at(declaration).run {
declaration.initializer = irNull()
}
return declaration
}
override fun visitGetValue(expression: IrGetValue): IrExpression {
val symbol = expression.symbol
if (symbol !is IrVariableSymbol || !symbol.owner.isLateinit) return expression
builder.at(expression).run {
return irBlock(expression) {
// TODO: do data flow analysis to check if value is proved to be not-null.
+irIfThen(
irEqualsNull(irGet(expression.type, symbol)),
throwUninitializedPropertyAccessException(symbol.owner.name)
)
+irGet(expression.type, symbol)
}
}
}
override fun visitCall(expression: IrCall): IrExpression {
expression.transformChildrenVoid(this)
if (expression.symbol != isInitializedGetter) return expression
val propertyReference = expression.extensionReceiver!! as IrPropertyReference
assert(propertyReference.extensionReceiver == null) {
"'lateinit' modifier is not allowed on extension properties"
}
val getter = propertyReference.getter?.owner ?: TODO(propertyReference.dump())
val property = getter.resolveFakeOverride().correspondingProperty!!
builder.at(expression).run {
val field = property.backingField!!
val fieldValue = irGetField(propertyReference.dispatchReceiver, field)
return irNotEquals(fieldValue, irNull())
}
}
override fun visitProperty(declaration: IrProperty): IrStatement {
declaration.transformChildrenVoid(this)
if (!declaration.isLateinit || !declaration.isReal)
return declaration
val backingField = declaration.backingField!!
transformGetter(backingField, declaration.getter!!)
assert(backingField.initializer == null) {
"'lateinit' modifier is not allowed for properties with initializer"
}
val irBuilder = context.createIrBuilder(backingField.symbol, declaration.startOffset, declaration.endOffset)
irBuilder.run {
backingField.initializer = irExprBody(irNull())
}
return declaration
}
private fun transformGetter(backingField: IrField, getter: IrFunction) {
val irBuilder = context.createIrBuilder(getter.symbol, getter.startOffset, getter.endOffset)
irBuilder.run {
getter.body = irBlockBody {
val resultVar = irTemporary(
irGetField(getter.dispatchReceiverParameter?.let { irGet(it) }, backingField)
)
+irIfThenElse(
context.irBuiltIns.nothingType,
irNotEquals(irGet(resultVar), irNull()),
irReturn(irGet(resultVar)),
throwUninitializedPropertyAccessException(backingField.name)
)
}
}
}
})
}
private fun IrBuilderWithScope.throwUninitializedPropertyAccessException(name: Name) =
irCall(throwErrorFunction, context.irBuiltIns.nothingType).apply {
if (generateParameterNameInAssertion) {
putValueArgument(
0,
IrConstImpl.string(
startOffset,
endOffset,
context.irBuiltIns.stringType,
name.asString()
)
)
}
}
private val throwErrorFunction = context.ir.symbols.ThrowUninitializedPropertyAccessException
}
@@ -50,8 +50,8 @@ fun ThrowNoWhenBranchMatchedException(): Nothing {
throw NoWhenBranchMatchedException()
}
fun ThrowUninitializedPropertyAccessException(): Nothing {
throw UninitializedPropertyAccessException()
fun ThrowUninitializedPropertyAccessException(propertyName: String): Nothing {
throw UninitializedPropertyAccessException("lateinit property $propertyName has not been initialized")
}
@ExportForCppRuntime