diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LateinitLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LateinitLowering.kt index 81a0e1d93061b..5260f76304a4a 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LateinitLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/LateinitLowering.kt @@ -36,144 +36,142 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid @PhaseDescription( name = "LateinitLowering", ) -class LateinitLowering(private val context: CommonBackendContext) : FileLoweringPass { +open class LateinitLowering(private val backendContext: CommonBackendContext) : FileLoweringPass, IrElementTransformerVoid() { override fun lower(irFile: IrFile) { - irFile.transformChildrenVoid(Transformer(context)) + irFile.transformChildrenVoid(this) } fun lower(irBody: IrBody) { - irBody.transformChildrenVoid(Transformer(context)) + irBody.transformChildrenVoid(this) } - private class Transformer(private val backendContext: CommonBackendContext) : IrElementTransformerVoid() { - override fun visitProperty(declaration: IrProperty): IrStatement { - if (declaration.isRealLateinit()) { - val backingField = declaration.backingField!! - transformBackingField(backingField, declaration) + override fun visitProperty(declaration: IrProperty): IrStatement { + if (declaration.isRealLateinit()) { + val backingField = declaration.backingField!! + transformLateinitBackingField(backingField, declaration) - declaration.getter?.let { - transformGetter(backingField, it) - } + declaration.getter?.let { + transformGetter(backingField, it) } - - declaration.transformChildrenVoid() - return declaration } - override fun visitVariable(declaration: IrVariable): IrStatement { - declaration.transformChildrenVoid() + declaration.transformChildrenVoid() + return declaration + } - if (declaration.isLateinit) { - declaration.type = declaration.type.makeNullable() - declaration.isVar = true - declaration.initializer = - IrConstImpl.constNull(declaration.startOffset, declaration.endOffset, backendContext.irBuiltIns.nothingNType) - } + override fun visitVariable(declaration: IrVariable): IrStatement { + declaration.transformChildrenVoid() - return declaration + if (declaration.isLateinit) { + declaration.type = declaration.type.makeNullable() + declaration.isVar = true + declaration.initializer = + IrConstImpl.constNull(declaration.startOffset, declaration.endOffset, backendContext.irBuiltIns.nothingNType) } - override fun visitGetValue(expression: IrGetValue): IrExpression { - expression.transformChildrenVoid() + return declaration + } - val irValue = expression.symbol.owner - if (irValue !is IrVariable || !irValue.isLateinit) { - return expression - } + override fun visitGetValue(expression: IrGetValue): IrExpression { + expression.transformChildrenVoid() - return backendContext.createIrBuilder( - (irValue.parent as IrSymbolOwner).symbol, - expression.startOffset, - expression.endOffset - ).run { - irIfThenElse( - expression.type, - irEqualsNull(irGet(irValue)), - backendContext.throwUninitializedPropertyAccessException(this, irValue.name.asString()), - irGet(irValue) - ) - } + val irValue = expression.symbol.owner + if (irValue !is IrVariable || !irValue.isLateinit) { + return expression } - override fun visitGetField(expression: IrGetField): IrExpression { - expression.transformChildrenVoid() - - val irField = expression.symbol.owner - if (irField.isLateinitBackingField()) { - expression.type = expression.type.makeNullable() - } - return expression + return backendContext.createIrBuilder( + (irValue.parent as IrSymbolOwner).symbol, + expression.startOffset, + expression.endOffset + ).run { + irIfThenElse( + expression.type, + irEqualsNull(irGet(irValue)), + backendContext.throwUninitializedPropertyAccessException(this, irValue.name.asString()), + irGet(irValue) + ) } + } - private fun IrField.isLateinitBackingField(): Boolean { - val property = this.correspondingPropertySymbol?.owner - return property != null && property.isRealLateinit() + override fun visitGetField(expression: IrGetField): IrExpression { + expression.transformChildrenVoid() + + val irField = expression.symbol.owner + if (irField.isLateinitBackingField()) { + expression.type = expression.type.makeNullable() } + return expression + } + + private fun IrField.isLateinitBackingField(): Boolean { + val property = this.correspondingPropertySymbol?.owner + return property != null && property.isRealLateinit() + } + + private fun IrProperty.isRealLateinit() = + isLateinit && !isFakeOverride + + override fun visitCall(expression: IrCall): IrExpression { + expression.transformChildrenVoid() + + if (!Symbols.isLateinitIsInitializedPropertyGetter(expression.symbol)) return expression - private fun IrProperty.isRealLateinit() = - isLateinit && !isFakeOverride - - override fun visitCall(expression: IrCall): IrExpression { - expression.transformChildrenVoid() - - if (!Symbols.isLateinitIsInitializedPropertyGetter(expression.symbol)) return expression - - return expression.extensionReceiver!!.replaceTailExpression { - val irPropertyRef = it as? IrPropertyReference - ?: throw AssertionError("Property reference expected: ${it.render()}") - val property = irPropertyRef.getter?.owner?.resolveFakeOverride()?.correspondingPropertySymbol?.owner - ?: throw AssertionError("isInitialized cannot be invoked on ${it.render()}") - require(property.isLateinit) { - "isInitialized invoked on non-lateinit property ${property.render()}" - } - val backingField = property.backingField - ?: throw AssertionError("Lateinit property is supposed to have a backing field") - transformBackingField(backingField, property) - backendContext.createIrBuilder(it.symbol, expression.startOffset, expression.endOffset).run { - irNotEquals( - irGetField(it.dispatchReceiver, backingField), - irNull() - ) - } + return expression.extensionReceiver!!.replaceTailExpression { + val irPropertyRef = it as? IrPropertyReference + ?: throw AssertionError("Property reference expected: ${it.render()}") + val property = irPropertyRef.getter?.owner?.resolveFakeOverride()?.correspondingPropertySymbol?.owner + ?: throw AssertionError("isInitialized cannot be invoked on ${it.render()}") + require(property.isLateinit) { + "isInitialized invoked on non-lateinit property ${property.render()}" + } + val backingField = property.backingField + ?: throw AssertionError("Lateinit property is supposed to have a backing field") + transformLateinitBackingField(backingField, property) + backendContext.createIrBuilder(it.symbol, expression.startOffset, expression.endOffset).run { + irNotEquals( + irGetField(it.dispatchReceiver, backingField), + irNull() + ) } } + } - private fun transformBackingField(backingField: IrField, property: IrProperty) { - assert(backingField.initializer == null) { - "lateinit property backing field should not have an initializer:\n${property.dump()}" - } - backingField.type = backingField.type.makeNullable() + protected open fun transformLateinitBackingField(backingField: IrField, property: IrProperty) { + assert(backingField.initializer == null) { + "lateinit property backing field should not have an initializer:\n${property.dump()}" } + backingField.type = backingField.type.makeNullable() + } - private fun transformGetter(backingField: IrField, getter: IrFunction) { - val type = backingField.type - assert(!type.isPrimitiveType()) { - "'lateinit' property type should not be primitive:\n${backingField.dump()}" - } - val startOffset = getter.startOffset - val endOffset = getter.endOffset - getter.body = backendContext.irFactory.createBlockBody(startOffset, endOffset) { - val irBuilder = backendContext.createIrBuilder(getter.symbol, startOffset, endOffset) - irBuilder.run { - val resultVar = scope.createTmpVariable( - irGetField(getter.dispatchReceiverParameter?.let { irGet(it) }, backingField, type) - ) - resultVar.parent = getter - statements.add(resultVar) - val throwIfNull = irIfThenElse( - context.irBuiltIns.nothingType, - irNotEquals(irGet(resultVar), irNull()), - irReturn(irGet(resultVar)), - throwUninitializedPropertyAccessException(backingField.name.asString()) - ) - statements.add(throwIfNull) - } + private fun transformGetter(backingField: IrField, getter: IrFunction) { + val type = backingField.type + assert(!type.isPrimitiveType()) { + "'lateinit' property type should not be primitive:\n${backingField.dump()}" + } + val startOffset = getter.startOffset + val endOffset = getter.endOffset + getter.body = backendContext.irFactory.createBlockBody(startOffset, endOffset) { + val irBuilder = backendContext.createIrBuilder(getter.symbol, startOffset, endOffset) + irBuilder.run { + val resultVar = scope.createTmpVariable( + irGetField(getter.dispatchReceiverParameter?.let { irGet(it) }, backingField, type) + ) + resultVar.parent = getter + statements.add(resultVar) + val throwIfNull = irIfThenElse( + context.irBuiltIns.nothingType, + irNotEquals(irGet(resultVar), irNull()), + irReturn(irGet(resultVar)), + throwUninitializedPropertyAccessException(backingField.name.asString()) + ) + statements.add(throwIfNull) } } - - private fun IrBuilderWithScope.throwUninitializedPropertyAccessException(name: String) = - backendContext.throwUninitializedPropertyAccessException(this, name) } + + private fun IrBuilderWithScope.throwUninitializedPropertyAccessException(name: String) = + backendContext.throwUninitializedPropertyAccessException(this, name) } private inline fun IrExpression.replaceTailExpression(crossinline transform: (IrExpression) -> IrExpression): IrExpression { diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLoweringPhases.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLoweringPhases.kt index 361263925d798..9a42957d909d8 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLoweringPhases.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/JvmLoweringPhases.kt @@ -22,7 +22,7 @@ private val jvmFilePhases = createFilePhases( ::PolymorphicSignatureLowering, ::VarargLowering, - ::LateinitLowering, + ::JvmLateinitLowering, ::JvmInventNamesForLocalClasses, ::JvmInlineCallableReferenceToLambdaPhase, diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmLateinitLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmLateinitLowering.kt new file mode 100644 index 0000000000000..14fdd29881dea --- /dev/null +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/JvmLateinitLowering.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2010-2024 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.backend.jvm.lower + +import org.jetbrains.kotlin.backend.common.CommonBackendContext +import org.jetbrains.kotlin.backend.common.lower.LateinitLowering +import org.jetbrains.kotlin.backend.common.phaser.PhaseDescription +import org.jetbrains.kotlin.ir.declarations.IrField +import org.jetbrains.kotlin.ir.declarations.IrProperty + +@PhaseDescription(name = "JvmLateinitLowering") +class JvmLateinitLowering(context: CommonBackendContext) : LateinitLowering(context) { + override fun transformLateinitBackingField(backingField: IrField, property: IrProperty) { + super.transformLateinitBackingField(backingField, property) + backingField.visibility = property.setter?.visibility ?: property.visibility + } +}