KT-28456 generate index arguments per expression

In the desugaring for compound assignment to a collection element,
argument expression 'i' is mapped to value parameters 'iG' and 'iS' of
corresponding 'get' and 'set' operators.
In general, these value parameters can have different indices.

This requires extra machinery in argument generation - that is, to be
able to generate a particular expression argument using an arbitrary
callback. In the vast majority of the cases this callback will just use
the corresponding StatementGenerator to generate IR subtree for the
provided expression. In case of 'get' and 'set' operator calls for an
augmented assignment expression this will map corresponding argument
expressions to pregenerated temporary variables.

Thus, in the following context:
```
  class A

  operator fun A.get(vararg xs: Int) = 0
  operator fun A.set(i: Int, j: Int, v: Int) {}
```

statement `a[1, 2] += 3` will be desugared as (in a really pseudo
Kotlin):
```
  {
    val tmp_array = a
    val tmp_index0 = 1
    val tmp_index1 = 2
    tmp_array.set(
      i = tmp_index0,
      j = tmp_index1,
      v = tmp_array.get(xs = [tmp_index0, tmp_index1]).plus(3)
    )
  }
```
This commit is contained in:
Dmitry Petrov
2018-11-27 12:03:22 +03:00
parent 036b12f408
commit 42e253b5ff
10 changed files with 311 additions and 19 deletions
+15
View File
@@ -0,0 +1,15 @@
class A
operator fun A.get(vararg xs: Int) = 0
operator fun A.set(i: Int, j: Int, v: Int) {}
fun testSimpleAssignment(a: A) {
a[1, 2] = 0
}
fun testPostfixIncrement(a: A) = a[1, 2]++
fun testCompoundAssignment(a: A) {
a[1, 2] += 10
}
+85
View File
@@ -0,0 +1,85 @@
FILE fqName:<root> fileName:/kt28456.kt
CLASS CLASS name:A modality:FINAL visibility:public flags: superTypes:[kotlin.Any]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:A flags:
CONSTRUCTOR visibility:public <> () returnType:A flags:primary
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'constructor Any()'
INSTANCE_INITIALIZER_CALL classDescriptor='A'
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags:
overridden:
FUN IR_EXTERNAL_DECLARATION_STUB name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags:
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
VALUE_PARAMETER name:other index:0 type:kotlin.Any? flags:
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags:
overridden:
FUN IR_EXTERNAL_DECLARATION_STUB name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags:
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags:
overridden:
FUN IR_EXTERNAL_DECLARATION_STUB name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags:
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
FUN name:get visibility:public modality:FINAL <> ($receiver:A, xs:kotlin.IntArray) returnType:kotlin.Int flags:
$receiver: VALUE_PARAMETER name:<this> type:A flags:
VALUE_PARAMETER name:xs index:0 type:kotlin.IntArray varargElementType:kotlin.Int flags:vararg
BLOCK_BODY
RETURN type=kotlin.Nothing from='get(vararg Int) on A: Int'
CONST Int type=kotlin.Int value=0
FUN name:set visibility:public modality:FINAL <> ($receiver:A, i:kotlin.Int, j:kotlin.Int, v:kotlin.Int) returnType:kotlin.Unit flags:
$receiver: VALUE_PARAMETER name:<this> type:A flags:
VALUE_PARAMETER name:i index:0 type:kotlin.Int flags:
VALUE_PARAMETER name:j index:1 type:kotlin.Int flags:
VALUE_PARAMETER name:v index:2 type:kotlin.Int flags:
BLOCK_BODY
FUN name:testSimpleAssignment visibility:public modality:FINAL <> (a:A) returnType:kotlin.Unit flags:
VALUE_PARAMETER name:a index:0 type:A flags:
BLOCK_BODY
CALL 'set(Int, Int, Int) on A: Unit' type=kotlin.Unit origin=EQ
$receiver: GET_VAR 'value-parameter a: A' type=A origin=null
i: CONST Int type=kotlin.Int value=1
j: CONST Int type=kotlin.Int value=2
v: CONST Int type=kotlin.Int value=0
FUN name:testPostfixIncrement visibility:public modality:FINAL <> (a:A) returnType:kotlin.Int flags:
VALUE_PARAMETER name:a index:0 type:A flags:
BLOCK_BODY
RETURN type=kotlin.Nothing from='testPostfixIncrement(A): Int'
BLOCK type=kotlin.Int origin=POSTFIX_INCR
VAR IR_TEMPORARY_VARIABLE name:tmp0_array type:A flags:val
GET_VAR 'value-parameter a: A' type=A origin=null
VAR IR_TEMPORARY_VARIABLE name:tmp1_index0 type:kotlin.Int flags:val
CONST Int type=kotlin.Int value=1
VAR IR_TEMPORARY_VARIABLE name:tmp2_index1 type:kotlin.Int flags:val
CONST Int type=kotlin.Int value=2
VAR IR_TEMPORARY_VARIABLE name:tmp3 type:kotlin.Int flags:val
CALL 'get(vararg Int) on A: Int' type=kotlin.Int origin=POSTFIX_INCR
$receiver: GET_VAR 'tmp0_array: A' type=A origin=null
xs: VARARG type=kotlin.IntArray varargElementType=kotlin.Int
GET_VAR 'tmp1_index0: Int' type=kotlin.Int origin=null
GET_VAR 'tmp2_index1: Int' type=kotlin.Int origin=null
CALL 'set(Int, Int, Int) on A: Unit' type=kotlin.Unit origin=POSTFIX_INCR
$receiver: GET_VAR 'tmp0_array: A' type=A origin=null
i: GET_VAR 'tmp1_index0: Int' type=kotlin.Int origin=null
j: GET_VAR 'tmp2_index1: Int' type=kotlin.Int origin=null
v: CALL 'inc(): Int' type=kotlin.Int origin=POSTFIX_INCR
$this: GET_VAR 'tmp3: Int' type=kotlin.Int origin=null
GET_VAR 'tmp3: Int' type=kotlin.Int origin=null
FUN name:testCompoundAssignment visibility:public modality:FINAL <> (a:A) returnType:kotlin.Unit flags:
VALUE_PARAMETER name:a index:0 type:A flags:
BLOCK_BODY
BLOCK type=kotlin.Unit origin=PLUSEQ
VAR IR_TEMPORARY_VARIABLE name:tmp0_array type:A flags:val
GET_VAR 'value-parameter a: A' type=A origin=null
VAR IR_TEMPORARY_VARIABLE name:tmp1_index0 type:kotlin.Int flags:val
CONST Int type=kotlin.Int value=1
VAR IR_TEMPORARY_VARIABLE name:tmp2_index1 type:kotlin.Int flags:val
CONST Int type=kotlin.Int value=2
CALL 'set(Int, Int, Int) on A: Unit' type=kotlin.Unit origin=PLUSEQ
$receiver: GET_VAR 'tmp0_array: A' type=A origin=null
i: GET_VAR 'tmp1_index0: Int' type=kotlin.Int origin=null
j: GET_VAR 'tmp2_index1: Int' type=kotlin.Int origin=null
v: CALL 'plus(Int): Int' type=kotlin.Int origin=PLUSEQ
$this: CALL 'get(vararg Int) on A: Int' type=kotlin.Int origin=PLUSEQ
$receiver: GET_VAR 'tmp0_array: A' type=A origin=null
xs: VARARG type=kotlin.IntArray varargElementType=kotlin.Int
GET_VAR 'tmp1_index0: Int' type=kotlin.Int origin=null
GET_VAR 'tmp2_index1: Int' type=kotlin.Int origin=null
other: CONST Int type=kotlin.Int value=10
+7
View File
@@ -0,0 +1,7 @@
class A
operator fun A.set(vararg i: Int, v: Int) {}
fun testSimpleAssignment(a: A) {
a[1, 2, 3] = 0
}
+35
View File
@@ -0,0 +1,35 @@
FILE fqName:<root> fileName:/kt28456a.kt
CLASS CLASS name:A modality:FINAL visibility:public flags: superTypes:[kotlin.Any]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:A flags:
CONSTRUCTOR visibility:public <> () returnType:A flags:primary
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'constructor Any()'
INSTANCE_INITIALIZER_CALL classDescriptor='A'
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags:
overridden:
FUN IR_EXTERNAL_DECLARATION_STUB name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags:
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
VALUE_PARAMETER name:other index:0 type:kotlin.Any? flags:
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags:
overridden:
FUN IR_EXTERNAL_DECLARATION_STUB name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags:
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags:
overridden:
FUN IR_EXTERNAL_DECLARATION_STUB name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags:
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
FUN name:set visibility:public modality:FINAL <> ($receiver:A, i:kotlin.IntArray, v:kotlin.Int) returnType:kotlin.Unit flags:
$receiver: VALUE_PARAMETER name:<this> type:A flags:
VALUE_PARAMETER name:i index:0 type:kotlin.IntArray varargElementType:kotlin.Int flags:vararg
VALUE_PARAMETER name:v index:1 type:kotlin.Int flags:
BLOCK_BODY
FUN name:testSimpleAssignment visibility:public modality:FINAL <> (a:A) returnType:kotlin.Unit flags:
VALUE_PARAMETER name:a index:0 type:A flags:
BLOCK_BODY
CALL 'set(vararg Int, Int) on A: Unit' type=kotlin.Unit origin=EQ
$receiver: GET_VAR 'value-parameter a: A' type=A origin=null
i: VARARG type=kotlin.IntArray varargElementType=kotlin.Int
CONST Int type=kotlin.Int value=1
CONST Int type=kotlin.Int value=2
CONST Int type=kotlin.Int value=3
v: CONST Int type=kotlin.Int value=0
+15
View File
@@ -0,0 +1,15 @@
class A
operator fun A.get(i: Int, a: Int = 1, b: Int = 2, c: Int = 3, d: Int = 4) = 0
operator fun A.set(i: Int, j: Int = 42, v: Int) {}
fun testSimpleAssignment(a: A) {
a[1] = 0
}
fun testPostfixIncrement(a: A) = a[1]++
fun testCompoundAssignment(a: A) {
a[1] += 10
}
+88
View File
@@ -0,0 +1,88 @@
FILE fqName:<root> fileName:/kt28456b.kt
CLASS CLASS name:A modality:FINAL visibility:public flags: superTypes:[kotlin.Any]
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:A flags:
CONSTRUCTOR visibility:public <> () returnType:A flags:primary
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'constructor Any()'
INSTANCE_INITIALIZER_CALL classDescriptor='A'
FUN FAKE_OVERRIDE name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags:
overridden:
FUN IR_EXTERNAL_DECLARATION_STUB name:equals visibility:public modality:OPEN <> ($this:kotlin.Any, other:kotlin.Any?) returnType:kotlin.Boolean flags:
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
VALUE_PARAMETER name:other index:0 type:kotlin.Any? flags:
FUN FAKE_OVERRIDE name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags:
overridden:
FUN IR_EXTERNAL_DECLARATION_STUB name:hashCode visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.Int flags:
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
FUN FAKE_OVERRIDE name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags:
overridden:
FUN IR_EXTERNAL_DECLARATION_STUB name:toString visibility:public modality:OPEN <> ($this:kotlin.Any) returnType:kotlin.String flags:
$this: VALUE_PARAMETER name:<this> type:kotlin.Any flags:
FUN name:get visibility:public modality:FINAL <> ($receiver:A, i:kotlin.Int, a:kotlin.Int, b:kotlin.Int, c:kotlin.Int, d:kotlin.Int) returnType:kotlin.Int flags:
$receiver: VALUE_PARAMETER name:<this> type:A flags:
VALUE_PARAMETER name:i index:0 type:kotlin.Int flags:
VALUE_PARAMETER name:a index:1 type:kotlin.Int flags:
EXPRESSION_BODY
CONST Int type=kotlin.Int value=1
VALUE_PARAMETER name:b index:2 type:kotlin.Int flags:
EXPRESSION_BODY
CONST Int type=kotlin.Int value=2
VALUE_PARAMETER name:c index:3 type:kotlin.Int flags:
EXPRESSION_BODY
CONST Int type=kotlin.Int value=3
VALUE_PARAMETER name:d index:4 type:kotlin.Int flags:
EXPRESSION_BODY
CONST Int type=kotlin.Int value=4
BLOCK_BODY
RETURN type=kotlin.Nothing from='get(Int, Int = ..., Int = ..., Int = ..., Int = ...) on A: Int'
CONST Int type=kotlin.Int value=0
FUN name:set visibility:public modality:FINAL <> ($receiver:A, i:kotlin.Int, j:kotlin.Int, v:kotlin.Int) returnType:kotlin.Unit flags:
$receiver: VALUE_PARAMETER name:<this> type:A flags:
VALUE_PARAMETER name:i index:0 type:kotlin.Int flags:
VALUE_PARAMETER name:j index:1 type:kotlin.Int flags:
EXPRESSION_BODY
CONST Int type=kotlin.Int value=42
VALUE_PARAMETER name:v index:2 type:kotlin.Int flags:
BLOCK_BODY
FUN name:testSimpleAssignment visibility:public modality:FINAL <> (a:A) returnType:kotlin.Unit flags:
VALUE_PARAMETER name:a index:0 type:A flags:
BLOCK_BODY
CALL 'set(Int, Int = ..., Int) on A: Unit' type=kotlin.Unit origin=EQ
$receiver: GET_VAR 'value-parameter a: A' type=A origin=null
i: CONST Int type=kotlin.Int value=1
v: CONST Int type=kotlin.Int value=0
FUN name:testPostfixIncrement visibility:public modality:FINAL <> (a:A) returnType:kotlin.Int flags:
VALUE_PARAMETER name:a index:0 type:A flags:
BLOCK_BODY
RETURN type=kotlin.Nothing from='testPostfixIncrement(A): Int'
BLOCK type=kotlin.Int origin=POSTFIX_INCR
VAR IR_TEMPORARY_VARIABLE name:tmp0_array type:A flags:val
GET_VAR 'value-parameter a: A' type=A origin=null
VAR IR_TEMPORARY_VARIABLE name:tmp1_index0 type:kotlin.Int flags:val
CONST Int type=kotlin.Int value=1
VAR IR_TEMPORARY_VARIABLE name:tmp2 type:kotlin.Int flags:val
CALL 'get(Int, Int = ..., Int = ..., Int = ..., Int = ...) on A: Int' type=kotlin.Int origin=POSTFIX_INCR
$receiver: GET_VAR 'tmp0_array: A' type=A origin=null
i: GET_VAR 'tmp1_index0: Int' type=kotlin.Int origin=null
CALL 'set(Int, Int = ..., Int) on A: Unit' type=kotlin.Unit origin=POSTFIX_INCR
$receiver: GET_VAR 'tmp0_array: A' type=A origin=null
i: GET_VAR 'tmp1_index0: Int' type=kotlin.Int origin=null
v: CALL 'inc(): Int' type=kotlin.Int origin=POSTFIX_INCR
$this: GET_VAR 'tmp2: Int' type=kotlin.Int origin=null
GET_VAR 'tmp2: Int' type=kotlin.Int origin=null
FUN name:testCompoundAssignment visibility:public modality:FINAL <> (a:A) returnType:kotlin.Unit flags:
VALUE_PARAMETER name:a index:0 type:A flags:
BLOCK_BODY
BLOCK type=kotlin.Unit origin=PLUSEQ
VAR IR_TEMPORARY_VARIABLE name:tmp0_array type:A flags:val
GET_VAR 'value-parameter a: A' type=A origin=null
VAR IR_TEMPORARY_VARIABLE name:tmp1_index0 type:kotlin.Int flags:val
CONST Int type=kotlin.Int value=1
CALL 'set(Int, Int = ..., Int) on A: Unit' type=kotlin.Unit origin=PLUSEQ
$receiver: GET_VAR 'tmp0_array: A' type=A origin=null
i: GET_VAR 'tmp1_index0: Int' type=kotlin.Int origin=null
v: CALL 'plus(Int): Int' type=kotlin.Int origin=PLUSEQ
$this: CALL 'get(Int, Int = ..., Int = ..., Int = ..., Int = ...) on A: Int' type=kotlin.Int origin=PLUSEQ
$receiver: GET_VAR 'tmp0_array: A' type=A origin=null
i: GET_VAR 'tmp1_index0: Int' type=kotlin.Int origin=null
other: CONST Int type=kotlin.Int value=10