PSI2IR: generate field writes for all val property assignments

Assuming the frontend is correct, and it hopefully is, all of them are
initializations even if not done in the owner class directly.
This commit is contained in:
pyos
2019-12-19 11:15:43 +01:00
committed by Dmitry Petrov
parent 92f8432b1e
commit a4b005fd5d
4 changed files with 54 additions and 20 deletions
@@ -25,7 +25,6 @@ import org.jetbrains.kotlin.ir.builders.irGet
import org.jetbrains.kotlin.ir.builders.irTemporary import org.jetbrains.kotlin.ir.builders.irTemporary
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrDynamicOperatorExpressionImpl import org.jetbrains.kotlin.ir.expressions.impl.IrDynamicOperatorExpressionImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.util.referenceFunction import org.jetbrains.kotlin.ir.util.referenceFunction
import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
@@ -39,7 +38,6 @@ import org.jetbrains.kotlin.resolve.PropertyImportedFromObject
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic import org.jetbrains.kotlin.resolve.calls.tasks.isDynamic
import org.jetbrains.kotlin.resolve.scopes.receivers.ThisClassReceiver
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGeneratorExtension(statementGenerator) { class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGeneratorExtension(statementGenerator) {
@@ -277,16 +275,10 @@ class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGen
isAssignmentReceiver = isAssignmentStatement isAssignmentReceiver = isAssignmentStatement
) )
) )
isValInitializationInConstructor(descriptor, resolvedCall) -> { origin == IrStatementOrigin.EQ && !descriptor.isVar -> {
val thisClass = getThisClass() // An assignment to a val property can only be its initialization in the constructor.
val thisAsReceiverParameter = thisClass.thisAsReceiverParameter val receiver = resolvedCall.dispatchReceiver ?: descriptor.dispatchReceiverParameter?.value
val thisType = thisAsReceiverParameter.type.toIrType() createBackingFieldLValue(ktLeft, descriptor, statementGenerator.generateReceiverOrNull(ktLeft, receiver), null)
val irThis = IrGetValueImpl(
ktLeft.startOffsetSkippingComments, ktLeft.endOffset,
thisType,
context.symbolTable.referenceValueParameter(thisAsReceiverParameter)
)
createBackingFieldLValue(ktLeft, descriptor, RematerializableValue(irThis), null)
} }
else -> { else -> {
val propertyReceiver = statementGenerator.generateCallReceiver( val propertyReceiver = statementGenerator.generateCallReceiver(
@@ -356,12 +348,6 @@ class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGen
) )
} }
private fun isValInitializationInConstructor(descriptor: PropertyDescriptor, resolvedCall: ResolvedCall<*>): Boolean =
!descriptor.isVar &&
descriptor.kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE &&
statementGenerator.scopeOwner.let { it is ConstructorDescriptor || it is ClassDescriptor } &&
resolvedCall.dispatchReceiver is ThisClassReceiver
private fun getThisClass(): ClassDescriptor { private fun getThisClass(): ClassDescriptor {
val scopeOwner = statementGenerator.scopeOwner val scopeOwner = statementGenerator.scopeOwner
return when (scopeOwner) { return when (scopeOwner) {
@@ -1,6 +1,5 @@
// !USE_EXPERIMENTAL: kotlin.contracts.ExperimentalContracts // !USE_EXPERIMENTAL: kotlin.contracts.ExperimentalContracts
// IGNORE_BACKEND: JVM_IR, JS_IR, NATIVE // IGNORE_BACKEND: NATIVE
// IGNORE_BACKEND_MULTI_MODULE: JVM_IR
// FILE: 1.kt // FILE: 1.kt
+8
View File
@@ -12,3 +12,11 @@ class TestInitValInInitBlock {
} }
} }
class TestInitValInLambdaCalledOnce {
val x: Int
init {
1.run {
x = 0
}
}
}
+41
View File
@@ -93,3 +93,44 @@ FILE fqName:<root> fileName:/initVal.kt
overridden: overridden:
public open fun toString (): kotlin.String declared in kotlin.Any public open fun toString (): kotlin.String declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any $this: VALUE_PARAMETER name:<this> type:kotlin.Any
CLASS CLASS name:TestInitValInLambdaCalledOnce modality:FINAL visibility:public superTypes:[kotlin.Any]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:<root>.TestInitValInLambdaCalledOnce
CONSTRUCTOR visibility:public <> () returnType:<root>.TestInitValInLambdaCalledOnce [primary]
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'public constructor <init> () [primary] declared in kotlin.Any'
INSTANCE_INITIALIZER_CALL classDescriptor='CLASS CLASS name:TestInitValInLambdaCalledOnce modality:FINAL visibility:public superTypes:[kotlin.Any]'
PROPERTY name:x visibility:public modality:FINAL [val]
FIELD PROPERTY_BACKING_FIELD name:x type:kotlin.Int visibility:private [final]
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-x> visibility:public modality:FINAL <> ($this:<root>.TestInitValInLambdaCalledOnce) returnType:kotlin.Int
correspondingProperty: PROPERTY name:x visibility:public modality:FINAL [val]
$this: VALUE_PARAMETER name:<this> type:<root>.TestInitValInLambdaCalledOnce
BLOCK_BODY
RETURN type=kotlin.Nothing from='public final fun <get-x> (): kotlin.Int declared in <root>.TestInitValInLambdaCalledOnce'
GET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:x type:kotlin.Int visibility:private [final]' type=kotlin.Int origin=null
receiver: GET_VAR '<this>: <root>.TestInitValInLambdaCalledOnce declared in <root>.TestInitValInLambdaCalledOnce.<get-x>' type=<root>.TestInitValInLambdaCalledOnce origin=null
ANONYMOUS_INITIALIZER isStatic=false
BLOCK_BODY
CALL 'public final fun run <T, R> (block: @[ExtensionFunctionType] kotlin.Function1<T of kotlin.run, R of kotlin.run>): R of kotlin.run [inline] declared in kotlin' type=kotlin.Unit origin=null
<T>: kotlin.Int
<R>: kotlin.Unit
$receiver: CONST Int type=kotlin.Int value=1
block: FUN_EXPR type=@[ExtensionFunctionType] kotlin.Function1<kotlin.Int, kotlin.Unit> origin=LAMBDA
FUN LOCAL_FUNCTION_FOR_LAMBDA name:<anonymous> visibility:local modality:FINAL <> ($receiver:kotlin.Int) returnType:kotlin.Unit
$receiver: VALUE_PARAMETER name:<this> type:kotlin.Int
BLOCK_BODY
SET_FIELD 'FIELD PROPERTY_BACKING_FIELD name:x type:kotlin.Int visibility:private [final]' type=kotlin.Unit origin=null
receiver: GET_VAR '<this>: <root>.TestInitValInLambdaCalledOnce declared in <root>.TestInitValInLambdaCalledOnce' type=<root>.TestInitValInLambdaCalledOnce origin=null
value: CONST Int type=kotlin.Int value=0
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean [fake_override,operator]
overridden:
public open fun equals (other: kotlin.Any?): kotlin.Boolean [operator] declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
VALUE_PARAMETER name:other index:0 type:kotlin.Any?
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int [fake_override]
overridden:
public open fun hashCode (): kotlin.Int declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String [fake_override]
overridden:
public open fun toString (): kotlin.String declared in kotlin.Any
$this: VALUE_PARAMETER name:<this> type:kotlin.Any