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.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor 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.ir.expressions.*
import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext import org.jetbrains.kotlin.psi2ir.generators.GeneratorContext
import org.jetbrains.kotlin.psi2ir.generators.Scope import org.jetbrains.kotlin.psi2ir.generators.Scope
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.SmartList
class SimplePropertyLValue( class SimplePropertyLValue(
val context: GeneratorContext, val context: GeneratorContext,
@@ -62,22 +58,29 @@ class SimplePropertyLValue(
override fun assign(withLValue: (LValue) -> IrExpression) = override fun assign(withLValue: (LValue) -> IrExpression) =
callReceiver.call { dispatchReceiverValue, extensionReceiverValue -> 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( val irResultExpression = withLValue(
SimplePropertyLValue(context, scope, startOffset, endOffset, irOperator, descriptor, SimplePropertyLValue(context, scope, startOffset, endOffset, irOperator, descriptor,
SimpleCallReceiver(dispatchReceiverValue, extensionReceiverValue), SimpleCallReceiver(dispatchReceiverValue2, extensionReceiverValue2),
superQualifier) superQualifier)
) )
if (variablesForReceivers.isEmpty()) {
irResultExpression
}
else {
val irBlock = IrBlockImpl(startOffset, endOffset, irResultExpression.type, irOperator) val irBlock = IrBlockImpl(startOffset, endOffset, irResultExpression.type, irOperator)
irBlock.addAll(variablesForReceivers) irBlock.addIfNotNull(dispatchReceiverTmp)
irBlock.addIfNotNull(extensionReceiverTmp)
irBlock.addStatement(irResultExpression) irBlock.addStatement(irResultExpression)
irBlock irBlock
} }
}
override fun assign(value: IrExpression): IrExpression =
store(value)
} }
@@ -28,22 +28,27 @@ FILE /augmentedAssignment1.kt
other: CONST Int type=kotlin.Int value='5' other: CONST Int type=kotlin.Int value='5'
FUN public fun testProperty(): kotlin.Unit FUN public fun testProperty(): kotlin.Unit
BLOCK_BODY BLOCK_BODY
BLOCK type=kotlin.Unit operator=PLUSEQ
CALL .<set-p> type=kotlin.Unit operator=PLUSEQ CALL .<set-p> type=kotlin.Unit operator=PLUSEQ
<set-?>: CALL .plus type=kotlin.Int operator=PLUSEQ <set-?>: CALL .plus type=kotlin.Int operator=PLUSEQ
$this: CALL .<get-p> type=kotlin.Int operator=PLUSEQ $this: CALL .<get-p> type=kotlin.Int operator=PLUSEQ
other: CONST Int type=kotlin.Int value='1' other: CONST Int type=kotlin.Int value='1'
BLOCK type=kotlin.Unit operator=MINUSEQ
CALL .<set-p> type=kotlin.Unit operator=MINUSEQ CALL .<set-p> type=kotlin.Unit operator=MINUSEQ
<set-?>: CALL .minus type=kotlin.Int operator=MINUSEQ <set-?>: CALL .minus type=kotlin.Int operator=MINUSEQ
$this: CALL .<get-p> type=kotlin.Int operator=MINUSEQ $this: CALL .<get-p> type=kotlin.Int operator=MINUSEQ
other: CONST Int type=kotlin.Int value='2' other: CONST Int type=kotlin.Int value='2'
BLOCK type=kotlin.Unit operator=MULTEQ
CALL .<set-p> type=kotlin.Unit operator=MULTEQ CALL .<set-p> type=kotlin.Unit operator=MULTEQ
<set-?>: CALL .times type=kotlin.Int operator=MULTEQ <set-?>: CALL .times type=kotlin.Int operator=MULTEQ
$this: CALL .<get-p> type=kotlin.Int operator=MULTEQ $this: CALL .<get-p> type=kotlin.Int operator=MULTEQ
other: CONST Int type=kotlin.Int value='3' other: CONST Int type=kotlin.Int value='3'
BLOCK type=kotlin.Unit operator=DIVEQ
CALL .<set-p> type=kotlin.Unit operator=DIVEQ CALL .<set-p> type=kotlin.Unit operator=DIVEQ
<set-?>: CALL .div type=kotlin.Int operator=DIVEQ <set-?>: CALL .div type=kotlin.Int operator=DIVEQ
$this: CALL .<get-p> type=kotlin.Int operator=DIVEQ $this: CALL .<get-p> type=kotlin.Int operator=DIVEQ
other: CONST Int type=kotlin.Int value='4' other: CONST Int type=kotlin.Int value='4'
BLOCK type=kotlin.Unit operator=PERCEQ
CALL .<set-p> type=kotlin.Unit operator=PERCEQ CALL .<set-p> type=kotlin.Unit operator=PERCEQ
<set-?>: CALL .mod type=kotlin.Int operator=PERCEQ <set-?>: CALL .mod type=kotlin.Int operator=PERCEQ
$this: CALL .<get-p> type=kotlin.Int operator=PERCEQ $this: CALL .<get-p> type=kotlin.Int operator=PERCEQ
@@ -38,18 +38,23 @@ FILE /augmentedAssignment2.kt
s: CONST String type=kotlin.String value='*=' s: CONST String type=kotlin.String value='*='
FUN public fun testProperty(): kotlin.Unit FUN public fun testProperty(): kotlin.Unit
BLOCK_BODY BLOCK_BODY
BLOCK type=kotlin.Unit operator=PLUSEQ
CALL .plusAssign type=kotlin.Unit operator=PLUSEQ CALL .plusAssign type=kotlin.Unit operator=PLUSEQ
$receiver: CALL .<get-p> type=A operator=PLUSEQ $receiver: CALL .<get-p> type=A operator=PLUSEQ
s: CONST String type=kotlin.String value='+=' s: CONST String type=kotlin.String value='+='
BLOCK type=kotlin.Unit operator=MINUSEQ
CALL .minusAssign type=kotlin.Unit operator=MINUSEQ CALL .minusAssign type=kotlin.Unit operator=MINUSEQ
$receiver: CALL .<get-p> type=A operator=MINUSEQ $receiver: CALL .<get-p> type=A operator=MINUSEQ
s: CONST String type=kotlin.String value='-=' s: CONST String type=kotlin.String value='-='
BLOCK type=kotlin.Unit operator=MULTEQ
CALL .timesAssign type=kotlin.Unit operator=MULTEQ CALL .timesAssign type=kotlin.Unit operator=MULTEQ
$receiver: CALL .<get-p> type=A operator=MULTEQ $receiver: CALL .<get-p> type=A operator=MULTEQ
s: CONST String type=kotlin.String value='*=' s: CONST String type=kotlin.String value='*='
BLOCK type=kotlin.Unit operator=DIVEQ
CALL .divAssign type=kotlin.Unit operator=DIVEQ CALL .divAssign type=kotlin.Unit operator=DIVEQ
$receiver: CALL .<get-p> type=A operator=DIVEQ $receiver: CALL .<get-p> type=A operator=DIVEQ
s: CONST String type=kotlin.String value='/=' s: CONST String type=kotlin.String value='/='
BLOCK type=kotlin.Unit operator=PERCEQ
CALL .modAssign type=kotlin.Unit operator=PERCEQ CALL .modAssign type=kotlin.Unit operator=PERCEQ
$receiver: CALL .<get-p> type=A operator=PERCEQ $receiver: CALL .<get-p> type=A operator=PERCEQ
s: CONST String type=kotlin.String value='%=' 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 BLOCK_BODY
RETURN type=kotlin.Nothing from=next RETURN type=kotlin.Nothing from=next
BLOCK type=kotlin.Int operator=POSTFIX_DECR BLOCK type=kotlin.Int operator=POSTFIX_DECR
VAR val tmp0: kotlin.Int 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 CALL .<get-value> type=kotlin.Int operator=POSTFIX_DECR
$this: $RECEIVER of: next type=IntCell $this: GET_VAR tmp0_this type=IntCell operator=null
CALL .<set-value> type=kotlin.Unit operator=POSTFIX_DECR CALL .<set-value> type=kotlin.Unit operator=POSTFIX_DECR
$this: $RECEIVER of: next type=IntCell $this: GET_VAR tmp0_this type=IntCell operator=null
<set-?>: CALL .dec type=kotlin.Int operator=POSTFIX_DECR <set-?>: CALL .dec type=kotlin.Int operator=POSTFIX_DECR
$this: GET_VAR tmp0 type=kotlin.Int operator=null $this: GET_VAR tmp1 type=kotlin.Int operator=null
GET_VAR tmp0 type=kotlin.Int operator=null GET_VAR tmp1 type=kotlin.Int operator=null
FUN public fun IReceiver.test(): kotlin.Unit FUN public fun IReceiver.test(): kotlin.Unit
BLOCK_BODY BLOCK_BODY
BLOCK type=kotlin.Unit operator=FOR_LOOP BLOCK type=kotlin.Unit operator=FOR_LOOP
@@ -52,6 +52,7 @@ FILE /incrementDecrement.kt
FUN public fun testPropPrefix(): kotlin.Unit FUN public fun testPropPrefix(): kotlin.Unit
BLOCK_BODY BLOCK_BODY
VAR val p1: kotlin.Int VAR val p1: kotlin.Int
BLOCK type=kotlin.Int operator=PREFIX_INCR
BLOCK type=kotlin.Int operator=PREFIX_INCR BLOCK type=kotlin.Int operator=PREFIX_INCR
VAR val tmp0: kotlin.Int VAR val tmp0: kotlin.Int
CALL .inc type=kotlin.Int operator=PREFIX_INCR CALL .inc type=kotlin.Int operator=PREFIX_INCR
@@ -60,6 +61,7 @@ FILE /incrementDecrement.kt
<set-?>: GET_VAR tmp0 type=kotlin.Int operator=null <set-?>: GET_VAR tmp0 type=kotlin.Int operator=null
GET_VAR tmp0 type=kotlin.Int operator=null GET_VAR tmp0 type=kotlin.Int operator=null
VAR val p2: kotlin.Int VAR val p2: kotlin.Int
BLOCK type=kotlin.Int operator=PREFIX_DECR
BLOCK type=kotlin.Int operator=PREFIX_DECR BLOCK type=kotlin.Int operator=PREFIX_DECR
VAR val tmp1: kotlin.Int VAR val tmp1: kotlin.Int
CALL .dec type=kotlin.Int operator=PREFIX_DECR CALL .dec type=kotlin.Int operator=PREFIX_DECR
@@ -70,6 +72,7 @@ FILE /incrementDecrement.kt
FUN public fun testPropPostfix(): kotlin.Unit FUN public fun testPropPostfix(): kotlin.Unit
BLOCK_BODY BLOCK_BODY
VAR val p1: kotlin.Int VAR val p1: kotlin.Int
BLOCK type=kotlin.Int operator=POSTFIX_INCR
BLOCK type=kotlin.Int operator=POSTFIX_INCR BLOCK type=kotlin.Int operator=POSTFIX_INCR
VAR val tmp0: kotlin.Int VAR val tmp0: kotlin.Int
CALL .<get-p> type=kotlin.Int operator=POSTFIX_INCR CALL .<get-p> type=kotlin.Int operator=POSTFIX_INCR
@@ -78,6 +81,7 @@ FILE /incrementDecrement.kt
$this: GET_VAR tmp0 type=kotlin.Int operator=null $this: GET_VAR tmp0 type=kotlin.Int operator=null
GET_VAR tmp0 type=kotlin.Int operator=null GET_VAR tmp0 type=kotlin.Int operator=null
VAR val p2: kotlin.Int VAR val p2: kotlin.Int
BLOCK type=kotlin.Int operator=PREFIX_DECR
BLOCK type=kotlin.Int operator=PREFIX_DECR BLOCK type=kotlin.Int operator=PREFIX_DECR
VAR val tmp1: kotlin.Int VAR val tmp1: kotlin.Int
CALL .dec type=kotlin.Int operator=PREFIX_DECR CALL .dec type=kotlin.Int operator=PREFIX_DECR
@@ -41,14 +41,17 @@ FILE /safeCallWithIncrementDecrement.kt
arg1: CONST Null type=kotlin.Nothing? value='null' arg1: CONST Null type=kotlin.Nothing? value='null'
then: CONST Null type=kotlin.Nothing? value='null' then: CONST Null type=kotlin.Nothing? value='null'
else: BLOCK type=kotlin.Int operator=POSTFIX_INCR else: BLOCK type=kotlin.Int operator=POSTFIX_INCR
VAR val tmp1: kotlin.Int 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 CALL .<get-p> type=kotlin.Int operator=POSTFIX_INCR
$this: GET_VAR tmp0_safe_receiver type=test.C? operator=null $this: GET_VAR tmp1_this type=test.C? operator=null
CALL .<set-p> type=kotlin.Unit operator=POSTFIX_INCR CALL .<set-p> type=kotlin.Unit operator=POSTFIX_INCR
$this: GET_VAR tmp0_safe_receiver type=test.C? operator=null $this: GET_VAR tmp1_this type=test.C? operator=null
value: CALL .inc type=kotlin.Int? operator=POSTFIX_INCR value: CALL .inc type=kotlin.Int? operator=POSTFIX_INCR
$receiver: GET_VAR tmp1 type=kotlin.Int operator=null $receiver: GET_VAR tmp2 type=kotlin.Int operator=null
GET_VAR tmp1 type=kotlin.Int operator=null GET_VAR tmp2 type=kotlin.Int operator=null
FUN public fun testArrayAccess(/*0*/ nc: test.C?): kotlin.Unit FUN public fun testArrayAccess(/*0*/ nc: test.C?): kotlin.Unit
BLOCK_BODY BLOCK_BODY
BLOCK type=kotlin.Int operator=POSTFIX_INCR BLOCK type=kotlin.Int operator=POSTFIX_INCR
@@ -322,6 +322,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
doTest(fileName); 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") @TestMetadata("conventionComparisons.kt")
public void testConventionComparisons() throws Exception { public void testConventionComparisons() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/conventionComparisons.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/conventionComparisons.kt");