diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/AssignmentGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/AssignmentGenerator.kt index dab9bb64651..8cf47c11d4f 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/AssignmentGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/AssignmentGenerator.kt @@ -114,7 +114,7 @@ class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGen BackingFieldLValue(ktLeft.startOffset, ktLeft.endOffset, descriptor.propertyDescriptor, operator) is LocalVariableDescriptor -> if (descriptor.isDelegated) - TODO("Delegated local variable") + DelegatedLocalPropertyLValue(ktLeft.startOffset, ktLeft.endOffset, descriptor, operator) else VariableLValue(ktLeft.startOffset, ktLeft.endOffset, descriptor, operator) is PropertyDescriptor -> diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt index 5399a2795b6..d0a8b6a2a78 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/CallGenerator.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.psi2ir.generators import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.psiUtil.endOffset @@ -35,7 +36,7 @@ class CallGenerator(statementGenerator: StatementGenerator): StatementGeneratorE generatePropertyGetterCall(descriptor, startOffset, endOffset, call) is VariableDescriptor -> call.callReceiver.call { dispatchReceiverValue, extensionReceiverValue -> - IrGetVariableImpl(startOffset, endOffset, descriptor, operator) + generateGetVariable(startOffset, endOffset, descriptor, operator) } is FunctionDescriptor -> generateFunctionCall(descriptor, startOffset, endOffset, operator, call) @@ -44,6 +45,12 @@ class CallGenerator(statementGenerator: StatementGenerator): StatementGeneratorE } } + fun generateGetVariable(startOffset: Int, endOffset: Int, descriptor: VariableDescriptor, operator: IrOperator? = null) = + if (descriptor is LocalVariableDescriptor && descriptor.isDelegated) + IrCallImpl(startOffset, endOffset, descriptor.type, descriptor.getter!!, operator ?: IrOperator.GET_LOCAL_PROPERTY) + else + IrGetVariableImpl(startOffset, endOffset, descriptor, operator) + fun generateDelegatingConstructorCall(startOffset: Int, endOffset: Int, call: CallBuilder) : IrExpression { val descriptor = call.descriptor if (descriptor !is ConstructorDescriptor) throw AssertionError("Constructor expected: $descriptor") diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/StatementGenerator.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/StatementGenerator.kt index 60d5c34208f..b64c667fde7 100644 --- a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/StatementGenerator.kt +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/generators/StatementGenerator.kt @@ -255,7 +255,7 @@ class StatementGenerator( CallGenerator(this).generateCall(expression.startOffset, expression.endOffset, pregenerateCall(resolvedCall)) } is VariableDescriptor -> - IrGetVariableImpl(expression.startOffset, expression.endOffset, descriptor) + CallGenerator(this).generateGetVariable(expression.startOffset, expression.endOffset, descriptor) else -> IrDummyExpression( expression.startOffset, expression.endOffset, getInferredTypeWithImplicitCastsOrFail(expression), diff --git a/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/DelegatedLocalPropertyLValue.kt b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/DelegatedLocalPropertyLValue.kt new file mode 100644 index 00000000000..4dad1c65dde --- /dev/null +++ b/compiler/ir/ir.psi2ir/src/org/jetbrains/kotlin/psi2ir/intermediate/DelegatedLocalPropertyLValue.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.psi2ir.intermediate + +import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors +import org.jetbrains.kotlin.ir.expressions.IrCallImpl +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrOperator +import org.jetbrains.kotlin.types.KotlinType + +class DelegatedLocalPropertyLValue( + val startOffset: Int, + val endOffset: Int, + val descriptor: VariableDescriptorWithAccessors, + val irOperator: IrOperator? = null +) : LValue, AssignmentReceiver { + override val type: KotlinType get() = descriptor.type + + override fun load(): IrExpression = + IrCallImpl(startOffset, endOffset, descriptor.type, descriptor.getter!!, irOperator) + + override fun store(irExpression: IrExpression): IrExpression = + IrCallImpl(startOffset, endOffset, descriptor.type, descriptor.setter!!, irOperator).apply { + putArgument(0, irExpression) + } + + override fun assign(withLValue: (LValue) -> IrExpression): IrExpression = + withLValue(this) +} diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrOperator.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrOperator.kt index 1594231ba8c..234273ceb63 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrOperator.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrOperator.kt @@ -72,6 +72,7 @@ interface IrOperator { object DESTRUCTURING_DECLARATION : IrOperatorImpl("DESTRUCTURING_DECLARATION") object GET_PROPERTY : IrOperatorImpl("GET_PROPERTY") + object GET_LOCAL_PROPERTY : IrOperatorImpl("GET_LOCAL_PROPERTY") object IF : IrOperatorImpl("IF") object WHEN : IrOperatorImpl("WHEN") diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/RenderIrElement.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/RenderIrElement.kt index 2158b804b5a..95a74afaed1 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/RenderIrElement.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/RenderIrElement.kt @@ -69,6 +69,12 @@ class RenderIrElementVisitor : IrElementVisitor { override fun visitAnonymousInitializer(declaration: IrAnonymousInitializer, data: Nothing?): String = "ANONYMOUS_INITIALIZER ${declaration.descriptor.name}" + override fun visitLocalDelegatedProperty(declaration: IrLocalDelegatedProperty, data: Nothing?): String = + "LOCAL_DELEGATED_PROPERTY ${declaration.descriptor.render()}" + + override fun visitLocalPropertyAccessor(declaration: IrLocalPropertyAccessor, data: Nothing?): String = + "LOCAL_PROPERTY_ACCESSOR ${declaration.descriptor.name}" // can't render, see nullability for modality + override fun visitExpressionBody(body: IrExpressionBody, data: Nothing?): String = "EXPRESSION_BODY" diff --git a/compiler/testData/ir/irText/declarations/localDelegatedProperties.kt b/compiler/testData/ir/irText/declarations/localDelegatedProperties.kt index 33c3df9b896..451196033e5 100644 --- a/compiler/testData/ir/irText/declarations/localDelegatedProperties.kt +++ b/compiler/testData/ir/irText/declarations/localDelegatedProperties.kt @@ -1,4 +1,11 @@ fun test1() { val x by lazy { 42 } println(x) +} + +fun test2() { + var x by hashMapOf() + x = 0 + x++ + x += 1 } \ No newline at end of file diff --git a/compiler/testData/ir/irText/declarations/localDelegatedProperties.txt b/compiler/testData/ir/irText/declarations/localDelegatedProperties.txt index cc11b75af34..abbd60cb91e 100644 --- a/compiler/testData/ir/irText/declarations/localDelegatedProperties.txt +++ b/compiler/testData/ir/irText/declarations/localDelegatedProperties.txt @@ -1,7 +1,7 @@ FILE /localDelegatedProperties.kt FUN public fun test1(): kotlin.Unit BLOCK_BODY - ? IrLocalDelegatedPropertyImpl x + LOCAL_DELEGATED_PROPERTY val x: kotlin.Int VAR val `x$delegate`: kotlin.Lazy CALL .lazy type=kotlin.Lazy operator=null initializer: BLOCK type=() -> kotlin.Int operator=LAMBDA @@ -10,7 +10,7 @@ FILE /localDelegatedProperties.kt RETURN type=kotlin.Nothing from= CONST Int type=kotlin.Int value='42' CALLABLE_REFERENCE local final fun (): kotlin.Int type=() -> kotlin.Int - ? IrLocalPropertyAccessorImpl + LOCAL_PROPERTY_ACCESSOR BLOCK_BODY RETURN type=kotlin.Nothing from= CALL .getValue type=kotlin.Int operator=null @@ -18,4 +18,38 @@ FILE /localDelegatedProperties.kt thisRef: CONST Null type=kotlin.Nothing? value='null' property: CALLABLE_REFERENCE val x: kotlin.Int type=kotlin.reflect.KProperty0 CALL .println type=kotlin.Unit operator=null - message: GET_VAR x type=kotlin.Int operator=null + message: CALL . type=kotlin.Int operator=GET_LOCAL_PROPERTY + FUN public fun test2(): kotlin.Unit + BLOCK_BODY + LOCAL_DELEGATED_PROPERTY var x: kotlin.Int + VAR val `x$delegate`: java.util.HashMap + CALL .hashMapOf type=java.util.HashMap operator=null + LOCAL_PROPERTY_ACCESSOR + BLOCK_BODY + RETURN type=kotlin.Nothing from= + CALL .getValue type=kotlin.Int operator=null + $receiver: GET_VAR x$delegate type=java.util.HashMap operator=null + thisRef: CONST Null type=kotlin.Nothing? value='null' + property: CALLABLE_REFERENCE var x: kotlin.Int type=kotlin.reflect.KMutableProperty0 + LOCAL_PROPERTY_ACCESSOR + BLOCK_BODY + RETURN type=kotlin.Nothing from= + TYPE_OP operator=IMPLICIT_CAST typeOperand=kotlin.Int + CALL .setValue type=kotlin.Unit operator=null + $receiver: GET_VAR x$delegate type=java.util.HashMap operator=null + thisRef: CONST Null type=kotlin.Nothing? value='null' + property: CALLABLE_REFERENCE var x: kotlin.Int type=kotlin.reflect.KMutableProperty0 + value: GET_VAR value type=kotlin.Int operator=null + CALL . type=kotlin.Int operator=EQ + value: CONST Int type=kotlin.Int value='0' + BLOCK type=kotlin.Int operator=POSTFIX_INCR + VAR val tmp0: kotlin.Int + CALL . type=kotlin.Int operator=POSTFIX_INCR + CALL . type=kotlin.Int operator=POSTFIX_INCR + value: CALL .inc type=kotlin.Int operator=POSTFIX_INCR + $this: GET_VAR tmp0 type=kotlin.Int operator=null + GET_VAR tmp0 type=kotlin.Int operator=null + CALL . type=kotlin.Int operator=PLUSEQ + value: CALL .plus type=kotlin.Int operator=PLUSEQ + $this: CALL . type=kotlin.Int operator=PLUSEQ + other: CONST Int type=kotlin.Int value='1'