Implemented isInitialized for lateinit properties + tests
This commit is contained in:
+4
-3
@@ -73,6 +73,10 @@ internal class KonanLower(val context: Context) {
|
|||||||
irModule.files.forEach(InteropLoweringPart1(context)::lower)
|
irModule.files.forEach(InteropLoweringPart1(context)::lower)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
phaser.phase(KonanPhase.LOWER_LATEINIT) {
|
||||||
|
irModule.files.forEach(LateinitLowering(context)::lower)
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
irModule.replaceUnboundSymbols(context)
|
irModule.replaceUnboundSymbols(context)
|
||||||
validateIrModule(context, irModule)
|
validateIrModule(context, irModule)
|
||||||
@@ -96,9 +100,6 @@ internal class KonanLower(val context: Context) {
|
|||||||
phaser.phase(KonanPhase.LOWER_INITIALIZERS) {
|
phaser.phase(KonanPhase.LOWER_INITIALIZERS) {
|
||||||
InitializersLowering(context).runOnFilePostfix(irFile)
|
InitializersLowering(context).runOnFilePostfix(irFile)
|
||||||
}
|
}
|
||||||
phaser.phase(KonanPhase.LOWER_LATEINIT) {
|
|
||||||
LateinitLowering(context).lower(irFile)
|
|
||||||
}
|
|
||||||
phaser.phase(KonanPhase.LOWER_SHARED_VARIABLES) {
|
phaser.phase(KonanPhase.LOWER_SHARED_VARIABLES) {
|
||||||
SharedVariablesLowering(context).runOnFilePostfix(irFile)
|
SharedVariablesLowering(context).runOnFilePostfix(irFile)
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -41,8 +41,8 @@ enum class KonanPhase(val description: String,
|
|||||||
/* ... ... */ LOWER_ENUMS("Enum classes lowering"),
|
/* ... ... */ LOWER_ENUMS("Enum classes lowering"),
|
||||||
/* ... ... */ LOWER_DELEGATION("Delegation lowering"),
|
/* ... ... */ LOWER_DELEGATION("Delegation lowering"),
|
||||||
/* ... ... */ LOWER_INITIALIZERS("Initializers lowering", LOWER_ENUMS),
|
/* ... ... */ LOWER_INITIALIZERS("Initializers lowering", LOWER_ENUMS),
|
||||||
/* ... ... */ LOWER_LATEINIT("Lateinit properties lowering"),
|
/* ... ... */ LOWER_LATEINIT("Lateinit properties lowering", LOWER_INLINE),
|
||||||
/* ... ... */ LOWER_SHARED_VARIABLES("Shared Variable Lowering", LOWER_INITIALIZERS, LOWER_LATEINIT),
|
/* ... ... */ LOWER_SHARED_VARIABLES("Shared Variable Lowering", LOWER_INITIALIZERS),
|
||||||
/* ... ... */ LOWER_CALLABLES("Callable references Lowering", LOWER_DELEGATION),
|
/* ... ... */ LOWER_CALLABLES("Callable references Lowering", LOWER_DELEGATION),
|
||||||
/* ... ... */ LOWER_LOCAL_FUNCTIONS("Local Function Lowering", LOWER_SHARED_VARIABLES, LOWER_CALLABLES),
|
/* ... ... */ LOWER_LOCAL_FUNCTIONS("Local Function Lowering", LOWER_SHARED_VARIABLES, LOWER_CALLABLES),
|
||||||
/* ... ... */ LOWER_INTEROP_PART2("Interop lowering, part 2", LOWER_LOCAL_FUNCTIONS),
|
/* ... ... */ LOWER_INTEROP_PART2("Interop lowering, part 2", LOWER_LOCAL_FUNCTIONS),
|
||||||
@@ -51,7 +51,7 @@ enum class KonanPhase(val description: String,
|
|||||||
/* ... ... */ LOWER_FINALLY("Finally blocks lowering", LOWER_INITIALIZERS, LOWER_LOCAL_FUNCTIONS, LOWER_TAILREC),
|
/* ... ... */ LOWER_FINALLY("Finally blocks lowering", LOWER_INITIALIZERS, LOWER_LOCAL_FUNCTIONS, LOWER_TAILREC),
|
||||||
/* ... ... */ LOWER_DEFAULT_PARAMETER_EXTENT("Default Parameter Extent Lowering", LOWER_TAILREC, LOWER_ENUMS),
|
/* ... ... */ LOWER_DEFAULT_PARAMETER_EXTENT("Default Parameter Extent Lowering", LOWER_TAILREC, LOWER_ENUMS),
|
||||||
/* ... ... */ LOWER_INNER_CLASSES("Inner classes lowering", LOWER_DEFAULT_PARAMETER_EXTENT),
|
/* ... ... */ LOWER_INNER_CLASSES("Inner classes lowering", LOWER_DEFAULT_PARAMETER_EXTENT),
|
||||||
/* ... ... */ LOWER_BUILTIN_OPERATORS("BuiltIn Operators Lowering", LOWER_DEFAULT_PARAMETER_EXTENT, LOWER_LATEINIT),
|
/* ... ... */ LOWER_BUILTIN_OPERATORS("BuiltIn Operators Lowering", LOWER_DEFAULT_PARAMETER_EXTENT),
|
||||||
/* ... ... */ LOWER_COROUTINES("Coroutines lowering", LOWER_LOCAL_FUNCTIONS),
|
/* ... ... */ LOWER_COROUTINES("Coroutines lowering", LOWER_LOCAL_FUNCTIONS),
|
||||||
/* ... ... */ LOWER_TYPE_OPERATORS("Type operators lowering", LOWER_COROUTINES),
|
/* ... ... */ LOWER_TYPE_OPERATORS("Type operators lowering", LOWER_COROUTINES),
|
||||||
/* ... ... */ BRIDGES_BUILDING("Bridges building", LOWER_COROUTINES),
|
/* ... ... */ BRIDGES_BUILDING("Bridges building", LOWER_COROUTINES),
|
||||||
|
|||||||
+2
-2
@@ -75,7 +75,7 @@ internal fun <T : CallableMemberDescriptor> T.resolveFakeOverride(): T {
|
|||||||
private val intrinsicAnnotation = FqName("konan.internal.Intrinsic")
|
private val intrinsicAnnotation = FqName("konan.internal.Intrinsic")
|
||||||
|
|
||||||
// TODO: check it is external?
|
// TODO: check it is external?
|
||||||
internal val FunctionDescriptor.isIntrinsic: Boolean
|
internal val CallableDescriptor.isIntrinsic: Boolean
|
||||||
get() = this.annotations.findAnnotation(intrinsicAnnotation) != null
|
get() = this.annotations.findAnnotation(intrinsicAnnotation) != null
|
||||||
|
|
||||||
internal fun FunctionDescriptor.externalOrIntrinsic() = isExternal || isIntrinsic || (this is IrBuiltinOperatorDescriptorBase)
|
internal fun FunctionDescriptor.externalOrIntrinsic() = isExternal || isIntrinsic || (this is IrBuiltinOperatorDescriptorBase)
|
||||||
@@ -305,7 +305,7 @@ internal val FunctionDescriptor.needsInlining: Boolean
|
|||||||
get() {
|
get() {
|
||||||
val inlineConstructor = annotations.hasAnnotation(inlineConstructor)
|
val inlineConstructor = annotations.hasAnnotation(inlineConstructor)
|
||||||
if (inlineConstructor) return true
|
if (inlineConstructor) return true
|
||||||
return (this.isInline && !this.isExternal)
|
return (this.isInline && !this.isExternal && !this.propertyIfAccessor.isIntrinsic)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val FunctionDescriptor.needsSerializedIr: Boolean
|
internal val FunctionDescriptor.needsSerializedIr: Boolean
|
||||||
|
|||||||
+48
-21
@@ -18,9 +18,7 @@ package org.jetbrains.kotlin.backend.konan.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.lower.createIrBuilder
|
import org.jetbrains.kotlin.backend.common.lower.*
|
||||||
import org.jetbrains.kotlin.backend.common.lower.irBlock
|
|
||||||
import org.jetbrains.kotlin.backend.common.lower.irIfThen
|
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||||
import org.jetbrains.kotlin.ir.IrStatement
|
import org.jetbrains.kotlin.ir.IrStatement
|
||||||
@@ -29,34 +27,45 @@ import org.jetbrains.kotlin.ir.declarations.IrFile
|
|||||||
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
import org.jetbrains.kotlin.ir.declarations.IrFunction
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
import org.jetbrains.kotlin.ir.declarations.IrVariable
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrGetValue
|
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.expressions.impl.IrConstImpl
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrFieldSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.types.TypeUtils
|
||||||
|
|
||||||
class LateinitLowering(
|
internal class LateinitLowering(
|
||||||
val context: CommonBackendContext,
|
val context: CommonBackendContext,
|
||||||
private val generateParameterNameInAssertion: Boolean = false
|
private val generateParameterNameInAssertion: Boolean = false
|
||||||
) : FileLoweringPass {
|
) : FileLoweringPass {
|
||||||
|
|
||||||
|
private val KOTLIN_FQ_NAME = FqName("kotlin")
|
||||||
|
private val kotlinPackageScope = context.ir.irModule.descriptor.getPackage(KOTLIN_FQ_NAME).memberScope
|
||||||
|
private val isInitializedPropertyDescriptor = kotlinPackageScope
|
||||||
|
.getContributedVariables(Name.identifier("isInitialized"), NoLookupLocation.FROM_BACKEND).single {
|
||||||
|
it.extensionReceiverParameter.let { it != null && TypeUtils.getClassDescriptor(it.type) == context.reflectionTypes.kProperty0 }
|
||||||
|
}
|
||||||
|
private val isInitializedGetterDescriptor = isInitializedPropertyDescriptor.getter!!
|
||||||
|
|
||||||
override fun lower(irFile: IrFile) {
|
override fun lower(irFile: IrFile) {
|
||||||
irFile.transformChildrenVoid(object : IrElementTransformerVoid() {
|
irFile.transformChildrenVoid(object : IrBuildingTransformer(context) {
|
||||||
|
|
||||||
override fun visitVariable(declaration: IrVariable): IrStatement {
|
override fun visitVariable(declaration: IrVariable): IrStatement {
|
||||||
declaration.transformChildrenVoid(this)
|
declaration.transformChildrenVoid(this)
|
||||||
|
|
||||||
val symbol = declaration.symbol
|
|
||||||
val descriptor = declaration.descriptor
|
val descriptor = declaration.descriptor
|
||||||
if (!descriptor.isLateInit) return declaration
|
if (!descriptor.isLateInit) return declaration
|
||||||
|
|
||||||
assert(declaration.initializer == null, { "'lateinit' modifier is not allowed for variables with initializer" })
|
assert(declaration.initializer == null, { "'lateinit' modifier is not allowed for variables with initializer" })
|
||||||
assert(!KotlinBuiltIns.isPrimitiveType(descriptor.type), { "'lateinit' modifier is not allowed on primitive types" })
|
assert(!KotlinBuiltIns.isPrimitiveType(descriptor.type), { "'lateinit' modifier is not allowed on primitive types" })
|
||||||
val startOffset = declaration.startOffset
|
builder.at(declaration).run {
|
||||||
val endOffset = declaration.endOffset
|
|
||||||
val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset)
|
|
||||||
irBuilder.run {
|
|
||||||
declaration.initializer = irNull()
|
declaration.initializer = irNull()
|
||||||
}
|
}
|
||||||
return declaration
|
return declaration
|
||||||
@@ -68,10 +77,7 @@ class LateinitLowering(
|
|||||||
if (descriptor == null || !descriptor.isLateInit) return expression
|
if (descriptor == null || !descriptor.isLateInit) return expression
|
||||||
|
|
||||||
assert(!KotlinBuiltIns.isPrimitiveType(descriptor.type), { "'lateinit' modifier is not allowed on primitive types" })
|
assert(!KotlinBuiltIns.isPrimitiveType(descriptor.type), { "'lateinit' modifier is not allowed on primitive types" })
|
||||||
val startOffset = expression.startOffset
|
builder.at(expression).run {
|
||||||
val endOffset = expression.endOffset
|
|
||||||
val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset)
|
|
||||||
irBuilder.run {
|
|
||||||
return irBlock(expression) {
|
return irBlock(expression) {
|
||||||
// TODO: do data flow analysis to check if value is proved to be not-null.
|
// TODO: do data flow analysis to check if value is proved to be not-null.
|
||||||
+irIfThen(
|
+irIfThen(
|
||||||
@@ -83,6 +89,31 @@ class LateinitLowering(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visitCall(expression: IrCall): IrExpression {
|
||||||
|
expression.transformChildrenVoid(this)
|
||||||
|
|
||||||
|
val descriptor = expression.descriptor
|
||||||
|
if (descriptor != isInitializedGetterDescriptor) return expression
|
||||||
|
|
||||||
|
val propertyReference = expression.extensionReceiver!! as IrPropertyReference
|
||||||
|
assert(propertyReference.extensionReceiver == null, { "'lateinit' modifier is not allowed on extension properties" })
|
||||||
|
// TODO: Take propertyReference.fieldSymbol as soon as it will show up in IR.
|
||||||
|
val propertyDescriptor = propertyReference.descriptor
|
||||||
|
|
||||||
|
val type = propertyDescriptor.type
|
||||||
|
assert(!KotlinBuiltIns.isPrimitiveType(type), { "'lateinit' modifier is not allowed on primitive types" })
|
||||||
|
builder.at(expression).run {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
val fieldValue = IrGetFieldImpl(
|
||||||
|
expression.startOffset,
|
||||||
|
expression.endOffset,
|
||||||
|
propertyDescriptor,
|
||||||
|
propertyReference.dispatchReceiver
|
||||||
|
)
|
||||||
|
return irNotEquals(fieldValue, irNull())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun visitProperty(declaration: IrProperty): IrStatement {
|
override fun visitProperty(declaration: IrProperty): IrStatement {
|
||||||
declaration.transformChildrenVoid(this)
|
declaration.transformChildrenVoid(this)
|
||||||
|
|
||||||
@@ -93,9 +124,7 @@ class LateinitLowering(
|
|||||||
transformGetter(backingField.symbol, declaration.getter!!)
|
transformGetter(backingField.symbol, declaration.getter!!)
|
||||||
|
|
||||||
assert(backingField.initializer == null, { "'lateinit' modifier is not allowed for properties with initializer" })
|
assert(backingField.initializer == null, { "'lateinit' modifier is not allowed for properties with initializer" })
|
||||||
val startOffset = declaration.startOffset
|
val irBuilder = context.createIrBuilder(backingField.symbol, declaration.startOffset, declaration.endOffset)
|
||||||
val endOffset = declaration.endOffset
|
|
||||||
val irBuilder = context.createIrBuilder(backingField.symbol, startOffset, endOffset)
|
|
||||||
irBuilder.run {
|
irBuilder.run {
|
||||||
backingField.initializer = irExprBody(irNull())
|
backingField.initializer = irExprBody(irNull())
|
||||||
}
|
}
|
||||||
@@ -106,9 +135,7 @@ class LateinitLowering(
|
|||||||
private fun transformGetter(backingFieldSymbol: IrFieldSymbol, getter: IrFunction) {
|
private fun transformGetter(backingFieldSymbol: IrFieldSymbol, getter: IrFunction) {
|
||||||
val type = backingFieldSymbol.descriptor.type
|
val type = backingFieldSymbol.descriptor.type
|
||||||
assert(!KotlinBuiltIns.isPrimitiveType(type), { "'lateinit' modifier is not allowed on primitive types" })
|
assert(!KotlinBuiltIns.isPrimitiveType(type), { "'lateinit' modifier is not allowed on primitive types" })
|
||||||
val startOffset = getter.startOffset
|
val irBuilder = context.createIrBuilder(getter.symbol, getter.startOffset, getter.endOffset)
|
||||||
val endOffset = getter.endOffset
|
|
||||||
val irBuilder = context.createIrBuilder(getter.symbol, startOffset, endOffset)
|
|
||||||
irBuilder.run {
|
irBuilder.run {
|
||||||
getter.body = irBlockBody {
|
getter.body = irBlockBody {
|
||||||
val resultVar = irTemporary(
|
val resultVar = irTemporary(
|
||||||
|
|||||||
@@ -1058,6 +1058,16 @@ task lateinit_localCapturedNotInitialized(type: RunKonanTest) {
|
|||||||
source = "codegen/lateinit/localCapturedNotInitialized.kt"
|
source = "codegen/lateinit/localCapturedNotInitialized.kt"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
task lateinit_isInitialized(type: RunKonanTest) {
|
||||||
|
goldValue = "false\ntrue\n"
|
||||||
|
source = "codegen/lateinit/isInitialized.kt"
|
||||||
|
}
|
||||||
|
|
||||||
|
task lateinit_globalIsInitialized(type: RunKonanTest) {
|
||||||
|
goldValue = "false\ntrue\n"
|
||||||
|
source = "codegen/lateinit/globalIsInitialized.kt"
|
||||||
|
}
|
||||||
|
|
||||||
task kclass0(type: RunKonanTest) {
|
task kclass0(type: RunKonanTest) {
|
||||||
source = "codegen/kclass/kclass0.kt"
|
source = "codegen/kclass/kclass0.kt"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package codegen.lateinit.globalIsInitialized
|
||||||
|
|
||||||
|
import kotlin.test.*
|
||||||
|
|
||||||
|
lateinit var s: String
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
println(::s.isInitialized)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun runTest() {
|
||||||
|
foo()
|
||||||
|
s = "zzz"
|
||||||
|
foo()
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package codegen.lateinit.isInitialized
|
||||||
|
|
||||||
|
import kotlin.test.*
|
||||||
|
|
||||||
|
class A {
|
||||||
|
lateinit var s: String
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
println(::s.isInitialized)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun runTest() {
|
||||||
|
val a = A()
|
||||||
|
a.foo()
|
||||||
|
a.s = "zzz"
|
||||||
|
a.foo()
|
||||||
|
}
|
||||||
@@ -40,7 +40,7 @@ annotation class CName(val fullName: String = "", val shortName: String = "")
|
|||||||
/**
|
/**
|
||||||
* This annotation denotes that the element is intrinsic and its usages require special handling in compiler.
|
* This annotation denotes that the element is intrinsic and its usages require special handling in compiler.
|
||||||
*/
|
*/
|
||||||
@Target(AnnotationTarget.FUNCTION)
|
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY)
|
||||||
@Retention(AnnotationRetention.BINARY)
|
@Retention(AnnotationRetention.BINARY)
|
||||||
annotation class Intrinsic
|
annotation class Intrinsic
|
||||||
|
|
||||||
|
|||||||
@@ -76,3 +76,12 @@ internal annotation class InlineExposed
|
|||||||
@Target(AnnotationTarget.TYPE_PARAMETER)
|
@Target(AnnotationTarget.TYPE_PARAMETER)
|
||||||
@Retention(AnnotationRetention.BINARY)
|
@Retention(AnnotationRetention.BINARY)
|
||||||
internal annotation class PureReifiable
|
internal annotation class PureReifiable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value of this parameter should be a property reference expression (`this::foo`), referencing a `lateinit` property,
|
||||||
|
* the backing field of which is accessible at the point where the corresponding argument is passed.
|
||||||
|
*/
|
||||||
|
@Target(AnnotationTarget.VALUE_PARAMETER)
|
||||||
|
@Retention(AnnotationRetention.BINARY)
|
||||||
|
@SinceKotlin("1.2")
|
||||||
|
internal annotation class AccessibleLateinitPropertyLiteral
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
@file:Suppress("unused", "WRONG_ANNOTATION_TARGET_WITH_USE_SITE_TARGET_ON_TYPE")
|
||||||
|
|
||||||
|
package kotlin
|
||||||
|
|
||||||
|
import kotlin.internal.InlineOnly
|
||||||
|
import kotlin.internal.AccessibleLateinitPropertyLiteral
|
||||||
|
import kotlin.reflect.KProperty0
|
||||||
|
import konan.internal.Intrinsic
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns `true` if this lateinit property has been assigned a value, and `false` otherwise.
|
||||||
|
*
|
||||||
|
* Cannot be used in an inline function, to avoid binary compatibility issues.
|
||||||
|
*/
|
||||||
|
@SinceKotlin("1.2")
|
||||||
|
@InlineOnly
|
||||||
|
@Intrinsic
|
||||||
|
inline val @receiver:AccessibleLateinitPropertyLiteral KProperty0<*>.isInitialized: Boolean
|
||||||
|
get() = throw NotImplementedError("Implementation is intrinsic")
|
||||||
Reference in New Issue
Block a user