From 78b29349c998070c37cc8c364e9a235fe6210bb7 Mon Sep 17 00:00:00 2001 From: Roman Artemev Date: Mon, 28 Oct 2019 16:38:50 +0300 Subject: [PATCH] Fix lateinit `isInitialized` check in multi-file case --- .../kotlin/backend/common/BackendContext.kt | 2 + .../backend/common/lower/LateinitLowering.kt | 36 ++++++++++----- .../ir/backend/js/JsIrBackendContext.kt | 1 + .../kotlin/backend/jvm/JvmBackendContext.kt | 1 + .../kotlin/backend/wasm/WasmBackendContext.kt | 1 + .../isInitializedMultiFile.kt | 46 +++++++++++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 5 ++ .../LightAnalysisModeTestGenerated.java | 5 ++ .../ir/IrBlackBoxCodegenTestGenerated.java | 5 ++ .../IrJsCodegenBoxTestGenerated.java | 5 ++ .../semantics/JsCodegenBoxTestGenerated.java | 5 ++ 11 files changed, 100 insertions(+), 12 deletions(-) create mode 100644 compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/isInitializedMultiFile.kt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/BackendContext.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/BackendContext.kt index 2101abe71bb..7f9f8433f8c 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/BackendContext.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/BackendContext.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.backend.common.ir.DeclarationFactory import org.jetbrains.kotlin.backend.common.ir.Ir import org.jetbrains.kotlin.backend.common.ir.SharedVariablesManager import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.ir.declarations.IrField import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol @@ -33,4 +34,5 @@ interface BackendContext { val declarationFactory: DeclarationFactory val internalPackageFqn: FqName val transformedFunction: MutableMap + val lateinitNullableFields: MutableMap } 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 70cbe59a497..780d131e152 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 @@ -29,6 +29,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.* import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl +import org.jetbrains.kotlin.ir.types.isMarkedNullable import org.jetbrains.kotlin.ir.types.isPrimitiveType import org.jetbrains.kotlin.ir.types.makeNullable import org.jetbrains.kotlin.ir.util.resolveFakeOverride @@ -36,8 +37,24 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid class LateinitLowering(val context: CommonBackendContext) : FileLoweringPass { + + private val nullableFields = context.lateinitNullableFields + private fun buildOrGetNullableField(originalField: IrField): IrField { + if (originalField.type.isMarkedNullable()) return originalField + return nullableFields.getOrPut(originalField) { + buildField { + updateFrom(originalField) + type = originalField.type.makeNullable() + name = originalField.name + }.apply { + parent = originalField.parent + correspondingPropertySymbol = originalField.correspondingPropertySymbol + } + } + } + override fun lower(irFile: IrFile) { - val nullableFields = mutableMapOf() + val nullableVariables = mutableMapOf() // Transform declarations @@ -46,17 +63,9 @@ class LateinitLowering(val context: CommonBackendContext) : FileLoweringPass { declaration.transformChildrenVoid(this) if (declaration.isLateinit && declaration.origin != IrDeclarationOrigin.FAKE_OVERRIDE) { val oldField = declaration.backingField!! - val newField = buildField { - updateFrom(oldField) - type = oldField.type.makeNullable() - name = oldField.name - }.also { newField -> - newField.parent = oldField.parent - newField.correspondingPropertySymbol = declaration.symbol - declaration.backingField = newField - } + val newField = buildOrGetNullableField(oldField) - nullableFields[oldField] = newField + declaration.backingField = newField transformGetter(newField, declaration.getter!!) } @@ -166,8 +175,11 @@ class LateinitLowering(val context: CommonBackendContext) : FileLoweringPass { val property = receiver.getter?.owner?.resolveFakeOverride()?.correspondingProperty!!.also { assert(it.isLateinit) } + val nullableField = + buildOrGetNullableField(property.backingField ?: error("Lateinit property is supposed to have backing field")) + return expression.run { context.createIrBuilder(symbol, startOffset, endOffset) }.run { - irNotEquals(irGetField(receiver.dispatchReceiver, property.backingField!!), irNull()) + irNotEquals(irGetField(receiver.dispatchReceiver, nullableField), irNull()) } } }) diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt index fa6d51b1fa2..2ff3b3832ec 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIrBackendContext.kt @@ -48,6 +48,7 @@ class JsIrBackendContext( override val scriptMode: Boolean = false ) : CommonBackendContext { override val transformedFunction = mutableMapOf() + override val lateinitNullableFields = mutableMapOf() override val builtIns = module.builtIns 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 a190eb4b39d..78fe33f2065 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 @@ -53,6 +53,7 @@ class JvmBackendContext( override val transformedFunction: MutableMap get() = TODO("not implemented") override val scriptMode: Boolean = false + override val lateinitNullableFields = mutableMapOf() override val builtIns = state.module.builtIns val typeMapper = IrTypeMapper(this) diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmBackendContext.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmBackendContext.kt index ddadee8584c..f04b7688485 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmBackendContext.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmBackendContext.kt @@ -38,6 +38,7 @@ class WasmBackendContext( override var inVerbosePhase: Boolean = false override val scriptMode = false override val transformedFunction = mutableMapOf() + override val lateinitNullableFields = mutableMapOf() // Place to store declarations excluded from code generation val excludedDeclarations: IrPackageFragment by lazy { diff --git a/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/isInitializedMultiFile.kt b/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/isInitializedMultiFile.kt new file mode 100644 index 00000000000..4628793268a --- /dev/null +++ b/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/isInitializedMultiFile.kt @@ -0,0 +1,46 @@ +// IGNORE_BACKEND: JVM, JVM_IR +// FILE: A.kt + +fun test1F(o: Foo) = if (!o::bar.isInitialized) "1F" else "Fail1F" +fun test1T(o: Foo) = if (o::bar.isInitialized) "1T" else "Fail1T" + +// FILE: B.kt + +fun test2F(o: Foo) = if (!o::bar.isInitialized) "2F" else "Fail2F" +fun test2T(o: Foo) = if (o::bar.isInitialized) "2T" else "Fail2T" + +class Foo { + lateinit var bar: String + + fun testF() = if (!this::bar.isInitialized) "0F" else "Fail0F" + fun testT() = if (this::bar.isInitialized) "0T" else "Fail0T" +} + + +fun test3F(o: Foo) = if (!o::bar.isInitialized) "3F" else "Fail3F" +fun test3T(o: Foo) = if (o::bar.isInitialized) "3T" else "Fail3T" + +// FILE: C.kt + +fun test4F(o: Foo) = if (!o::bar.isInitialized) "4F" else "Fail4F" +fun test4T(o: Foo) = if (o::bar.isInitialized) "4T" else "Fail4T" + + +fun box(): String { + val o = Foo() + if (o.testF() != "0F") return "Fail0F" + if (test1F(o) != "1F") return "Fail1F" + if (test2F(o) != "2F") return "Fail2F" + if (test3F(o) != "3F") return "Fail3F" + if (test4F(o) != "4F") return "Fail4F" + + o.bar = "OK" + + if (o.testT() != "0T") return "Fail0T" + if (test1T(o) != "1T") return "Fail1T" + if (test2T(o) != "2T") return "Fail2T" + if (test3T(o) != "3T") return "Fail3T" + if (test4T(o) != "4T") return "Fail4T" + + return o.bar +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 7f4918c805e..da15db896a7 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -18912,6 +18912,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/innerSubclass.kt"); } + @TestMetadata("isInitializedMultiFile.kt") + public void testIsInitializedMultiFile() throws Exception { + runTest("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/isInitializedMultiFile.kt"); + } + @TestMetadata("nonInlineLambda.kt") public void testNonInlineLambda() throws Exception { runTest("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/nonInlineLambda.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index a2f5bcf5380..8df402e74e1 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -18912,6 +18912,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/innerSubclass.kt"); } + @TestMetadata("isInitializedMultiFile.kt") + public void testIsInitializedMultiFile() throws Exception { + runTest("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/isInitializedMultiFile.kt"); + } + @TestMetadata("nonInlineLambda.kt") public void testNonInlineLambda() throws Exception { runTest("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/nonInlineLambda.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 73873d9157e..787b47ab3c0 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -17731,6 +17731,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/innerSubclass.kt"); } + @TestMetadata("isInitializedMultiFile.kt") + public void testIsInitializedMultiFile() throws Exception { + runTest("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/isInitializedMultiFile.kt"); + } + @TestMetadata("nonInlineLambda.kt") public void testNonInlineLambda() throws Exception { runTest("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/nonInlineLambda.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 e16836089a7..e667e1043ca 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 @@ -14687,6 +14687,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTest("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/innerSubclass.kt"); } + @TestMetadata("isInitializedMultiFile.kt") + public void testIsInitializedMultiFile() throws Exception { + runTest("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/isInitializedMultiFile.kt"); + } + @TestMetadata("nonInlineLambda.kt") public void testNonInlineLambda() throws Exception { runTest("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/nonInlineLambda.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 fcf0450d1b0..ce603614a96 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 @@ -15842,6 +15842,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTest("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/innerSubclass.kt"); } + @TestMetadata("isInitializedMultiFile.kt") + public void testIsInitializedMultiFile() throws Exception { + runTest("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/isInitializedMultiFile.kt"); + } + @TestMetadata("nonInlineLambda.kt") public void testNonInlineLambda() throws Exception { runTest("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/nonInlineLambda.kt");