Always generate temporary variables for receivers in a property compound assignment

(emulate 'resolve' behavior).
This commit is contained in:
Dmitry Petrov
2016-09-05 09:49:40 +03:00
committed by Dmitry Petrov
parent 28b3ea27f3
commit 4cc5fb7454
9 changed files with 227 additions and 90 deletions
@@ -18,14 +18,10 @@ package org.jetbrains.kotlin.psi2ir.intermediate
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.PropertyGetterDescriptor
import org.jetbrains.kotlin.descriptors.PropertySetterDescriptor
import org.jetbrains.kotlin.ir.declarations.IrVariable
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
import org.jetbrains.kotlin.psi2ir.generators.Scope
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.SmartList
class SimplePropertyLValue(
val context: GeneratorContext,
@@ -62,22 +58,29 @@ class SimplePropertyLValue(
override fun assign(withLValue: (LValue) -> IrExpression) =
callReceiver.call { dispatchReceiverValue, extensionReceiverValue ->
val variablesForReceivers = SmartList<IrVariable>()
val dispatchReceiverTmp = dispatchReceiverValue?.let {
scope.createTemporaryVariable(dispatchReceiverValue.load(), "this")
}
val dispatchReceiverValue2 = dispatchReceiverTmp?.let { VariableLValue(it) }
val extensionReceiverTmp = extensionReceiverValue?.let {
scope.createTemporaryVariable(extensionReceiverValue.load(), "receiver")
}
val extensionReceiverValue2 = extensionReceiverTmp?.let { VariableLValue(it) }
val irResultExpression = withLValue(
SimplePropertyLValue(context, scope, startOffset, endOffset, irOperator, descriptor,
SimpleCallReceiver(dispatchReceiverValue, extensionReceiverValue),
SimpleCallReceiver(dispatchReceiverValue2, extensionReceiverValue2),
superQualifier)
)
if (variablesForReceivers.isEmpty()) {
irResultExpression
}
else {
val irBlock = IrBlockImpl(startOffset, endOffset, irResultExpression.type, irOperator)
irBlock.addAll(variablesForReceivers)
irBlock.addStatement(irResultExpression)
irBlock
}
val irBlock = IrBlockImpl(startOffset, endOffset, irResultExpression.type, irOperator)
irBlock.addIfNotNull(dispatchReceiverTmp)
irBlock.addIfNotNull(extensionReceiverTmp)
irBlock.addStatement(irResultExpression)
irBlock
}
override fun assign(value: IrExpression): IrExpression =
store(value)
}
@@ -28,23 +28,28 @@ FILE /augmentedAssignment1.kt
other: CONST Int type=kotlin.Int value='5'
FUN public fun testProperty(): kotlin.Unit
BLOCK_BODY
CALL .<set-p> type=kotlin.Unit operator=PLUSEQ
<set-?>: CALL .plus type=kotlin.Int operator=PLUSEQ
$this: CALL .<get-p> type=kotlin.Int operator=PLUSEQ
other: CONST Int type=kotlin.Int value='1'
CALL .<set-p> type=kotlin.Unit operator=MINUSEQ
<set-?>: CALL .minus type=kotlin.Int operator=MINUSEQ
$this: CALL .<get-p> type=kotlin.Int operator=MINUSEQ
other: CONST Int type=kotlin.Int value='2'
CALL .<set-p> type=kotlin.Unit operator=MULTEQ
<set-?>: CALL .times type=kotlin.Int operator=MULTEQ
$this: CALL .<get-p> type=kotlin.Int operator=MULTEQ
other: CONST Int type=kotlin.Int value='3'
CALL .<set-p> type=kotlin.Unit operator=DIVEQ
<set-?>: CALL .div type=kotlin.Int operator=DIVEQ
$this: CALL .<get-p> type=kotlin.Int operator=DIVEQ
other: CONST Int type=kotlin.Int value='4'
CALL .<set-p> type=kotlin.Unit operator=PERCEQ
<set-?>: CALL .mod type=kotlin.Int operator=PERCEQ
$this: CALL .<get-p> type=kotlin.Int operator=PERCEQ
other: CONST Int type=kotlin.Int value='5'
BLOCK type=kotlin.Unit operator=PLUSEQ
CALL .<set-p> type=kotlin.Unit operator=PLUSEQ
<set-?>: CALL .plus type=kotlin.Int operator=PLUSEQ
$this: CALL .<get-p> type=kotlin.Int operator=PLUSEQ
other: CONST Int type=kotlin.Int value='1'
BLOCK type=kotlin.Unit operator=MINUSEQ
CALL .<set-p> type=kotlin.Unit operator=MINUSEQ
<set-?>: CALL .minus type=kotlin.Int operator=MINUSEQ
$this: CALL .<get-p> type=kotlin.Int operator=MINUSEQ
other: CONST Int type=kotlin.Int value='2'
BLOCK type=kotlin.Unit operator=MULTEQ
CALL .<set-p> type=kotlin.Unit operator=MULTEQ
<set-?>: CALL .times type=kotlin.Int operator=MULTEQ
$this: CALL .<get-p> type=kotlin.Int operator=MULTEQ
other: CONST Int type=kotlin.Int value='3'
BLOCK type=kotlin.Unit operator=DIVEQ
CALL .<set-p> type=kotlin.Unit operator=DIVEQ
<set-?>: CALL .div type=kotlin.Int operator=DIVEQ
$this: CALL .<get-p> type=kotlin.Int operator=DIVEQ
other: CONST Int type=kotlin.Int value='4'
BLOCK type=kotlin.Unit operator=PERCEQ
CALL .<set-p> type=kotlin.Unit operator=PERCEQ
<set-?>: CALL .mod type=kotlin.Int operator=PERCEQ
$this: CALL .<get-p> type=kotlin.Int operator=PERCEQ
other: CONST Int type=kotlin.Int value='5'
@@ -38,18 +38,23 @@ FILE /augmentedAssignment2.kt
s: CONST String type=kotlin.String value='*='
FUN public fun testProperty(): kotlin.Unit
BLOCK_BODY
CALL .plusAssign type=kotlin.Unit operator=PLUSEQ
$receiver: CALL .<get-p> type=A operator=PLUSEQ
s: CONST String type=kotlin.String value='+='
CALL .minusAssign type=kotlin.Unit operator=MINUSEQ
$receiver: CALL .<get-p> type=A operator=MINUSEQ
s: CONST String type=kotlin.String value='-='
CALL .timesAssign type=kotlin.Unit operator=MULTEQ
$receiver: CALL .<get-p> type=A operator=MULTEQ
s: CONST String type=kotlin.String value='*='
CALL .divAssign type=kotlin.Unit operator=DIVEQ
$receiver: CALL .<get-p> type=A operator=DIVEQ
s: CONST String type=kotlin.String value='/='
CALL .modAssign type=kotlin.Unit operator=PERCEQ
$receiver: CALL .<get-p> type=A operator=PERCEQ
s: CONST String type=kotlin.String value='%='
BLOCK type=kotlin.Unit operator=PLUSEQ
CALL .plusAssign type=kotlin.Unit operator=PLUSEQ
$receiver: CALL .<get-p> type=A operator=PLUSEQ
s: CONST String type=kotlin.String value='+='
BLOCK type=kotlin.Unit operator=MINUSEQ
CALL .minusAssign type=kotlin.Unit operator=MINUSEQ
$receiver: CALL .<get-p> type=A operator=MINUSEQ
s: CONST String type=kotlin.String value='-='
BLOCK type=kotlin.Unit operator=MULTEQ
CALL .timesAssign type=kotlin.Unit operator=MULTEQ
$receiver: CALL .<get-p> type=A operator=MULTEQ
s: CONST String type=kotlin.String value='*='
BLOCK type=kotlin.Unit operator=DIVEQ
CALL .divAssign type=kotlin.Unit operator=DIVEQ
$receiver: CALL .<get-p> type=A operator=DIVEQ
s: CONST String type=kotlin.String value='/='
BLOCK type=kotlin.Unit operator=PERCEQ
CALL .modAssign type=kotlin.Unit operator=PERCEQ
$receiver: CALL .<get-p> type=A operator=PERCEQ
s: CONST String type=kotlin.String value='%='
@@ -0,0 +1,20 @@
object X1 {
var x1 = 0
object X2 {
var x2 = 0
object X3 {
var x3 = 0
}
}
}
fun test1(a: IntArray) {
var i = 0
a[i++]++
}
fun test2() {
X1.x1++
X1.X2.x2++
X1.X2.X3.x3++
}
@@ -0,0 +1,88 @@
FILE /complexAugmentedAssignment.kt
CLASS OBJECT X1
CONSTRUCTOR private constructor X1()
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL Any
INSTANCE_INITIALIZER_CALL classDescriptor=X1
PROPERTY public final var x1: kotlin.Int
EXPRESSION_BODY
CONST Int type=kotlin.Int value='0'
CLASS OBJECT X2
CONSTRUCTOR private constructor X2()
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL Any
INSTANCE_INITIALIZER_CALL classDescriptor=X2
PROPERTY public final var x2: kotlin.Int
EXPRESSION_BODY
CONST Int type=kotlin.Int value='0'
CLASS OBJECT X3
CONSTRUCTOR private constructor X3()
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL Any
INSTANCE_INITIALIZER_CALL classDescriptor=X3
PROPERTY public final var x3: kotlin.Int
EXPRESSION_BODY
CONST Int type=kotlin.Int value='0'
FUN public fun test1(/*0*/ a: kotlin.IntArray): kotlin.Unit
BLOCK_BODY
VAR var i: kotlin.Int
CONST Int type=kotlin.Int value='0'
BLOCK type=kotlin.Int operator=POSTFIX_INCR
VAR val tmp1_array: kotlin.IntArray
GET_VAR a type=kotlin.IntArray operator=null
VAR val tmp2_index0: kotlin.Int
BLOCK type=kotlin.Int operator=POSTFIX_INCR
VAR val tmp0: kotlin.Int
GET_VAR i type=kotlin.Int operator=POSTFIX_INCR
SET_VAR i type=kotlin.Unit operator=POSTFIX_INCR
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
VAR val tmp3: kotlin.Int
CALL .get type=kotlin.Int operator=POSTFIX_INCR
$this: GET_VAR tmp1_array type=kotlin.IntArray operator=null
index: GET_VAR tmp2_index0 type=kotlin.Int operator=null
CALL .set type=kotlin.Unit operator=POSTFIX_INCR
$this: GET_VAR tmp1_array type=kotlin.IntArray operator=null
index: GET_VAR tmp2_index0 type=kotlin.Int operator=null
value: CALL .inc type=kotlin.Int operator=POSTFIX_INCR
$this: GET_VAR tmp3 type=kotlin.Int operator=null
GET_VAR tmp3 type=kotlin.Int operator=null
FUN public fun test2(): kotlin.Unit
BLOCK_BODY
BLOCK type=kotlin.Int operator=POSTFIX_INCR
VAR val tmp0_this: X1
GET_OBJECT X1 type=X1
BLOCK type=kotlin.Int operator=POSTFIX_INCR
VAR val tmp1: kotlin.Int
CALL .<get-x1> type=kotlin.Int operator=POSTFIX_INCR
$this: GET_VAR tmp0_this type=X1 operator=null
CALL .<set-x1> type=kotlin.Unit operator=POSTFIX_INCR
$this: GET_VAR tmp0_this type=X1 operator=null
<set-?>: CALL .inc type=kotlin.Int operator=POSTFIX_INCR
$this: GET_VAR tmp1 type=kotlin.Int operator=null
GET_VAR tmp1 type=kotlin.Int operator=null
BLOCK type=kotlin.Int operator=POSTFIX_INCR
VAR val tmp2_this: X1.X2
GET_OBJECT X2 type=X1.X2
BLOCK type=kotlin.Int operator=POSTFIX_INCR
VAR val tmp3: kotlin.Int
CALL .<get-x2> type=kotlin.Int operator=POSTFIX_INCR
$this: GET_VAR tmp2_this type=X1.X2 operator=null
CALL .<set-x2> type=kotlin.Unit operator=POSTFIX_INCR
$this: GET_VAR tmp2_this type=X1.X2 operator=null
<set-?>: CALL .inc type=kotlin.Int operator=POSTFIX_INCR
$this: GET_VAR tmp3 type=kotlin.Int operator=null
GET_VAR tmp3 type=kotlin.Int operator=null
BLOCK type=kotlin.Int operator=POSTFIX_INCR
VAR val tmp4_this: X1.X2.X3
GET_OBJECT X3 type=X1.X2.X3
BLOCK type=kotlin.Int operator=POSTFIX_INCR
VAR val tmp5: kotlin.Int
CALL .<get-x3> type=kotlin.Int operator=POSTFIX_INCR
$this: GET_VAR tmp4_this type=X1.X2.X3 operator=null
CALL .<set-x3> type=kotlin.Unit operator=POSTFIX_INCR
$this: GET_VAR tmp4_this type=X1.X2.X3 operator=null
<set-?>: CALL .inc type=kotlin.Int operator=POSTFIX_INCR
$this: GET_VAR tmp5 type=kotlin.Int operator=null
GET_VAR tmp5 type=kotlin.Int operator=null
@@ -32,14 +32,17 @@ FILE /forWithImplicitReceivers.kt
BLOCK_BODY
RETURN type=kotlin.Nothing from=next
BLOCK type=kotlin.Int operator=POSTFIX_DECR
VAR val tmp0: kotlin.Int
CALL .<get-value> type=kotlin.Int operator=POSTFIX_DECR
$this: $RECEIVER of: next type=IntCell
CALL .<set-value> type=kotlin.Unit operator=POSTFIX_DECR
$this: $RECEIVER of: next type=IntCell
<set-?>: CALL .dec type=kotlin.Int operator=POSTFIX_DECR
$this: GET_VAR tmp0 type=kotlin.Int operator=null
GET_VAR tmp0 type=kotlin.Int operator=null
VAR val tmp0_this: IntCell
$RECEIVER of: next type=IntCell
BLOCK type=kotlin.Int operator=POSTFIX_DECR
VAR val tmp1: kotlin.Int
CALL .<get-value> type=kotlin.Int operator=POSTFIX_DECR
$this: GET_VAR tmp0_this type=IntCell operator=null
CALL .<set-value> type=kotlin.Unit operator=POSTFIX_DECR
$this: GET_VAR tmp0_this type=IntCell operator=null
<set-?>: CALL .dec type=kotlin.Int operator=POSTFIX_DECR
$this: GET_VAR tmp1 type=kotlin.Int operator=null
GET_VAR tmp1 type=kotlin.Int operator=null
FUN public fun IReceiver.test(): kotlin.Unit
BLOCK_BODY
BLOCK type=kotlin.Unit operator=FOR_LOOP
@@ -53,38 +53,42 @@ FILE /incrementDecrement.kt
BLOCK_BODY
VAR val p1: kotlin.Int
BLOCK type=kotlin.Int operator=PREFIX_INCR
VAR val tmp0: kotlin.Int
CALL .inc type=kotlin.Int operator=PREFIX_INCR
$this: CALL .<get-p> type=kotlin.Int operator=PREFIX_INCR
CALL .<set-p> type=kotlin.Unit operator=PREFIX_INCR
<set-?>: GET_VAR tmp0 type=kotlin.Int operator=null
GET_VAR tmp0 type=kotlin.Int operator=null
BLOCK type=kotlin.Int operator=PREFIX_INCR
VAR val tmp0: kotlin.Int
CALL .inc type=kotlin.Int operator=PREFIX_INCR
$this: CALL .<get-p> type=kotlin.Int operator=PREFIX_INCR
CALL .<set-p> type=kotlin.Unit operator=PREFIX_INCR
<set-?>: GET_VAR tmp0 type=kotlin.Int operator=null
GET_VAR tmp0 type=kotlin.Int operator=null
VAR val p2: kotlin.Int
BLOCK type=kotlin.Int operator=PREFIX_DECR
VAR val tmp1: kotlin.Int
CALL .dec type=kotlin.Int operator=PREFIX_DECR
$this: CALL .<get-p> type=kotlin.Int operator=PREFIX_DECR
CALL .<set-p> type=kotlin.Unit operator=PREFIX_DECR
<set-?>: GET_VAR tmp1 type=kotlin.Int operator=null
GET_VAR tmp1 type=kotlin.Int operator=null
BLOCK type=kotlin.Int operator=PREFIX_DECR
VAR val tmp1: kotlin.Int
CALL .dec type=kotlin.Int operator=PREFIX_DECR
$this: CALL .<get-p> type=kotlin.Int operator=PREFIX_DECR
CALL .<set-p> type=kotlin.Unit operator=PREFIX_DECR
<set-?>: GET_VAR tmp1 type=kotlin.Int operator=null
GET_VAR tmp1 type=kotlin.Int operator=null
FUN public fun testPropPostfix(): kotlin.Unit
BLOCK_BODY
VAR val p1: kotlin.Int
BLOCK type=kotlin.Int operator=POSTFIX_INCR
VAR val tmp0: kotlin.Int
CALL .<get-p> type=kotlin.Int operator=POSTFIX_INCR
CALL .<set-p> type=kotlin.Unit operator=POSTFIX_INCR
<set-?>: 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
BLOCK type=kotlin.Int operator=POSTFIX_INCR
VAR val tmp0: kotlin.Int
CALL .<get-p> type=kotlin.Int operator=POSTFIX_INCR
CALL .<set-p> type=kotlin.Unit operator=POSTFIX_INCR
<set-?>: 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
VAR val p2: kotlin.Int
BLOCK type=kotlin.Int operator=PREFIX_DECR
VAR val tmp1: kotlin.Int
CALL .dec type=kotlin.Int operator=PREFIX_DECR
$this: CALL .<get-p> type=kotlin.Int operator=PREFIX_DECR
CALL .<set-p> type=kotlin.Unit operator=PREFIX_DECR
<set-?>: GET_VAR tmp1 type=kotlin.Int operator=null
GET_VAR tmp1 type=kotlin.Int operator=null
BLOCK type=kotlin.Int operator=PREFIX_DECR
VAR val tmp1: kotlin.Int
CALL .dec type=kotlin.Int operator=PREFIX_DECR
$this: CALL .<get-p> type=kotlin.Int operator=PREFIX_DECR
CALL .<set-p> type=kotlin.Unit operator=PREFIX_DECR
<set-?>: GET_VAR tmp1 type=kotlin.Int operator=null
GET_VAR tmp1 type=kotlin.Int operator=null
FUN public fun testArrayPrefix(): kotlin.Unit
BLOCK_BODY
VAR val a1: kotlin.Int
@@ -41,14 +41,17 @@ FILE /safeCallWithIncrementDecrement.kt
arg1: CONST Null type=kotlin.Nothing? value='null'
then: CONST Null type=kotlin.Nothing? value='null'
else: BLOCK type=kotlin.Int operator=POSTFIX_INCR
VAR val tmp1: kotlin.Int
CALL .<get-p> type=kotlin.Int operator=POSTFIX_INCR
$this: GET_VAR tmp0_safe_receiver type=test.C? operator=null
CALL .<set-p> type=kotlin.Unit operator=POSTFIX_INCR
$this: GET_VAR tmp0_safe_receiver type=test.C? operator=null
value: CALL .inc type=kotlin.Int? operator=POSTFIX_INCR
$receiver: GET_VAR tmp1 type=kotlin.Int operator=null
GET_VAR tmp1 type=kotlin.Int operator=null
VAR val tmp1_this: test.C?
GET_VAR tmp0_safe_receiver type=test.C? operator=null
BLOCK type=kotlin.Int operator=POSTFIX_INCR
VAR val tmp2: kotlin.Int
CALL .<get-p> type=kotlin.Int operator=POSTFIX_INCR
$this: GET_VAR tmp1_this type=test.C? operator=null
CALL .<set-p> type=kotlin.Unit operator=POSTFIX_INCR
$this: GET_VAR tmp1_this type=test.C? operator=null
value: CALL .inc type=kotlin.Int? operator=POSTFIX_INCR
$receiver: GET_VAR tmp2 type=kotlin.Int operator=null
GET_VAR tmp2 type=kotlin.Int operator=null
FUN public fun testArrayAccess(/*0*/ nc: test.C?): kotlin.Unit
BLOCK_BODY
BLOCK type=kotlin.Int operator=POSTFIX_INCR
@@ -322,6 +322,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
doTest(fileName);
}
@TestMetadata("complexAugmentedAssignment.kt")
public void testComplexAugmentedAssignment() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/complexAugmentedAssignment.kt");
doTest(fileName);
}
@TestMetadata("conventionComparisons.kt")
public void testConventionComparisons() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/conventionComparisons.kt");