[FIR] Fix exponential analysis of augmented array access calls

^KT-50861 Fixed
This commit is contained in:
Dmitriy Novozhilov
2022-03-07 10:40:14 +03:00
committed by teamcity
parent c4735f9f29
commit ade2307345
40 changed files with 919 additions and 288 deletions
@@ -0,0 +1,201 @@
FILE: arraySetWithPlusAssign.kt
public final class A : R|kotlin/Any| {
public constructor(): R|A| {
super<R|kotlin/Any|>()
}
public final operator fun get(i: R|kotlin/Int|): R|A| {
^get this@R|/A|
}
public final operator fun plusAssign(v: R|() -> kotlin/Unit|): R|kotlin/Unit| {
}
}
public final fun test_1(x: R|A|): R|kotlin/Unit| {
R|<local>/x|.R|/A.get|(Int(1)).R|/A.plusAssign|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/A.get|(Int(1)).R|/A.plusAssign|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
Q|kotlin/Unit|
}
)
}
)
}
public final class B : R|kotlin/Any| {
public constructor(): R|B| {
super<R|kotlin/Any|>()
}
public final operator fun get(i: R|kotlin/Int|): R|B| {
^get this@R|/B|
}
public final operator fun plusAssign(a: R|() -> kotlin/Unit|): R|kotlin/Unit| {
}
public final operator fun plus(x: R|kotlin/String|): R|kotlin/Unit| {
}
}
public final fun test_2(x: R|B|): R|kotlin/Unit| {
R|<local>/x|.R|/B.get|(Int(1)).R|/B.plusAssign|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/B.get|(Int(1)).R|/B.plusAssign|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
Q|kotlin/Unit|
}
)
}
)
}
public final class C : R|kotlin/Any| {
public constructor(): R|C| {
super<R|kotlin/Any|>()
}
public final operator fun get(i: R|kotlin/Int|): R|C| {
^get this@R|/C|
}
public final operator fun set(i: R|kotlin/Int|, v: R|kotlin/String|): R|kotlin/Unit| {
}
public final operator fun plusAssign(a: R|() -> kotlin/Unit|): R|kotlin/Unit| {
}
public final operator fun plus(v: R|() -> kotlin/Unit|): R|kotlin/Unit| {
}
}
public final fun test_3(x: R|C|): R|kotlin/Unit| {
R|<local>/x|.R|/C.get|(Int(1)).R|/C.plusAssign|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/C.get|(Int(1)).R|/C.plusAssign|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
Q|kotlin/Unit|
}
)
}
)
}
public final class D : R|kotlin/Any| {
public constructor(): R|D| {
super<R|kotlin/Any|>()
}
public final operator fun set(i: R|kotlin/Int|, x: R|D|): R|kotlin/Unit| {
}
public final operator fun get(i: R|kotlin/Int|): R|D| {
^get this@R|/D|
}
public final operator fun plusAssign(x: R|() -> kotlin/Unit|): R|kotlin/Unit| {
}
public final operator fun plus(v: R|() -> kotlin/Unit|): R|D| {
^plus this@R|/D|
}
}
public final fun test_4(x: R|D|): R|kotlin/Unit| {
R|<local>/x|.R|/D.get|(Int(1)).<Operator overload ambiguity. Compatible candidates: [/D.plusAssign, /D.set]>#(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/D.get|(Int(1)).<Operator overload ambiguity. Compatible candidates: [/D.plusAssign, /D.set]>#(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
Q|kotlin/Unit|
}
)
}
)
}
public final class E : R|kotlin/Any| {
public constructor(): R|E| {
super<R|kotlin/Any|>()
}
}
public final fun test_5(x: R|E|): R|kotlin/Unit| {
R|<local>/x|.<Unresolved name: get>#(Int(1)).<Unresolved name: +=>#(fun <anonymous>(): <ERROR TYPE REF: Unresolved name: plusAssign> <inline=Unknown> {
R|/someCallInsideLambda|()
^ R|<local>/x|.<Unresolved name: get>#(Int(1)).<Unresolved name: +=>#(fun <anonymous>(): R|kotlin/Unit| <inline=Unknown> {
R|/someCallInsideLambda|()
Q|kotlin/Unit|
}
)
}
)
}
public final class F : R|kotlin/Any| {
public constructor(): R|F| {
super<R|kotlin/Any|>()
}
public final operator fun set(i: R|kotlin/Int|, x: R|F|): R|kotlin/Unit| {
}
public final operator fun get(i: R|kotlin/Int|): R|F| {
^get this@R|/F|
}
public final operator fun plusAssign(x: R|() -> kotlin/String|): R|kotlin/Unit| {
}
public final operator fun plus(v: R|() -> kotlin/Int|): R|F| {
^plus this@R|/F|
}
}
public final fun test_6(x: R|F|): R|kotlin/Unit| {
R|<local>/x|.R|/F.get|(Int(1)).<Operator overload ambiguity. Compatible candidates: [/F.plusAssign, /F.set]>#(fun <anonymous>(): R|kotlin/String| <inline=NoInline> {
R|/someCallInsideLambda|()
^ String(please choose String)
}
)
}
public final class G : R|kotlin/Any| {
public constructor(): R|G| {
super<R|kotlin/Any|>()
}
public final operator fun get(i: R|kotlin/Int|, j: R|kotlin/Int|, k: R|kotlin/Int|): R|G| {
^get this@R|/G|
}
public final operator fun set(i: R|kotlin/Int|, j: R|kotlin/Int|, k: R|kotlin/Int|, x: R|G|): R|kotlin/Unit| {
}
public final operator fun plus(v: R|() -> kotlin/Unit|): R|G| {
^plus this@R|/G|
}
}
public final fun test_7(x: R|G|): R|kotlin/Unit| {
{
lval <<array>>: R|G| = R|<local>/x|
lval <<index_0>>: R|kotlin/Int| = Int(1)
lval <<index_1>>: R|kotlin/Int| = Int(2)
lval <<index_2>>: R|kotlin/Int| = Int(3)
R|<local>/<<array>>|.R|/G.set|(R|<local>/<<index_0>>|, R|<local>/<<index_1>>|, R|<local>/<<index_2>>|, R|<local>/<<array>>|.R|/G.get|(R|<local>/<<index_0>>|, R|<local>/<<index_1>>|, R|<local>/<<index_2>>|).R|/G.plus|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
{
lval <<array>>: R|G| = R|<local>/x|
lval <<index_0>>: R|kotlin/Int| = Int(1)
lval <<index_1>>: R|kotlin/Int| = Int(2)
lval <<index_2>>: R|kotlin/Int| = Int(3)
R|<local>/<<array>>|.R|/G.set|(R|<local>/<<index_0>>|, R|<local>/<<index_1>>|, R|<local>/<<index_2>>|, R|<local>/<<array>>|.R|/G.get|(R|<local>/<<index_0>>|, R|<local>/<<index_1>>|, R|<local>/<<index_2>>|).R|/G.plus|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
Q|kotlin/Unit|
}
))
}
}
))
}
}
public final fun someCallInsideLambda(): R|kotlin/Unit| {
}
@@ -0,0 +1,125 @@
// ISSUE: KT-50861
/*
* a[b] += c desugars to:
*
* 1. a.get(b).plusAssign(c)
* 2. a.set(b, a.get(b).plus(c))
*/
// only plusAssign, no set
class A {
operator fun get(i: Int): A = this
operator fun plusAssign(v: () -> Unit) {}
}
fun test_1(x: A) {
x[1] += {
someCallInsideLambda()
x[1] += {
someCallInsideLambda()
Unit
}
}
}
// only plusAssign, plus doesn't fit
class B {
operator fun get(i: Int): B = this
operator fun plusAssign(a: () -> Unit) {}
operator fun plus(x: String) {}
}
fun test_2(x: B) {
x[1] += {
someCallInsideLambda()
x[1] += {
someCallInsideLambda()
Unit
}
}
}
// only plusAssign, set doesn't fit
class C {
operator fun get(i: Int): C = this
operator fun set(i: Int, v: String) {}
operator fun plusAssign(a: () -> Unit) {}
operator fun plus(v: () -> Unit) {}
}
fun test_3(x: C) {
x[1] += {
someCallInsideLambda()
x[1] += {
someCallInsideLambda()
Unit
}
}
}
// both fit
class D {
operator fun set(i: Int, x: D) {}
operator fun get(i: Int): D = this
operator fun plusAssign(x: () -> Unit) {}
operator fun plus(v: () -> Unit): D = this
}
fun test_4(x: D) {
x[1] <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> {
someCallInsideLambda()
x[1] <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> {
someCallInsideLambda()
Unit
}
}
}
// nothing fit
class E
fun test_5(x: E) {
x<!NO_GET_METHOD!>[1]<!> += {
someCallInsideLambda()
x<!NO_GET_METHOD!>[1]<!> += {
someCallInsideLambda()
Unit
}
}
}
// only plus, plusAssign doesn't fir because of lambda
class F {
operator fun set(i: Int, x: F) {}
operator fun get(i: Int): F = this
operator fun plusAssign(x: () -> String) {}
operator fun plus(v: () -> Int): F = this
}
fun test_6(x: F) {
x[1] <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> {
someCallInsideLambda()
"please choose String"
}
}
// only plus, no set. 3 indices in get/set
class G {
operator fun get(i: Int, j: Int, k: Int): G = this
operator fun set(i: Int, j: Int, k: Int, x: G) {}
operator fun plus(v: () -> Unit): G = this
}
fun test_7(x: G) {
x[1, 2, 3] += {
someCallInsideLambda()
x[1, 2, 3] += {
someCallInsideLambda()
Unit
}
}
}
fun someCallInsideLambda() {}
@@ -0,0 +1,162 @@
FILE: exponentialArraySetWithPlusAssign.kt
public final class A : R|kotlin/Any| {
public constructor(): R|A| {
super<R|kotlin/Any|>()
}
public final operator fun get(i: R|kotlin/Int|): R|A| {
^get this@R|/A|
}
public final operator fun plusAssign(v: R|() -> kotlin/Unit|): R|kotlin/Unit| {
}
}
public final fun test_1(x: R|A|): R|kotlin/Unit| {
R|<local>/x|.R|/A.get|(Int(1)).R|/A.plusAssign|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/A.get|(Int(1)).R|/A.plusAssign|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/A.get|(Int(1)).R|/A.plusAssign|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/A.get|(Int(1)).R|/A.plusAssign|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/A.get|(Int(1)).R|/A.plusAssign|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/A.get|(Int(1)).R|/A.plusAssign|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/A.get|(Int(1)).R|/A.plusAssign|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/A.get|(Int(1)).R|/A.plusAssign|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/A.get|(Int(1)).R|/A.plusAssign|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/A.get|(Int(1)).R|/A.plusAssign|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/A.get|(Int(1)).R|/A.plusAssign|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/A.get|(Int(1)).R|/A.plusAssign|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/A.get|(Int(1)).R|/A.plusAssign|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/A.get|(Int(1)).R|/A.plusAssign|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/A.get|(Int(1)).R|/A.plusAssign|(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
Q|kotlin/Unit|
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
public final class D : R|kotlin/Any| {
public constructor(): R|D| {
super<R|kotlin/Any|>()
}
public final operator fun set(i: R|kotlin/Int|, x: R|D|): R|kotlin/Unit| {
}
public final operator fun get(i: R|kotlin/Int|): R|D| {
^get this@R|/D|
}
public final operator fun plusAssign(x: R|() -> kotlin/Unit|): R|kotlin/Unit| {
}
public final operator fun plus(v: R|() -> kotlin/Unit|): R|D| {
^plus this@R|/D|
}
}
public final fun test_2(x: R|D|): R|kotlin/Unit| {
R|<local>/x|.R|/D.get|(Int(1)).<Operator overload ambiguity. Compatible candidates: [/D.plusAssign, /D.set]>#(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/D.get|(Int(1)).<Operator overload ambiguity. Compatible candidates: [/D.plusAssign, /D.set]>#(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/D.get|(Int(1)).<Operator overload ambiguity. Compatible candidates: [/D.plusAssign, /D.set]>#(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/D.get|(Int(1)).<Operator overload ambiguity. Compatible candidates: [/D.plusAssign, /D.set]>#(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/D.get|(Int(1)).<Operator overload ambiguity. Compatible candidates: [/D.plusAssign, /D.set]>#(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/D.get|(Int(1)).<Operator overload ambiguity. Compatible candidates: [/D.plusAssign, /D.set]>#(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/D.get|(Int(1)).<Operator overload ambiguity. Compatible candidates: [/D.plusAssign, /D.set]>#(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/D.get|(Int(1)).<Operator overload ambiguity. Compatible candidates: [/D.plusAssign, /D.set]>#(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/D.get|(Int(1)).<Operator overload ambiguity. Compatible candidates: [/D.plusAssign, /D.set]>#(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/D.get|(Int(1)).<Operator overload ambiguity. Compatible candidates: [/D.plusAssign, /D.set]>#(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/D.get|(Int(1)).<Operator overload ambiguity. Compatible candidates: [/D.plusAssign, /D.set]>#(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/D.get|(Int(1)).<Operator overload ambiguity. Compatible candidates: [/D.plusAssign, /D.set]>#(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/D.get|(Int(1)).<Operator overload ambiguity. Compatible candidates: [/D.plusAssign, /D.set]>#(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/D.get|(Int(1)).<Operator overload ambiguity. Compatible candidates: [/D.plusAssign, /D.set]>#(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
R|<local>/x|.R|/D.get|(Int(1)).<Operator overload ambiguity. Compatible candidates: [/D.plusAssign, /D.set]>#(fun <anonymous>(): R|kotlin/Unit| <inline=NoInline> {
R|/someCallInsideLambda|()
Q|kotlin/Unit|
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
)
}
public final fun someCallInsideLambda(): R|kotlin/Unit| {
}
@@ -0,0 +1,116 @@
// ISSUE: KT-50861
// only plusAssign, no set
class A {
operator fun get(i: Int): A = this
operator fun plusAssign(v: () -> Unit) {}
}
fun test_1(x: A) {
x[1] += {
someCallInsideLambda()
x[1] += {
someCallInsideLambda()
x[1] += {
someCallInsideLambda()
x[1] += {
someCallInsideLambda()
x[1] += {
someCallInsideLambda()
x[1] += {
someCallInsideLambda()
x[1] += {
someCallInsideLambda()
x[1] += {
someCallInsideLambda()
x[1] += {
someCallInsideLambda()
x[1] += {
someCallInsideLambda()
x[1] += {
someCallInsideLambda()
x[1] += {
someCallInsideLambda()
x[1] += {
someCallInsideLambda()
x[1] += {
someCallInsideLambda()
x[1] += {
someCallInsideLambda()
Unit
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
// both fit
class D {
operator fun set(i: Int, x: D) {}
operator fun get(i: Int): D = this
operator fun plusAssign(x: () -> Unit) {}
operator fun plus(v: () -> Unit): D = this
}
fun test_2(x: D) {
x[1] <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> {
someCallInsideLambda()
x[1] <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> {
someCallInsideLambda()
x[1] <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> {
someCallInsideLambda()
x[1] <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> {
someCallInsideLambda()
x[1] <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> {
someCallInsideLambda()
x[1] <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> {
someCallInsideLambda()
x[1] <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> {
someCallInsideLambda()
x[1] <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> {
someCallInsideLambda()
x[1] <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> {
someCallInsideLambda()
x[1] <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> {
someCallInsideLambda()
x[1] <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> {
someCallInsideLambda()
x[1] <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> {
someCallInsideLambda()
x[1] <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> {
someCallInsideLambda()
x[1] <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> {
someCallInsideLambda()
x[1] <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> {
someCallInsideLambda()
Unit
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
}
fun someCallInsideLambda() {}