diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CommonBackendContext.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CommonBackendContext.kt index fb58f30d888..62f7114cbe4 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CommonBackendContext.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/CommonBackendContext.kt @@ -7,10 +7,12 @@ package org.jetbrains.kotlin.backend.common import org.jetbrains.kotlin.backend.common.ir.Ir import org.jetbrains.kotlin.config.CompilerConfiguration -import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope +import org.jetbrains.kotlin.ir.builders.irCall +import org.jetbrains.kotlin.ir.builders.irString import org.jetbrains.kotlin.ir.declarations.IrFile -import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.ir.expressions.IrExpression interface LoggingContext { var inVerbosePhase: Boolean @@ -24,4 +26,11 @@ interface CommonBackendContext : BackendContext, LoggingContext { val configuration: CompilerConfiguration val scriptMode: Boolean + + fun throwUninitializedPropertyAccessException(builder: IrBuilderWithScope, name: String): IrExpression { + val throwErrorFunction = ir.symbols.ThrowUninitializedPropertyAccessException.owner + return builder.irCall(throwErrorFunction).apply { + putValueArgument(0, builder.irString(name)) + } + } } 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 56deb9464f7..4d8ee688d15 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 @@ -35,9 +35,9 @@ import org.jetbrains.kotlin.ir.util.resolveFakeOverride import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid -open class LateinitLowering(val context: CommonBackendContext) : FileLoweringPass { +class LateinitLowering(val backendContext: CommonBackendContext) : FileLoweringPass { - private val nullableFields = context.lateinitNullableFields + private val nullableFields = backendContext.lateinitNullableFields private fun buildOrGetNullableField(originalField: IrField): IrField { if (originalField.type.isMarkedNullable()) return originalField return nullableFields.getOrPut(originalField) { @@ -92,7 +92,7 @@ open class LateinitLowering(val context: CommonBackendContext) : FileLoweringPas ).also { descriptor.bind(it) it.parent = declaration.parent - it.initializer = IrConstImpl.constNull(declaration.startOffset, declaration.endOffset, context.irBuiltIns.nothingNType) + it.initializer = IrConstImpl.constNull(declaration.startOffset, declaration.endOffset, backendContext.irBuiltIns.nothingNType) } nullableVariables[declaration] = newVar @@ -105,7 +105,7 @@ open class LateinitLowering(val context: CommonBackendContext) : FileLoweringPas assert(!type.isPrimitiveType()) { "'lateinit' modifier is not allowed on primitive types" } val startOffset = getter.startOffset val endOffset = getter.endOffset - val irBuilder = context.createIrBuilder(getter.symbol, startOffset, endOffset) + val irBuilder = backendContext.createIrBuilder(getter.symbol, startOffset, endOffset) irBuilder.run { val body = IrBlockBodyImpl(startOffset, endOffset) val resultVar = scope.createTemporaryVariable( @@ -117,7 +117,7 @@ open class LateinitLowering(val context: CommonBackendContext) : FileLoweringPas context.irBuiltIns.nothingType, irNotEquals(irGet(resultVar), irNull()), irReturn(irGet(resultVar)), - throwUninitializedPropertyAccessException(this, backingField.name.asString()) + backendContext.throwUninitializedPropertyAccessException(this, backingField.name.asString()) ) body.statements.add(throwIfNull) getter.body = body @@ -131,12 +131,12 @@ open class LateinitLowering(val context: CommonBackendContext) : FileLoweringPas val irVar = nullableVariables[expression.symbol.owner] ?: return expression val parent = irVar.parent as IrSymbolOwner - val irBuilder = context.createIrBuilder(parent.symbol, expression.startOffset, expression.endOffset) + val irBuilder = backendContext.createIrBuilder(parent.symbol, expression.startOffset, expression.endOffset) return irBuilder.run { irIfThenElse( expression.type, irEqualsNull(irGet(irVar)), - throwUninitializedPropertyAccessException(this, irVar.name.asString()), + backendContext.throwUninitializedPropertyAccessException(this, irVar.name.asString()), irGet(irVar) ) } @@ -179,17 +179,10 @@ open class LateinitLowering(val context: CommonBackendContext) : FileLoweringPas val nullableField = buildOrGetNullableField(property.backingField ?: error("Lateinit property is supposed to have backing field")) - return expression.run { context.createIrBuilder(symbol, startOffset, endOffset) }.run { + return expression.run { backendContext.createIrBuilder(symbol, startOffset, endOffset) }.run { irNotEquals(irGetField(receiver.dispatchReceiver, nullableField), irNull()) } } }) } - - open fun throwUninitializedPropertyAccessException(builder: IrBuilderWithScope, name: String): IrExpression = - builder.irCall(throwErrorFunction).apply { - putValueArgument(0, builder.irString(name)) - } - - private val throwErrorFunction by lazy { context.ir.symbols.ThrowUninitializedPropertyAccessException.owner } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt index 824c602554a..630c8c67552 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.jvm import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.ir.Ir +import org.jetbrains.kotlin.backend.common.lower.irThrow import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig import org.jetbrains.kotlin.backend.jvm.codegen.IrTypeMapper import org.jetbrains.kotlin.backend.jvm.codegen.MethodSignatureMapper @@ -24,6 +25,9 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope +import org.jetbrains.kotlin.ir.builders.irBlock +import org.jetbrains.kotlin.ir.builders.irNull import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.expressions.IrExpression @@ -136,6 +140,12 @@ class JvmBackendContext( print(message) } + override fun throwUninitializedPropertyAccessException(builder: IrBuilderWithScope, name: String): IrExpression = + builder.irBlock { + +super.throwUninitializedPropertyAccessException(builder, name) + +irThrow(irNull()) + } + inner class JvmIr( irModuleFragment: IrModuleFragment, symbolTable: ReferenceSymbolTable diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 28ee3d65dbf..0dc76f90fb4 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -14,11 +14,7 @@ import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibility import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope -import org.jetbrains.kotlin.ir.builders.irBlock -import org.jetbrains.kotlin.ir.builders.irNull import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.util.PatchDeclarationParentsVisitor import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid @@ -82,15 +78,7 @@ private val expectDeclarationsRemovingPhase = makeIrModulePhase( ) private val lateinitPhase = makeIrFilePhase( - { backend: JvmBackendContext -> - object : LateinitLowering(backend) { - override fun throwUninitializedPropertyAccessException(builder: IrBuilderWithScope, name: String): IrExpression = - builder.irBlock { - +super.throwUninitializedPropertyAccessException(builder, name) - +irThrow(irNull()) - } - } - }, + ::LateinitLowering, name = "Lateinit", description = "Insert checks for lateinit field references" ) diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertiesToFieldsLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertiesToFieldsLowering.kt index 71ae531c23e..e39973e86c0 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertiesToFieldsLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/PropertiesToFieldsLowering.kt @@ -13,12 +13,14 @@ import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Visibilities 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.IrFieldAccessExpression import org.jetbrains.kotlin.ir.expressions.IrTypeOperator import org.jetbrains.kotlin.ir.expressions.impl.* +import org.jetbrains.kotlin.ir.types.makeNotNull import org.jetbrains.kotlin.ir.util.hasAnnotation import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid @@ -63,9 +65,6 @@ class PropertiesToFieldsLowering(val context: CommonBackendContext) : IrElementT private fun shouldSubstituteAccessorWithField(property: IrProperty, accessor: IrSimpleFunction?): Boolean { if (accessor == null) return false - // In contrast to the old backend, we do generate getters for lateinit properties, which fixes KT-28331 - if (property.isLateinit) return false - if ((property.parent as? IrClass)?.kind == ClassKind.ANNOTATION_CLASS) return false if (property.backingField?.hasAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME) == true) return true @@ -96,12 +95,34 @@ class PropertiesToFieldsLowering(val context: CommonBackendContext) : IrElementT expression.startOffset, expression.endOffset, backingField.symbol, - expression.type, + backingField.type, receiver, expression.origin, expression.superQualifierSymbol ) - return buildSubstitution(backingField.isStatic, getExpr, receiver) + val substitution = buildSubstitution(backingField.isStatic, getExpr, receiver) + return if (irProperty.isLateinit) + insertLateinitCheck(substitution, backingField) + else + substitution + } + + private fun insertLateinitCheck(expression: IrExpression, field: IrField): IrExpression { + val backendContext = context + val startOffset = expression.startOffset + val endOffset = expression.endOffset + val irBuilder = context.createIrBuilder(field.symbol, startOffset, endOffset) + irBuilder.run { + return irBlock(expression) { + val tmpVar = irTemporaryVar(expression) + +irIfThenElse( + expression.type.makeNotNull(), + irEqualsNull(irGet(tmpVar)), + backendContext.throwUninitializedPropertyAccessException(this, field.name.asString()), + irGet(expression.type.makeNotNull(), tmpVar.symbol) + ) + } + } } private fun buildSubstitution(needBlock: Boolean, setOrGetExpr: IrFieldAccessExpression, receiver: IrExpression?): IrExpression { diff --git a/compiler/testData/codegen/box/annotations/annotationsOnLateinitAccessors.kt b/compiler/testData/codegen/box/annotations/annotationsOnLateinitAccessors.kt index 5674371b92f..b47fc4ff259 100644 --- a/compiler/testData/codegen/box/annotations/annotationsOnLateinitAccessors.kt +++ b/compiler/testData/codegen/box/annotations/annotationsOnLateinitAccessors.kt @@ -1,6 +1,5 @@ // IGNORE_BACKEND_FIR: JVM_IR // WITH_REFLECT -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // Please make sure that this test is consistent with the diagnostic test "annotationsTargetingLateinitAccessor.kt" diff --git a/compiler/testData/codegen/box/properties/lateinit/nameClash.kt b/compiler/testData/codegen/box/properties/lateinit/nameClash.kt new file mode 100644 index 00000000000..dc6aeec23b6 --- /dev/null +++ b/compiler/testData/codegen/box/properties/lateinit/nameClash.kt @@ -0,0 +1,17 @@ +// IGNORE_BACKEND_FIR: JVM_IR + +class SS { + private lateinit var s: String + + fun setS(s: String) { + this.s = s + } + + fun getS() = s +} + +fun box(): String { + val ss = SS() + ss.setS("OK") + return ss.getS() +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 06e57d79069..44edc7d71f3 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -19169,6 +19169,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/properties/lateinit/exceptionGetter.kt"); } + @TestMetadata("nameClash.kt") + public void testNameClash() throws Exception { + runTest("compiler/testData/codegen/box/properties/lateinit/nameClash.kt"); + } + @TestMetadata("override.kt") public void testOverride() throws Exception { runTest("compiler/testData/codegen/box/properties/lateinit/override.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 6d5a897260d..15e4c93635a 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -19169,6 +19169,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/properties/lateinit/exceptionGetter.kt"); } + @TestMetadata("nameClash.kt") + public void testNameClash() throws Exception { + runTest("compiler/testData/codegen/box/properties/lateinit/nameClash.kt"); + } + @TestMetadata("override.kt") public void testOverride() throws Exception { runTest("compiler/testData/codegen/box/properties/lateinit/override.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java index 71f0c466998..67331472b2e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/FirBlackBoxCodegenTestGenerated.java @@ -17704,6 +17704,11 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/properties/lateinit/exceptionGetter.kt"); } + @TestMetadata("nameClash.kt") + public void testNameClash() throws Exception { + runTest("compiler/testData/codegen/box/properties/lateinit/nameClash.kt"); + } + @TestMetadata("override.kt") public void testOverride() throws Exception { runTest("compiler/testData/codegen/box/properties/lateinit/override.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 6a580d2b0a4..33d1a8165dc 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -17704,6 +17704,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/properties/lateinit/exceptionGetter.kt"); } + @TestMetadata("nameClash.kt") + public void testNameClash() throws Exception { + runTest("compiler/testData/codegen/box/properties/lateinit/nameClash.kt"); + } + @TestMetadata("override.kt") public void testOverride() throws Exception { runTest("compiler/testData/codegen/box/properties/lateinit/override.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index 6a87ac9a866..bcee5c1c787 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -14874,6 +14874,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/properties/lateinit/exceptionGetter.kt"); } + @TestMetadata("nameClash.kt") + public void testNameClash() throws Exception { + runTest("compiler/testData/codegen/box/properties/lateinit/nameClash.kt"); + } + @TestMetadata("override.kt") public void testOverride() throws Exception { runTest("compiler/testData/codegen/box/properties/lateinit/override.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 339ba921403..a6a2fea2793 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -16049,6 +16049,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/properties/lateinit/exceptionGetter.kt"); } + @TestMetadata("nameClash.kt") + public void testNameClash() throws Exception { + runTest("compiler/testData/codegen/box/properties/lateinit/nameClash.kt"); + } + @TestMetadata("override.kt") public void testOverride() throws Exception { runTest("compiler/testData/codegen/box/properties/lateinit/override.kt");