[IR BE] Make lateinit fields and variables nullable
This commit is contained in:
+98
-39
@@ -18,29 +18,46 @@ package org.jetbrains.kotlin.backend.common.lower
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||||
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
import org.jetbrains.kotlin.backend.common.FileLoweringPass
|
||||||
|
import org.jetbrains.kotlin.backend.common.descriptors.WrappedVariableDescriptor
|
||||||
import org.jetbrains.kotlin.backend.common.ir.Symbols
|
import org.jetbrains.kotlin.backend.common.ir.Symbols
|
||||||
import org.jetbrains.kotlin.ir.IrStatement
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||||
import org.jetbrains.kotlin.ir.builders.*
|
import org.jetbrains.kotlin.ir.builders.*
|
||||||
|
import org.jetbrains.kotlin.ir.builders.declarations.buildField
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrGetValue
|
import org.jetbrains.kotlin.ir.expressions.impl.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrPropertyReference
|
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
|
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
|
||||||
import org.jetbrains.kotlin.ir.types.isPrimitiveType
|
import org.jetbrains.kotlin.ir.types.isPrimitiveType
|
||||||
|
import org.jetbrains.kotlin.ir.types.makeNullable
|
||||||
import org.jetbrains.kotlin.ir.util.resolveFakeOverride
|
import org.jetbrains.kotlin.ir.util.resolveFakeOverride
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
|
|
||||||
class LateinitLowering(val context: CommonBackendContext) : FileLoweringPass {
|
class LateinitLowering(val context: CommonBackendContext) : FileLoweringPass {
|
||||||
override fun lower(irFile: IrFile) {
|
override fun lower(irFile: IrFile) {
|
||||||
|
val nullableFields = mutableMapOf<IrField, IrField>()
|
||||||
|
val nullableVariables = mutableMapOf<IrVariable, IrVariable>()
|
||||||
|
|
||||||
|
// Transform declarations
|
||||||
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
|
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||||
override fun visitProperty(declaration: IrProperty): IrStatement {
|
override fun visitProperty(declaration: IrProperty): IrStatement {
|
||||||
declaration.transformChildrenVoid(this)
|
declaration.transformChildrenVoid(this)
|
||||||
if (declaration.isLateinit && declaration.origin != IrDeclarationOrigin.FAKE_OVERRIDE) {
|
if (declaration.isLateinit && declaration.origin != IrDeclarationOrigin.FAKE_OVERRIDE) {
|
||||||
transformGetter(declaration.backingField!!, declaration.getter!!)
|
val oldField = declaration.backingField!!
|
||||||
|
val newField = buildField {
|
||||||
|
updateFrom(oldField)
|
||||||
|
type = oldField.type.makeNullable()
|
||||||
|
name = oldField.name
|
||||||
|
}.also { newField ->
|
||||||
|
newField.parent = oldField.parent
|
||||||
|
declaration.backingField = newField
|
||||||
|
}
|
||||||
|
|
||||||
|
nullableFields[oldField] = newField
|
||||||
|
|
||||||
|
transformGetter(newField, declaration.getter!!)
|
||||||
}
|
}
|
||||||
return declaration
|
return declaration
|
||||||
}
|
}
|
||||||
@@ -50,41 +67,27 @@ class LateinitLowering(val context: CommonBackendContext) : FileLoweringPass {
|
|||||||
|
|
||||||
if (!declaration.isLateinit) return declaration
|
if (!declaration.isLateinit) return declaration
|
||||||
|
|
||||||
declaration.run { initializer = IrConstImpl.constNull(startOffset, endOffset, type) }
|
val descriptor = WrappedVariableDescriptor()
|
||||||
|
val type = declaration.type.makeNullable()
|
||||||
return declaration
|
val newVar = IrVariableImpl(
|
||||||
}
|
declaration.startOffset,
|
||||||
|
declaration.endOffset,
|
||||||
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
declaration.origin,
|
||||||
val irVar = expression.symbol.owner as? IrVariable ?: return expression
|
IrVariableSymbolImpl(descriptor),
|
||||||
|
declaration.name,
|
||||||
if (!irVar.isLateinit) return expression
|
type,
|
||||||
|
true,
|
||||||
val parent = irVar.parent as IrSymbolOwner
|
false,
|
||||||
|
true
|
||||||
val irBuilder = context.createIrBuilder(parent.symbol, expression.startOffset, expression.endOffset)
|
).also {
|
||||||
|
descriptor.bind(it)
|
||||||
return irBuilder.run {
|
it.parent = declaration.parent
|
||||||
irIfThenElse(
|
it.initializer = IrConstImpl.constNull(declaration.startOffset, declaration.endOffset, type)
|
||||||
expression.type, irEqualsNull(irGet(irVar)),
|
|
||||||
throwUninitializedPropertyAccessException(irVar.name.asString()),
|
|
||||||
irGet(irVar)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override fun visitCall(expression: IrCall): IrExpression {
|
nullableVariables[declaration] = newVar
|
||||||
expression.transformChildrenVoid(this)
|
|
||||||
|
|
||||||
if (!Symbols.isLateinitIsInitializedPropertyGetter(expression.symbol)) return expression
|
return newVar
|
||||||
|
|
||||||
val receiver = expression.extensionReceiver as IrPropertyReference
|
|
||||||
|
|
||||||
val property = receiver.getter?.owner?.resolveFakeOverride()?.correspondingProperty!!.also { assert(it.isLateinit) }
|
|
||||||
|
|
||||||
return expression.run { context.createIrBuilder(symbol, startOffset, endOffset) }.run {
|
|
||||||
irNotEquals(irGetField(receiver.dispatchReceiver, property.backingField!!), irNull())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun transformGetter(backingField: IrField, getter: IrFunction) {
|
private fun transformGetter(backingField: IrField, getter: IrFunction) {
|
||||||
@@ -111,6 +114,62 @@ class LateinitLowering(val context: CommonBackendContext) : FileLoweringPass {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Transform usages
|
||||||
|
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||||
|
override fun visitGetValue(expression: IrGetValue): IrExpression {
|
||||||
|
val irVar = nullableVariables[expression.symbol.owner] ?: return expression
|
||||||
|
|
||||||
|
val parent = irVar.parent as IrSymbolOwner
|
||||||
|
val irBuilder = context.createIrBuilder(parent.symbol, expression.startOffset, expression.endOffset)
|
||||||
|
|
||||||
|
return irBuilder.run {
|
||||||
|
irIfThenElse(
|
||||||
|
expression.type, irEqualsNull(irGet(irVar)),
|
||||||
|
throwUninitializedPropertyAccessException(irVar.name.asString()),
|
||||||
|
irGet(irVar)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitSetVariable(expression: IrSetVariable): IrExpression {
|
||||||
|
expression.transformChildrenVoid(this)
|
||||||
|
val newVar = nullableVariables[expression.symbol.owner] ?: return expression
|
||||||
|
return with(expression) {
|
||||||
|
IrSetVariableImpl(startOffset, endOffset, type, newVar.symbol, value, origin)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitGetField(expression: IrGetField): IrExpression {
|
||||||
|
expression.transformChildrenVoid(this)
|
||||||
|
val newField = nullableFields[expression.symbol.owner] ?: return expression
|
||||||
|
return with(expression) {
|
||||||
|
IrGetFieldImpl(startOffset, endOffset, newField.symbol, newField.type, receiver, origin, superQualifierSymbol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitSetField(expression: IrSetField): IrExpression {
|
||||||
|
expression.transformChildrenVoid(this)
|
||||||
|
val newField = nullableFields[expression.symbol.owner] ?: return expression
|
||||||
|
return with(expression) {
|
||||||
|
IrSetFieldImpl(startOffset, endOffset, newField.symbol, receiver, value, type, origin, superQualifierSymbol)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitCall(expression: IrCall): IrExpression {
|
||||||
|
expression.transformChildrenVoid(this)
|
||||||
|
|
||||||
|
if (!Symbols.isLateinitIsInitializedPropertyGetter(expression.symbol)) return expression
|
||||||
|
|
||||||
|
val receiver = expression.extensionReceiver as IrPropertyReference
|
||||||
|
|
||||||
|
val property = receiver.getter?.owner?.resolveFakeOverride()?.correspondingProperty!!.also { assert(it.isLateinit) }
|
||||||
|
|
||||||
|
return expression.run { context.createIrBuilder(symbol, startOffset, endOffset) }.run {
|
||||||
|
irNotEquals(irGetField(receiver.dispatchReceiver, property.backingField!!), irNull())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun IrBuilderWithScope.throwUninitializedPropertyAccessException(name: String) =
|
private fun IrBuilderWithScope.throwUninitializedPropertyAccessException(name: String) =
|
||||||
|
|||||||
Reference in New Issue
Block a user