[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
@@ -1,6 +1,6 @@
KT element: KtBinaryExpression
FIR element: FirFunctionCallImpl
FIR source kind: DesugaredCompoundAssignment
FIR source kind: KtRealSourceElementKind
FIR element rendered:
R|<local>/<<array>>|.R|SubstitutionOverride</MyMap.set: R|kotlin/Unit|>|(R|<local>/<<index_0>>|, R|<local>/<<array>>|.R|SubstitutionOverride</MyMap.get: R|kotlin/Int|>|(R|<local>/<<index_0>>|).R|kotlin/Int.plus|(Int(1)))
R|<local>/<<array>>|.R|SubstitutionOverride</MyMap.set: R|kotlin/Unit|>|(R|<local>/<<index_0>>|, R|<local>/<<array>>|.R|SubstitutionOverride</MyMap.get: R|kotlin/Int|>|(R|<local>/<<index_0>>|).R|kotlin/Int.plus|(Int(1)))
@@ -863,6 +863,12 @@ public class DiagnosisCompilerFirTestdataTestGenerated extends AbstractDiagnosis
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/callResolution"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@Test
@TestMetadata("arraySetWithPlusAssign.kt")
public void testArraySetWithPlusAssign() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/arraySetWithPlusAssign.kt");
}
@Test
@TestMetadata("companionInvoke.kt")
public void testCompanionInvoke() throws Exception {
@@ -893,6 +899,12 @@ public class DiagnosisCompilerFirTestdataTestGenerated extends AbstractDiagnosis
runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/errorCandidates.kt");
}
@Test
@TestMetadata("exponentialArraySetWithPlusAssign.kt")
public void testExponentialArraySetWithPlusAssign() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/exponentialArraySetWithPlusAssign.kt");
}
@Test
@TestMetadata("extensionInvokeAfterSafeCall.kt")
public void testExtensionInvokeAfterSafeCall() throws Exception {
@@ -745,6 +745,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/callResolution"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@TestMetadata("arraySetWithPlusAssign.kt")
public void testArraySetWithPlusAssign() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/arraySetWithPlusAssign.kt");
}
@TestMetadata("companionInvoke.kt")
public void testCompanionInvoke() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/companionInvoke.kt");
@@ -770,6 +775,11 @@ public class LazyBodyIsNotTouchedTilContractsPhaseTestGenerated extends Abstract
runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/errorCandidates.kt");
}
@TestMetadata("exponentialArraySetWithPlusAssign.kt")
public void testExponentialArraySetWithPlusAssign() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/exponentialArraySetWithPlusAssign.kt");
}
@TestMetadata("extensionInvokeAfterSafeCall.kt")
public void testExtensionInvokeAfterSafeCall() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/extensionInvokeAfterSafeCall.kt");
@@ -56,8 +56,8 @@ FILE: arraySetWithOperation.kt
}
public final fun test_3(a: R|A<D>|): R|kotlin/Unit| {
ArraySet:[R|<local>/a|.R|SubstitutionOverride</A.get: R|D|>|(Int(0)) += R|/D.D|()]
R|<local>/a|.R|SubstitutionOverride</A.get: R|D|>|(Int(0)).<Operator overload ambiguity. Compatible candidates: [/D.plusAssign, /A.set]>#(R|/D.D|())
}
public final fun test_4(b: R|B|): R|kotlin/Unit| {
ArraySet:[R|<local>/b|.<Unresolved name: get>#(Int(0)) += R|/B.B|()]
R|<local>/b|.<Unresolved name: get>#(Int(0)).<Unresolved name: +=>#(R|/B.B|())
}
@@ -28,9 +28,9 @@ fun test_2(a: A<C>) {
}
fun test_3(a: A<D>) {
a[0] <!OVERLOAD_RESOLUTION_AMBIGUITY!>+=<!> D() // ambiguity
a[0] <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> D() // ambiguity
}
fun test_4(b: B) {
b<!NO_GET_METHOD!>[0]<!> <!UNRESOLVED_REFERENCE!>+=<!> B() // unresolved
b<!NO_GET_METHOD!>[0]<!> += B() // unresolved
}
@@ -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() {}
@@ -5,5 +5,5 @@ class Foo {
fun test() {
var f = Foo()
<!ASSIGN_OPERATOR_AMBIGUITY!>f += f<!>
f <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> f
}
@@ -24,7 +24,7 @@ fun testOperatorAssignment() {
val c = C()
c += ""
<!CAN_BE_VAL!>var<!> c1 = C()
<!ASSIGN_OPERATOR_AMBIGUITY!>c1 += ""<!>
c1 <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> ""
var a = 1
a += 12
@@ -863,6 +863,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/callResolution"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@Test
@TestMetadata("arraySetWithPlusAssign.kt")
public void testArraySetWithPlusAssign() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/arraySetWithPlusAssign.kt");
}
@Test
@TestMetadata("companionInvoke.kt")
public void testCompanionInvoke() throws Exception {
@@ -893,6 +899,12 @@ public class FirDiagnosticTestGenerated extends AbstractFirDiagnosticTest {
runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/errorCandidates.kt");
}
@Test
@TestMetadata("exponentialArraySetWithPlusAssign.kt")
public void testExponentialArraySetWithPlusAssign() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/exponentialArraySetWithPlusAssign.kt");
}
@Test
@TestMetadata("extensionInvokeAfterSafeCall.kt")
public void testExtensionInvokeAfterSafeCall() throws Exception {
@@ -863,6 +863,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/fir/analysis-tests/testData/resolve/callResolution"), Pattern.compile("^([^.]+)\\.kt$"), null, true);
}
@Test
@TestMetadata("arraySetWithPlusAssign.kt")
public void testArraySetWithPlusAssign() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/arraySetWithPlusAssign.kt");
}
@Test
@TestMetadata("companionInvoke.kt")
public void testCompanionInvoke() throws Exception {
@@ -893,6 +899,12 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos
runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/errorCandidates.kt");
}
@Test
@TestMetadata("exponentialArraySetWithPlusAssign.kt")
public void testExponentialArraySetWithPlusAssign() throws Exception {
runTest("compiler/fir/analysis-tests/testData/resolve/callResolution/exponentialArraySetWithPlusAssign.kt");
}
@Test
@TestMetadata("extensionInvokeAfterSafeCall.kt")
public void testExtensionInvokeAfterSafeCall() throws Exception {
@@ -518,7 +518,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val OVERLOAD_RESOLUTION_AMBIGUITY by error<PsiElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
parameter<Collection<Symbol>>("candidates")
}
val ASSIGN_OPERATOR_AMBIGUITY by error<PsiElement> {
val ASSIGN_OPERATOR_AMBIGUITY by error<PsiElement>(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) {
parameter<Collection<Symbol>>("candidates")
}
val ITERATOR_AMBIGUITY by error<PsiElement>(PositioningStrategy.REFERENCE_BY_QUALIFIED) {
@@ -351,7 +351,7 @@ object FirErrors {
// Ambiguity
val OVERLOAD_RESOLUTION_AMBIGUITY by error1<PsiElement, Collection<FirBasedSymbol<*>>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val ASSIGN_OPERATOR_AMBIGUITY by error1<PsiElement, Collection<FirBasedSymbol<*>>>()
val ASSIGN_OPERATOR_AMBIGUITY by error1<PsiElement, Collection<FirBasedSymbol<*>>>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED)
val ITERATOR_AMBIGUITY by error1<PsiElement, Collection<FirBasedSymbol<*>>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val HAS_NEXT_FUNCTION_AMBIGUITY by error1<PsiElement, Collection<FirBasedSymbol<*>>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
val NEXT_AMBIGUITY by error1<PsiElement, Collection<FirBasedSymbol<*>>>(SourceElementPositioningStrategies.REFERENCE_BY_QUALIFIED)
@@ -1058,14 +1058,6 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
null,
ConeSimpleDiagnostic("No value for array set", DiagnosticKind.Syntax)
)
// Second copy of rhs is used because we analyze it twice in different contexts
// and now they should be different expressions instances to make everything work properly.
// But this lead to exponential time already at FIR building stage,
// so we hope this hack will be removed with KT-50861
this.rhs2 = rhs?.convert() ?: buildErrorExpression(
null,
ConeSimpleDiagnostic("No value for array set", DiagnosticKind.Syntax)
)
this.arrayAccessSource = arrayAccessSource
this.annotations += annotations
}
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.resolve.transformers.body.resolve
import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.fakeElement
import org.jetbrains.kotlin.fir.*
@@ -25,7 +26,6 @@ import org.jetbrains.kotlin.fir.references.builder.buildSimpleNamedReference
import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.dfa.coneType
import org.jetbrains.kotlin.fir.resolve.dfa.unwrapSmartcastExpression
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
import org.jetbrains.kotlin.fir.resolve.inference.FirStubInferenceSession
@@ -46,7 +46,6 @@ import org.jetbrains.kotlin.fir.visitors.transformSingle
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.StandardClassIds
import org.jetbrains.kotlin.resolve.calls.inference.buildAbstractResultingSubstitutor
import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.ConstantValueKind
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
@@ -516,28 +515,17 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
assignmentOperatorStatement: FirAssignmentOperatorStatement,
data: ResolutionMode
): FirStatement {
require(assignmentOperatorStatement.operation != FirOperation.ASSIGN)
val operation = assignmentOperatorStatement.operation
require(operation != FirOperation.ASSIGN)
assignmentOperatorStatement.transformAnnotations(transformer, ResolutionMode.ContextIndependent)
val leftArgument = assignmentOperatorStatement.leftArgument.transformSingle(transformer, ResolutionMode.ContextIndependent)
val rightArgument = assignmentOperatorStatement.rightArgument.transformSingle(transformer, ResolutionMode.ContextDependent)
fun createFunctionCall(name: Name): FirFunctionCall = buildFunctionCall {
source = assignmentOperatorStatement.source?.fakeElement(KtFakeSourceElementKind.DesugaredCompoundAssignment)
explicitReceiver = leftArgument
argumentList = buildUnaryArgumentList(rightArgument)
calleeReference = buildSimpleNamedReference {
// TODO: Use source of operator for callee reference source
source = assignmentOperatorStatement.source?.fakeElement(KtFakeSourceElementKind.DesugaredCompoundAssignment)
this.name = name
candidateSymbol = null
}
origin = FirFunctionCallOrigin.Operator
}
val generator = GeneratorOfPlusAssignCalls(assignmentOperatorStatement, operation, leftArgument, rightArgument)
// x.plusAssign(y)
val assignmentOperatorName = FirOperationNameConventions.ASSIGNMENTS.getValue(assignmentOperatorStatement.operation)
val assignOperatorCall = createFunctionCall(assignmentOperatorName)
val assignOperatorCall = generator.createAssignOperatorCall()
val resolvedAssignCall = resolveCandidateForAssignmentOperatorCall {
assignOperatorCall.transformSingle(this, ResolutionMode.ContextDependent)
}
@@ -545,8 +533,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
val assignIsSuccessful = assignCallReference?.isError == false
// x = x + y
val simpleOperatorName = FirOperationNameConventions.ASSIGNMENTS_TO_SIMPLE_OPERATOR.getValue(assignmentOperatorStatement.operation)
val simpleOperatorCall = createFunctionCall(simpleOperatorName)
val simpleOperatorCall = generator.createSimpleOperatorCall()
val resolvedOperatorCall = resolveCandidateForAssignmentOperatorCall {
simpleOperatorCall.transformSingle(this, ResolutionMode.ContextDependent)
}
@@ -1118,90 +1105,155 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
}
}
private class GeneratorOfPlusAssignCalls(
val baseElement: FirStatement,
val operation: FirOperation,
val lhs: FirExpression,
val rhs: FirExpression
) {
companion object {
fun createFunctionCall(
name: Name,
source: KtSourceElement?,
receiver: FirExpression,
vararg arguments: FirExpression
): FirFunctionCall = buildFunctionCall {
this.source = source
explicitReceiver = receiver
argumentList = when(arguments.size) {
0 -> FirEmptyArgumentList
1 -> buildUnaryArgumentList(arguments.first())
else -> buildArgumentList {
this.arguments.addAll(arguments)
}
}
calleeReference = buildSimpleNamedReference {
// TODO: Use source of operator for callee reference source
this.source = source
this.name = name
candidateSymbol = null
}
origin = FirFunctionCallOrigin.Operator
}
}
private fun createFunctionCall(name: Name): FirFunctionCall {
return createFunctionCall(name, baseElement.source?.fakeElement(KtFakeSourceElementKind.DesugaredCompoundAssignment), lhs, rhs)
}
fun createAssignOperatorCall(): FirFunctionCall {
return createFunctionCall(FirOperationNameConventions.ASSIGNMENTS.getValue(operation))
}
fun createSimpleOperatorCall(): FirFunctionCall {
return createFunctionCall(FirOperationNameConventions.ASSIGNMENTS_TO_SIMPLE_OPERATOR.getValue(operation))
}
}
@OptIn(ExperimentalStdlibApi::class)
override fun transformAugmentedArraySetCall(
augmentedArraySetCall: FirAugmentedArraySetCall,
data: ResolutionMode
): FirStatement {
assert(augmentedArraySetCall.operation in FirOperation.ASSIGNMENTS)
assert(augmentedArraySetCall.operation != FirOperation.ASSIGN)
/*
* a[b] += c can be desugared to:
*
* 1. a.get(b).plusAssign(c)
* 2. a.set(b, a.get(b).plus(c))
*/
val operation = augmentedArraySetCall.operation
assert(operation in FirOperation.ASSIGNMENTS)
assert(operation != FirOperation.ASSIGN)
augmentedArraySetCall.transformAnnotations(transformer, data)
val operatorName = FirOperationNameConventions.ASSIGNMENTS.getValue(augmentedArraySetCall.operation)
val transformedLhsCall =
augmentedArraySetCall.lhsGetCall.transformSingle(transformer, ResolutionMode.ContextIndependent).takeIf { it.isSuccessful() }
// transformedLhsCall: a.get(index)
val transformedLhsCall = augmentedArraySetCall.lhsGetCall.transformSingle(transformer, ResolutionMode.ContextIndependent)
val transformedRhs = augmentedArraySetCall.rhs.transformSingle(transformer, ResolutionMode.ContextDependent)
val blockForGetSetVersion: FirBlock? =
transformedLhsCall?.let { augmentedArraySetCall.tryResolveAugmentedArraySetCallAsSetGetBlock(it) }
val assignResolvedCall: FirFunctionCall? =
transformedLhsCall?.let { augmentedArraySetCall.tryResolveWithOperatorAssignConvention(it) }
val generator = GeneratorOfPlusAssignCalls(augmentedArraySetCall, operation, transformedLhsCall, transformedRhs)
val result: FirStatement = when {
assignResolvedCall != null && blockForGetSetVersion != null -> {
augmentedArraySetCall.also {
it.replaceCalleeReference(
buildErrorNamedReference {
// TODO: add better diagnostic
source = augmentedArraySetCall.source
diagnostic = ConeAmbiguityError(operatorName, CandidateApplicability.RESOLVED, emptyList())
}
)
}
}
blockForGetSetVersion != null -> {
//checking secondResult leave erroneous nodes in dfa graph,
//we add another block so final type of expression will be correct
//todo replace this hack with proper graph cleaning
transformer.components.dataFlowAnalyzer.enterBlock(blockForGetSetVersion)
transformer.components.dataFlowAnalyzer.exitBlock(blockForGetSetVersion)
blockForGetSetVersion
}
assignResolvedCall != null -> assignResolvedCall
else -> {
augmentedArraySetCall.rhs.transformSingle(transformer, ResolutionMode.ContextIndependent)
augmentedArraySetCall.rhs2.transformSingle(transformer, ResolutionMode.ContextIndependent)
augmentedArraySetCall.also {
it.replaceCalleeReference(
buildErrorNamedReference {
source = augmentedArraySetCall.source
diagnostic = ConeUnresolvedNameError(operatorName)
}
)
}
}
// a.get(b).plusAssign(c)
val assignOperatorCall = generator.createAssignOperatorCall()
val resolvedAssignCall = resolveCandidateForAssignmentOperatorCall {
assignOperatorCall.transformSingle(this, ResolutionMode.ContextDependent)
}
return result
}
val assignCallReference = resolvedAssignCall.calleeReference as? FirNamedReferenceWithCandidate
val assignIsSuccessful = assignCallReference?.isError == false
private fun FirFunctionCall.isSuccessful(): Boolean =
typeRef !is FirErrorTypeRef && calleeReference is FirResolvedNamedReference
// <array>.set(<index_i>, <array>.get(<index_i>).plus(c))
val info = tryResolveAugmentedArraySetCallAsSetGetBlock(augmentedArraySetCall, transformedLhsCall, transformedRhs)
val resolvedOperatorCall = info.operatorCall
val operatorCallReference = resolvedOperatorCall.calleeReference as? FirNamedReferenceWithCandidate
val operatorIsSuccessful = operatorCallReference?.isError == false
/**
* Desugarings of a[x, y] += z to
* a.get(x, y).plusAssign(z)
*
* @return null if `plusAssign` is unresolved
* @return block defined as described above, otherwise
*/
private fun FirAugmentedArraySetCall.tryResolveWithOperatorAssignConvention(lhsGetCall: FirFunctionCall): FirFunctionCall? {
val assignCall = buildFunctionCall {
source = this@tryResolveWithOperatorAssignConvention.source?.fakeElement(KtFakeSourceElementKind.DesugaredCompoundAssignment)
calleeReference = buildSimpleNamedReference {
name = FirOperationNameConventions.ASSIGNMENTS.getValue(operation)
}
explicitReceiver = lhsGetCall
argumentList = buildArgumentList {
arguments += rhs2
}
origin = FirFunctionCallOrigin.Operator
annotations += this@tryResolveWithOperatorAssignConvention.annotations
fun completeAssignCall() {
dataFlowAnalyzer.enterFunctionCall(resolvedAssignCall)
callCompleter.completeCall(resolvedAssignCall, noExpectedType)
dataFlowAnalyzer.exitFunctionCall(resolvedAssignCall, callCompleted = true)
}
val transformedCall = assignCall.transformSingle(transformer, ResolutionMode.ContextIndependent)
return transformedCall.takeIf { it.isSuccessful() }
fun chooseAssign(): FirStatement {
completeAssignCall()
return resolvedAssignCall
}
// if `plus` call already inapplicable then there is no need to try to resolve `set` call
if (assignIsSuccessful && !operatorIsSuccessful) {
return chooseAssign()
}
// a.set(b, a.get(b).plus(c))
val resolvedSetCall = info.setCall
val setCallReference = resolvedSetCall.calleeReference as? FirNamedReferenceWithCandidate
val setIsSuccessful = setCallReference?.isError == false
fun chooseSetOperator(): FirStatement {
dataFlowAnalyzer.enterFunctionCall(resolvedSetCall)
dataFlowAnalyzer.enterFunctionCall(resolvedOperatorCall)
callCompleter.completeCall(
resolvedSetCall,
noExpectedType,
expectedTypeMismatchIsReportedInChecker = true
)
dataFlowAnalyzer.exitFunctionCall(resolvedOperatorCall, callCompleted = true)
dataFlowAnalyzer.exitFunctionCall(resolvedSetCall, callCompleted = true)
return info.toBlock()
}
fun reportError(diagnostic: ConeDiagnostic): FirStatement {
completeAssignCall()
val errorReference = buildErrorNamedReference {
source = augmentedArraySetCall.source
this.diagnostic = diagnostic
}
resolvedAssignCall.replaceCalleeReference(errorReference)
return resolvedAssignCall
}
fun reportAmbiguity(
firstReference: FirNamedReferenceWithCandidate?,
secondReference: FirNamedReferenceWithCandidate?
): FirStatement {
val firstCandidate = firstReference?.candidate
val secondCandidate = secondReference?.candidate
requireNotNull(firstCandidate)
requireNotNull(secondCandidate)
return reportError(ConeOperatorAmbiguityError(listOf(firstCandidate, secondCandidate)))
}
fun reportUnresolvedReference(): FirStatement {
return reportError(ConeUnresolvedNameError(Name.identifier(operation.operator)))
}
return when {
assignIsSuccessful && setIsSuccessful -> reportAmbiguity(assignCallReference, setCallReference)
assignIsSuccessful -> chooseAssign()
setIsSuccessful -> chooseSetOperator()
else -> reportUnresolvedReference()
}
}
/**
@@ -1216,15 +1268,41 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
* @return null if `set` or `plus` calls are unresolved
* @return block defined as described above, otherwise
*/
private fun FirAugmentedArraySetCall.tryResolveAugmentedArraySetCallAsSetGetBlock(lhsGetCall: FirFunctionCall): FirBlock? {
private inner class AugmentedArraySetAsGetSetCallDesugaringInfo(
val augmentedArraySetCall: FirAugmentedArraySetCall,
val arrayVariable: FirProperty,
val indexVariables: List<FirProperty>,
val operatorCall: FirFunctionCall,
val setCall: FirFunctionCall
) {
fun toBlock(): FirBlock {
return buildBlock {
annotations += augmentedArraySetCall.annotations
statements += arrayVariable
statements += indexVariables
statements += setCall
}.also {
it.replaceTypeRef(
buildResolvedTypeRef {
type = session.builtinTypes.unitType.type
}
)
}
}
}
private fun tryResolveAugmentedArraySetCallAsSetGetBlock(
augmentedArraySetCall: FirAugmentedArraySetCall,
lhsGetCall: FirFunctionCall,
transformedRhs: FirExpression
): AugmentedArraySetAsGetSetCallDesugaringInfo {
val arrayVariable = generateTemporaryVariable(
session.moduleData,
source = null,
source = lhsGetCall.explicitReceiver?.source?.fakeElement(KtFakeSourceElementKind.DesugaredCompoundAssignment),
specialName = "<array>",
initializer = lhsGetCall.explicitReceiver ?: buildErrorExpression {
source =
this@tryResolveAugmentedArraySetCallAsSetGetBlock.source
?.fakeElement(KtFakeSourceElementKind.DesugaredCompoundAssignment)
source = augmentedArraySetCall.source
?.fakeElement(KtFakeSourceElementKind.DesugaredCompoundAssignment)
diagnostic = ConeSimpleDiagnostic("No receiver for array access", DiagnosticKind.Syntax)
}
)
@@ -1235,88 +1313,78 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
else
listOf(it)
}.mapIndexed { i, index ->
generateTemporaryVariable(session.moduleData, source = null, specialName = "<index_$i>", initializer = index)
}
val getCall = buildFunctionCall {
source = arrayAccessSource?.fakeElement(KtFakeSourceElementKind.DesugaredCompoundAssignment)
explicitReceiver = arrayVariable.toQualifiedAccess()
calleeReference = buildSimpleNamedReference {
name = OperatorNameConventions.GET
}
argumentList = buildArgumentList {
for (indexVariable in indexVariables) {
arguments += indexVariable.toQualifiedAccess()
}
}
origin = FirFunctionCallOrigin.Operator
}
val operatorCall = buildFunctionCall {
calleeReference = buildSimpleNamedReference {
name = FirOperationNameConventions.ASSIGNMENTS_TO_SIMPLE_OPERATOR.getValue(operation)
}
explicitReceiver = getCall
argumentList = buildArgumentList {
arguments += rhs
}
origin = FirFunctionCallOrigin.Operator
}
val setCall = buildFunctionCall {
source =
this@tryResolveAugmentedArraySetCallAsSetGetBlock.source
?.fakeElement(KtFakeSourceElementKind.DesugaredCompoundAssignment)
explicitReceiver = arrayVariable.toQualifiedAccess()
calleeReference = buildSimpleNamedReference {
name = OperatorNameConventions.SET
}
origin = FirFunctionCallOrigin.Operator
argumentList = buildArgumentList {
for (indexVariable in indexVariables) {
arguments += indexVariable.toQualifiedAccess()
}
arguments += operatorCall
}
annotations += this@tryResolveAugmentedArraySetCallAsSetGetBlock.annotations
generateTemporaryVariable(
session.moduleData,
source = index.source?.fakeElement(KtFakeSourceElementKind.DesugaredCompoundAssignment),
specialName = "<index_$i>",
initializer = index
)
}
arrayVariable.transformSingle(transformer, ResolutionMode.ContextIndependent)
indexVariables.forEach { it.transformSingle(transformer, ResolutionMode.ContextIndependent) }
val transformedSet = setCall.transformSingle(transformer, ResolutionMode.ContextIndependent)
if (!transformedSet.isSuccessful()) return null
val arrayAccess = arrayVariable.toQualifiedAccess()
val indicesQualifiedAccess = indexVariables.map { it.toQualifiedAccess() }
val transformedOperator =
transformedSet.argumentList.arguments.last().let {
if (it is FirNamedArgumentExpression)
it.expression
else
it
val getCall = buildFunctionCall {
source = augmentedArraySetCall.arrayAccessSource?.fakeElement(KtFakeSourceElementKind.DesugaredCompoundAssignment)
explicitReceiver = arrayAccess
if (lhsGetCall.explicitReceiver == lhsGetCall.dispatchReceiver) {
dispatchReceiver = arrayAccess
extensionReceiver = lhsGetCall.extensionReceiver
} else {
extensionReceiver = arrayAccess
dispatchReceiver = lhsGetCall.dispatchReceiver
}
require(transformedOperator is FirFunctionCall) {
"Last argument of set call should be an operator but $transformedOperator found"
}
if (!transformedOperator.isSuccessful()) return null
return buildBlock {
statements += arrayVariable
statements += indexVariables
statements += setCall
}.also {
it.replaceTypeRef(
buildResolvedTypeRef {
source = this@tryResolveAugmentedArraySetCallAsSetGetBlock.source
type = session.builtinTypes.unitType.type
calleeReference = lhsGetCall.calleeReference
argumentList = buildArgumentList {
var i = 0
for (argument in lhsGetCall.argumentList.arguments) {
arguments += if (argument is FirVarargArgumentsExpression) {
buildVarargArgumentsExpression {
val varargSize = argument.arguments.size
arguments += indicesQualifiedAccess.subList(i, i + varargSize)
i += varargSize
source = argument.source
typeRef = argument.typeRef
varargElementType = argument.varargElementType
}
} else {
indicesQualifiedAccess[i++]
}
}
)
}
origin = FirFunctionCallOrigin.Operator
typeRef = lhsGetCall.typeRef
}
}
val generator = GeneratorOfPlusAssignCalls(augmentedArraySetCall, augmentedArraySetCall.operation, getCall, transformedRhs)
val operatorCall = generator.createSimpleOperatorCall()
val resolvedOperatorCall = resolveCandidateForAssignmentOperatorCall {
operatorCall.transformSingle(this, ResolutionMode.ContextDependent)
}
val setCall = GeneratorOfPlusAssignCalls.createFunctionCall(
OperatorNameConventions.SET,
augmentedArraySetCall.source,
receiver = arrayAccess, // a
*indicesQualifiedAccess.toTypedArray(), // indices
resolvedOperatorCall // a.get(b).plus(c)
)
val resolvedSetCall = resolveCandidateForAssignmentOperatorCall {
setCall.transformSingle(this, ResolutionMode.ContextDependent)
}
return AugmentedArraySetAsGetSetCallDesugaringInfo(
augmentedArraySetCall,
arrayVariable,
indexVariables,
resolvedOperatorCall,
resolvedSetCall
)
}
override fun transformArrayOfCall(arrayOfCall: FirArrayOfCall, data: ResolutionMode): FirStatement {
if (data is ResolutionMode.ContextDependent) {
@@ -21,7 +21,6 @@ abstract class FirAugmentedArraySetCall : FirPureAbstractElement(), FirStatement
abstract override val annotations: List<FirAnnotation>
abstract val lhsGetCall: FirFunctionCall
abstract val rhs: FirExpression
abstract val rhs2: FirExpression
abstract val operation: FirOperation
abstract val calleeReference: FirReference
abstract val arrayAccessSource: KtSourceElement?
@@ -30,7 +30,6 @@ class FirAugmentedArraySetCallBuilder : FirAnnotationContainerBuilder {
override val annotations: MutableList<FirAnnotation> = mutableListOf()
lateinit var lhsGetCall: FirFunctionCall
lateinit var rhs: FirExpression
lateinit var rhs2: FirExpression
lateinit var operation: FirOperation
var calleeReference: FirReference = FirStubReference
var arrayAccessSource: KtSourceElement? = null
@@ -41,7 +40,6 @@ class FirAugmentedArraySetCallBuilder : FirAnnotationContainerBuilder {
annotations,
lhsGetCall,
rhs,
rhs2,
operation,
calleeReference,
arrayAccessSource,
@@ -24,7 +24,6 @@ internal class FirAugmentedArraySetCallImpl(
override val annotations: MutableList<FirAnnotation>,
override var lhsGetCall: FirFunctionCall,
override var rhs: FirExpression,
override var rhs2: FirExpression,
override val operation: FirOperation,
override var calleeReference: FirReference,
override val arrayAccessSource: KtSourceElement?,
@@ -33,7 +32,6 @@ internal class FirAugmentedArraySetCallImpl(
annotations.forEach { it.accept(visitor, data) }
lhsGetCall.accept(visitor, data)
rhs.accept(visitor, data)
rhs2.accept(visitor, data)
calleeReference.accept(visitor, data)
}
@@ -41,7 +39,6 @@ internal class FirAugmentedArraySetCallImpl(
transformAnnotations(transformer, data)
lhsGetCall = lhsGetCall.transform(transformer, data)
rhs = rhs.transform(transformer, data)
rhs2 = rhs2.transform(transformer, data)
calleeReference = calleeReference.transform(transformer, data)
return this
}
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fakeElement
import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.declarations.FirVariable
import org.jetbrains.kotlin.fir.declarations.builder.buildProperty
import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl
@@ -25,11 +26,14 @@ import org.jetbrains.kotlin.fir.types.builder.buildImplicitTypeRef
import org.jetbrains.kotlin.name.Name
fun FirVariable.toQualifiedAccess(): FirQualifiedAccessExpression = buildPropertyAccessExpression {
val fakeSource = this@toQualifiedAccess.source?.fakeElement(KtFakeSourceElementKind.ReferenceInAtomicQualifiedAccess)
source = fakeSource
calleeReference = buildResolvedNamedReference {
source = this@toQualifiedAccess.source?.fakeElement(KtFakeSourceElementKind.ReferenceInAtomicQualifiedAccess)
source = fakeSource
name = this@toQualifiedAccess.name
resolvedSymbol = this@toQualifiedAccess.symbol
}
typeRef = this@toQualifiedAccess.returnTypeRef
}
fun generateTemporaryVariable(
@@ -39,7 +43,7 @@ fun generateTemporaryVariable(
initializer: FirExpression,
typeRef: FirTypeRef? = null,
extractedAnnotations: Collection<FirAnnotation>? = null,
): FirVariable =
): FirProperty =
buildProperty {
this.source = source
this.moduleData = moduleData
@@ -66,7 +70,7 @@ fun generateTemporaryVariable(
specialName: String,
initializer: FirExpression,
extractedAnnotations: Collection<FirAnnotation>? = null,
): FirVariable =
): FirProperty =
generateTemporaryVariable(
moduleData,
source,
@@ -463,7 +463,6 @@ object NodeConfigurator : AbstractFieldConfigurator<FirTreeBuilder>(FirTreeBuild
augmentedArraySetCall.configure {
+field("lhsGetCall", functionCall)
+field("rhs", expression)
+field("rhs2", expression)
+field("operation", operationType)
// Used for resolution errors reporting in case
+field("calleeReference", reference, withReplace = true)
@@ -16,4 +16,4 @@ fun withVararg(vararg xs: Int) = 42
fun test1() {
A[::withVararg] += 1
}
}
@@ -34,7 +34,7 @@ fun testArrayAssignment4(n: Nothing) {
fun testArrayPlusAssign(array: Array<Any>) {
operator fun Any.plusAssign(a: Any) {}
array[1] <!UNREACHABLE_CODE!>+=<!> todo()
<!UNREACHABLE_CODE!>array<!><!UNREACHABLE_CODE!>[<!><!UNREACHABLE_CODE!>1<!><!UNREACHABLE_CODE!>] +=<!> todo()
}
fun todo(): Nothing = throw Exception()
@@ -1,12 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
object RemAndRemAssign {
operator fun rem(x: Int) = RemAndRemAssign
}
operator fun RemAndRemAssign.remAssign(x: Int) {}
fun test() {
var c = RemAndRemAssign
<!ASSIGN_OPERATOR_AMBIGUITY!>c %= 1<!>
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER
object RemAndRemAssign {
@@ -1,17 +0,0 @@
//KT-1820 Write test for ASSIGN_OPERATOR_AMBIGUITY
package kt1820
class MyInt(val i: Int) {
operator fun plus(m: MyInt) : MyInt = MyInt(m.i + i)
}
operator fun Any.plusAssign(a: Any) {}
fun test(m: MyInt) {
m += m
var i = 1
<!ASSIGN_OPERATOR_AMBIGUITY!>i += 34<!>
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
//KT-1820 Write test for ASSIGN_OPERATOR_AMBIGUITY
package kt1820
@@ -40,6 +40,6 @@ fun testMismatchingArities() {
<!NO_VALUE_FOR_PARAMETER!>++MismatchingArities2[0]<!>
<!NO_VALUE_FOR_PARAMETER!>MismatchingArities2[0]++<!>
<!NO_VALUE_FOR_PARAMETER!>MismatchingArities2[0]<!> <!UNRESOLVED_REFERENCE!>+=<!> 1
<!NO_VALUE_FOR_PARAMETER!>MismatchingArities2[0]<!> += 1
}
@@ -1,20 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
class C {
operator fun get(i: Int): C = this
}
operator fun C.plus(a: Any): C = this
operator fun C.plusAssign(a: Any) {}
class C1 {
operator fun get(i: Int): C = C()
operator fun set(i: Int, v: C) {}
}
fun test() {
val c = C()
c[0] += ""
var c1 = C1()
c1[0] <!OVERLOAD_RESOLUTION_AMBIGUITY!>+=<!> ""
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER
class C {
@@ -1,13 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
class C
operator fun C.plus(a: Any): C = this
operator fun C.plusAssign(a: Any) {}
fun test() {
val c = C()
c += ""
var c1 = C()
<!ASSIGN_OPERATOR_AMBIGUITY!>c1 += ""<!>
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER
class C
@@ -1,19 +0,0 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
class C {
val c: C = C()
}
operator fun C.plus(a: Any): C = this
operator fun C.plusAssign(a: Any) {}
class C1 {
var c: C = C()
}
fun test() {
val c = C()
c.c += ""
var c1 = C1()
<!ASSIGN_OPERATOR_AMBIGUITY!>c1.c += ""<!>
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// !DIAGNOSTICS: -UNUSED_PARAMETER
class C {
@@ -13,8 +13,8 @@ fun <T> id(x: T) = x
fun main() {
var newValue = A()
<!ASSIGN_OPERATOR_AMBIGUITY!>newValue += id { total -> A() }<!>
<!ASSIGN_OPERATOR_AMBIGUITY!>newValue += id(fun(total) = A())<!>
<!ASSIGN_OPERATOR_AMBIGUITY!>newValue += id(fun(total): A { return A() })<!>
<!ASSIGN_OPERATOR_AMBIGUITY!>newValue += id(::foo)<!>
newValue <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> id { total -> A() }
<!ASSIGN_OPERATOR_AMBIGUITY!>newValue<!> += id(fun(total) = A())
newValue <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> id(fun(total): A { return A() })
newValue <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> id(::foo)
}
@@ -20,14 +20,14 @@ class B() {
fun case7(){
var b =B()
<!ASSIGN_OPERATOR_AMBIGUITY!>b+=1<!> //ASSIGN_OPERATOR_AMBIGUITY
b<!ASSIGN_OPERATOR_AMBIGUITY!>+=<!>1 //ASSIGN_OPERATOR_AMBIGUITY
this <!PROPERTY_AS_OPERATOR!>+=<!> 1 //ASSIGNMENT_OPERATOR_SHOULD_RETURN_UNIT, PROPERTY_AS_OPERATOR
}
}
fun case1() {
var b = B()
<!ASSIGN_OPERATOR_AMBIGUITY!>b += 1<!> //ASSIGN_OPERATOR_AMBIGUITY
b <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> 1 //ASSIGN_OPERATOR_AMBIGUITY
}
// FILE: TestCase2.kt
@@ -52,7 +52,7 @@ class B() {
b += 1 //ok
var b1 = B()
<!ASSIGN_OPERATOR_AMBIGUITY!>b1 += 1<!> //ASSIGN_OPERATOR_AMBIGUITY
b1 <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> 1 //ASSIGN_OPERATOR_AMBIGUITY
this += 1 //ok
}
@@ -60,5 +60,5 @@ class B() {
fun case2() {
var b = B()
<!ASSIGN_OPERATOR_AMBIGUITY!>b += 1<!> //ASSIGN_OPERATOR_AMBIGUITY
b <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> 1 //ASSIGN_OPERATOR_AMBIGUITY
}
@@ -15,5 +15,5 @@ class B(var a: Int) {
// TESTCASE NUMBER: 1
fun case1() {
var b = B(1)
<!ASSIGN_OPERATOR_AMBIGUITY!>b += 1<!>
b <!ASSIGN_OPERATOR_AMBIGUITY!>+=<!> 1
}
@@ -5,7 +5,7 @@
// TESTCASE NUMBER: 1
fun case_1(x: Class?) {
x!!
<!DEBUG_INFO_EXPRESSION_TYPE("Class & Class"), DEBUG_INFO_EXPRESSION_TYPE("Class?")!>x<!>[if (true) {<!VAL_REASSIGNMENT!>x<!>=null;0} else 0] += <!DEBUG_INFO_EXPRESSION_TYPE("Class? & Class")!>x<!>[0]
<!DEBUG_INFO_EXPRESSION_TYPE("Class & Class"), DEBUG_INFO_EXPRESSION_TYPE("Class?"), DEBUG_INFO_EXPRESSION_TYPE("Class"), DEBUG_INFO_EXPRESSION_TYPE("Class")!>x<!>[if (true) {<!VAL_REASSIGNMENT!>x<!>=null;0} else 0] += <!DEBUG_INFO_EXPRESSION_TYPE("Class? & Class")!>x<!>[0]
<!DEBUG_INFO_EXPRESSION_TYPE("Class? & Class")!>x<!>
<!DEBUG_INFO_EXPRESSION_TYPE("Class? & Class")!>x<!>[0].inv()
}
@@ -10,7 +10,7 @@
fun case_1() {
var x: MutableList<Int>? = mutableListOf(1)
x!!
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.collections.MutableList<kotlin.Int>? & kotlin.collections.MutableList<kotlin.Int>")!>x<!>[if (true) {x=null;0} else 0] += <!UNSAFE_CALL!><!DEBUG_INFO_EXPRESSION_TYPE("kotlin.collections.MutableList<kotlin.Int>?")!>x<!>[0]<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.collections.MutableList<kotlin.Int>? & kotlin.collections.MutableList<kotlin.Int>")!>x<!>[if (true) {x=null;0} else 0] <!UNRESOLVED_REFERENCE!>+=<!> <!UNSAFE_CALL!><!DEBUG_INFO_EXPRESSION_TYPE("kotlin.collections.MutableList<kotlin.Int>?")!>x<!>[0]<!>
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.collections.MutableList<kotlin.Int>?")!>x<!>
<!UNSAFE_CALL!><!DEBUG_INFO_EXPRESSION_TYPE("kotlin.collections.MutableList<kotlin.Int>?")!>x<!>[0]<!>.inv()
}