FIR: Change semantics for combination of safe calls and operators
^KT-41034 Fixed
This commit is contained in:
+18
@@ -433,6 +433,24 @@ public class FirLazyBodiesCalculatorTestGenerated extends AbstractFirLazyBodiesC
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/qualifierWithTypeArguments.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("safeCallsWithAssignment.kt")
|
||||
public void testSafeCallsWithAssignment() throws Exception {
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAssignment.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("safeCallsWithAugmentedAssignment.kt")
|
||||
public void testSafeCallsWithAugmentedAssignment() throws Exception {
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAugmentedAssignment.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("safeCallsWithUnaryOperators.kt")
|
||||
public void testSafeCallsWithUnaryOperators() throws Exception {
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithUnaryOperators.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("simpleReturns.kt")
|
||||
public void testSimpleReturns() throws Exception {
|
||||
|
||||
@@ -391,7 +391,9 @@ class Fir2IrVisitor(
|
||||
)
|
||||
|
||||
return conversionScope.withSafeCallSubject(receiverVariable) {
|
||||
val afterNotNullCheck = safeCallExpression.selector.accept(this, data) as IrExpression
|
||||
val afterNotNullCheck =
|
||||
(safeCallExpression.selector as? FirExpression)?.let(::convertToIrExpression)
|
||||
?: safeCallExpression.selector.accept(this, data) as IrExpression
|
||||
components.createSafeCallConstruction(receiverVariable, variableSymbol, afterNotNullCheck)
|
||||
}
|
||||
}
|
||||
|
||||
+30
@@ -41267,12 +41267,36 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/safeCall"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("augmentedAssigmentPlus.kt")
|
||||
public void testAugmentedAssigmentPlus() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/safeCall/augmentedAssigmentPlus.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("augmentedAssigmentPlusAssign.kt")
|
||||
public void testAugmentedAssigmentPlusAssign() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/safeCall/augmentedAssigmentPlusAssign.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("genericNull.kt")
|
||||
public void testGenericNull() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/safeCall/genericNull.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("incrementPostfix.kt")
|
||||
public void testIncrementPostfix() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/safeCall/incrementPostfix.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("incrementPrefix.kt")
|
||||
public void testIncrementPrefix() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/safeCall/incrementPrefix.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt1572.kt")
|
||||
public void testKt1572() throws Exception {
|
||||
@@ -41368,6 +41392,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
public void testSafeCallWithElvisFolding() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/safeCall/safeCallWithElvisFolding.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("withAssignment.kt")
|
||||
public void testWithAssignment() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/safeCall/withAssignment.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
||||
+4
-4
@@ -12,8 +12,8 @@ import org.jetbrains.kotlin.ElementTypeUtils.getOperationSymbol
|
||||
import org.jetbrains.kotlin.ElementTypeUtils.isExpression
|
||||
import org.jetbrains.kotlin.KtFakeSourceElementKind
|
||||
import org.jetbrains.kotlin.KtLightSourceElement
|
||||
import org.jetbrains.kotlin.KtSourceElement
|
||||
import org.jetbrains.kotlin.KtNodeTypes.*
|
||||
import org.jetbrains.kotlin.KtSourceElement
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.fakeElement
|
||||
@@ -556,7 +556,7 @@ class ExpressionsConverter(
|
||||
if (isSafe) {
|
||||
@OptIn(FirImplementationDetail::class)
|
||||
it.replaceSource(dotQualifiedExpression.toFirSourceElement(KtFakeSourceElementKind.DesugaredSafeCallExpression))
|
||||
return it.wrapWithSafeCall(
|
||||
return it.createSafeCall(
|
||||
firReceiver!!,
|
||||
dotQualifiedExpression.toFirSourceElement()
|
||||
)
|
||||
@@ -918,7 +918,7 @@ class ExpressionsConverter(
|
||||
* @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parseArrayAccess
|
||||
* @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.visitArrayAccessExpression
|
||||
*/
|
||||
private fun convertArrayAccessExpression(arrayAccess: LighterASTNode): FirFunctionCall {
|
||||
private fun convertArrayAccessExpression(arrayAccess: LighterASTNode): FirExpression {
|
||||
var firExpression: FirExpression? = null
|
||||
val indices: MutableList<FirExpression> = mutableListOf()
|
||||
arrayAccess.forEachChildren {
|
||||
@@ -942,7 +942,7 @@ class ExpressionsConverter(
|
||||
getArgument?.let { arguments += it }
|
||||
}
|
||||
origin = FirFunctionCallOrigin.Operator
|
||||
}
|
||||
}.pullUpSafeCallIfNecessary()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+15
@@ -393,6 +393,21 @@ public class LightTree2FirConverterTestCaseGenerated extends AbstractLightTree2F
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/qualifierWithTypeArguments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("safeCallsWithAssignment.kt")
|
||||
public void testSafeCallsWithAssignment() throws Exception {
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAssignment.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("safeCallsWithAugmentedAssignment.kt")
|
||||
public void testSafeCallsWithAugmentedAssignment() throws Exception {
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAugmentedAssignment.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("safeCallsWithUnaryOperators.kt")
|
||||
public void testSafeCallsWithUnaryOperators() throws Exception {
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithUnaryOperators.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simpleReturns.kt")
|
||||
public void testSimpleReturns() throws Exception {
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/simpleReturns.kt");
|
||||
|
||||
@@ -10,7 +10,10 @@ import com.intellij.psi.tree.IElementType
|
||||
import org.jetbrains.kotlin.*
|
||||
import org.jetbrains.kotlin.builtins.StandardNames.BACKING_FIELD
|
||||
import org.jetbrains.kotlin.builtins.StandardNames.DEFAULT_VALUE_PARAMETER
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.descriptors.Visibility
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationUseSiteTarget.*
|
||||
import org.jetbrains.kotlin.fir.*
|
||||
import org.jetbrains.kotlin.fir.contracts.FirContractDescription
|
||||
@@ -2354,7 +2357,7 @@ open class RawFirBuilder(
|
||||
}
|
||||
}
|
||||
origin = FirFunctionCallOrigin.Operator
|
||||
}
|
||||
}.pullUpSafeCallIfNecessary()
|
||||
}
|
||||
|
||||
override fun visitQualifiedExpression(expression: KtQualifiedExpression, data: Unit): FirElement {
|
||||
@@ -2369,7 +2372,7 @@ open class RawFirBuilder(
|
||||
if (expression is KtSafeQualifiedExpression) {
|
||||
@OptIn(FirImplementationDetail::class)
|
||||
firSelector.replaceSource(expression.toFirSourceElement(KtFakeSourceElementKind.DesugaredSafeCallExpression))
|
||||
return firSelector.wrapWithSafeCall(
|
||||
return firSelector.createSafeCall(
|
||||
receiver,
|
||||
expression.toFirSourceElement()
|
||||
)
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
fun foo() {
|
||||
a?.b = 1
|
||||
a?.b?.c = 1
|
||||
a?.b.c = 1
|
||||
|
||||
a?.b[0] = 1
|
||||
a?.b?.c[0] = 1
|
||||
a?.b.c[0] = 1
|
||||
|
||||
a?.b[0][0] = 1
|
||||
a?.b?.c[0][0] = 1
|
||||
a?.b.c[0][0] = 1
|
||||
|
||||
a?.b.d() = 1
|
||||
}
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
FILE: safeCallsWithAssignment.kt
|
||||
public? final? fun foo(): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
FILE: safeCallsWithAssignment.kt
|
||||
public? final? fun foo(): R|kotlin/Unit| {
|
||||
a#?.{ $subj$.b# = IntegerLiteral(1) }
|
||||
a#?.{ $subj$.b# }?.{ $subj$.c# = IntegerLiteral(1) }
|
||||
a#?.{ $subj$.b# }.c# = IntegerLiteral(1)
|
||||
a#?.{ $subj$.b#.set#(IntegerLiteral(0), IntegerLiteral(1)) }
|
||||
a#?.{ $subj$.b# }?.{ $subj$.c#.set#(IntegerLiteral(0), IntegerLiteral(1)) }
|
||||
a#?.{ $subj$.b# }.c#.set#(IntegerLiteral(0), IntegerLiteral(1))
|
||||
a#?.{ $subj$.b#.get#(IntegerLiteral(0)).set#(IntegerLiteral(0), IntegerLiteral(1)) }
|
||||
a#?.{ $subj$.b# }?.{ $subj$.c#.get#(IntegerLiteral(0)).set#(IntegerLiteral(0), IntegerLiteral(1)) }
|
||||
a#?.{ $subj$.b# }.c#.get#(IntegerLiteral(0)).set#(IntegerLiteral(0), IntegerLiteral(1))
|
||||
a#?.{ $subj$.b# }.d# = IntegerLiteral(1)
|
||||
}
|
||||
Vendored
+15
@@ -0,0 +1,15 @@
|
||||
fun foo() {
|
||||
a?.b += 1
|
||||
a?.b?.c += 1
|
||||
a?.b.c += 1
|
||||
|
||||
a?.b[0] += 1
|
||||
a?.b?.c[0] += 1
|
||||
a?.b.c[0] += 1
|
||||
|
||||
a?.b[0][0] += 1
|
||||
a?.b?.c[0][0] += 1
|
||||
a?.b.c[0][0] += 1
|
||||
|
||||
a?.b.d() += 1
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
FILE: safeCallsWithAugmentedAssignment.kt
|
||||
public? final? fun foo(): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
Vendored
+13
@@ -0,0 +1,13 @@
|
||||
FILE: safeCallsWithAugmentedAssignment.kt
|
||||
public? final? fun foo(): R|kotlin/Unit| {
|
||||
a#?.{ +=($subj$.b#, IntegerLiteral(1)) }
|
||||
a#?.{ $subj$.b# }?.{ +=($subj$.c#, IntegerLiteral(1)) }
|
||||
+=(a#?.{ $subj$.b# }.c#, IntegerLiteral(1))
|
||||
a#?.{ ArraySet:[$subj$.b#.get#(IntegerLiteral(0)).plusAssign#(IntegerLiteral(1))] }
|
||||
a#?.{ $subj$.b# }?.{ ArraySet:[$subj$.c#.get#(IntegerLiteral(0)).plusAssign#(IntegerLiteral(1))] }
|
||||
ArraySet:[a#?.{ $subj$.b# }.c#.get#(IntegerLiteral(0)).plusAssign#(IntegerLiteral(1))]
|
||||
a#?.{ ArraySet:[$subj$.b#.get#(IntegerLiteral(0)).get#(IntegerLiteral(0)).plusAssign#(IntegerLiteral(1))] }
|
||||
a#?.{ $subj$.b# }?.{ ArraySet:[$subj$.c#.get#(IntegerLiteral(0)).get#(IntegerLiteral(0)).plusAssign#(IntegerLiteral(1))] }
|
||||
ArraySet:[a#?.{ $subj$.b# }.c#.get#(IntegerLiteral(0)).get#(IntegerLiteral(0)).plusAssign#(IntegerLiteral(1))]
|
||||
+=(a#?.{ $subj$.b# }.d#(), IntegerLiteral(1))
|
||||
}
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
fun foo() {
|
||||
a?.b++
|
||||
a?.b?.c++
|
||||
a?.b.c++
|
||||
|
||||
a?.b[0]++
|
||||
a?.b?.c[0]++
|
||||
a?.b.c[0]++
|
||||
|
||||
a?.b[0][0]++
|
||||
a?.b?.c[0][0]++
|
||||
a?.b.c[0][0]++
|
||||
|
||||
a?.b.d()++
|
||||
}
|
||||
|
||||
fun foo2() {
|
||||
++a?.b
|
||||
++a?.b?.c
|
||||
++a?.b.c
|
||||
|
||||
++a?.b[0]
|
||||
++a?.b?.c[0]
|
||||
++a?.b.c[0]
|
||||
|
||||
++a?.b[0][0]
|
||||
++a?.b?.c[0][0]
|
||||
++a?.b.c[0][0]
|
||||
|
||||
++a?.b.d()
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
FILE: safeCallsWithUnaryOperators.kt
|
||||
public? final? fun foo(): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
public? final? fun foo2(): R|kotlin/Unit| { LAZY_BLOCK }
|
||||
Vendored
+133
@@ -0,0 +1,133 @@
|
||||
FILE: safeCallsWithUnaryOperators.kt
|
||||
public? final? fun foo(): R|kotlin/Unit| {
|
||||
a#?.{ {
|
||||
lval <receiver>: <implicit> = $subj$
|
||||
lval <unary>: <implicit> = R|<local>/<receiver>|.b#
|
||||
R|<local>/<receiver>|.b# = R|<local>/<unary>|.inc#()
|
||||
R|<local>/<unary>|
|
||||
}
|
||||
}
|
||||
a#?.{ $subj$.b# }?.{ {
|
||||
lval <receiver>: <implicit> = $subj$
|
||||
lval <unary>: <implicit> = R|<local>/<receiver>|.c#
|
||||
R|<local>/<receiver>|.c# = R|<local>/<unary>|.inc#()
|
||||
R|<local>/<unary>|
|
||||
}
|
||||
}
|
||||
lval <receiver>: <implicit> = a#?.{ $subj$.b# }
|
||||
lval <unary>: <implicit> = R|<local>/<receiver>|.c#
|
||||
R|<local>/<receiver>|.c# = R|<local>/<unary>|.inc#()
|
||||
R|<local>/<unary>|
|
||||
a#?.{ {
|
||||
lval <array>: <implicit> = $subj$.b#
|
||||
lval <index0>: <implicit> = IntegerLiteral(0)
|
||||
lval <unary>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|)
|
||||
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary>|.inc#())
|
||||
R|<local>/<unary>|
|
||||
}
|
||||
}
|
||||
a#?.{ $subj$.b# }?.{ {
|
||||
lval <array>: <implicit> = $subj$.c#
|
||||
lval <index0>: <implicit> = IntegerLiteral(0)
|
||||
lval <unary>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|)
|
||||
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary>|.inc#())
|
||||
R|<local>/<unary>|
|
||||
}
|
||||
}
|
||||
lval <array>: <implicit> = a#?.{ $subj$.b# }.c#
|
||||
lval <index0>: <implicit> = IntegerLiteral(0)
|
||||
lval <unary>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|)
|
||||
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary>|.inc#())
|
||||
R|<local>/<unary>|
|
||||
a#?.{ {
|
||||
lval <array>: <implicit> = $subj$.b#.get#(IntegerLiteral(0))
|
||||
lval <index0>: <implicit> = IntegerLiteral(0)
|
||||
lval <unary>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|)
|
||||
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary>|.inc#())
|
||||
R|<local>/<unary>|
|
||||
}
|
||||
}
|
||||
a#?.{ $subj$.b# }?.{ {
|
||||
lval <array>: <implicit> = $subj$.c#.get#(IntegerLiteral(0))
|
||||
lval <index0>: <implicit> = IntegerLiteral(0)
|
||||
lval <unary>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|)
|
||||
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary>|.inc#())
|
||||
R|<local>/<unary>|
|
||||
}
|
||||
}
|
||||
lval <array>: <implicit> = a#?.{ $subj$.b# }.c#.get#(IntegerLiteral(0))
|
||||
lval <index0>: <implicit> = IntegerLiteral(0)
|
||||
lval <unary>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|)
|
||||
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary>|.inc#())
|
||||
R|<local>/<unary>|
|
||||
lval <receiver>: <implicit> = a#?.{ $subj$.b# }
|
||||
lval <unary>: <implicit> = R|<local>/<receiver>|.d#()
|
||||
R|<local>/<receiver>|.d# = R|<local>/<unary>|.inc#()
|
||||
R|<local>/<unary>|
|
||||
}
|
||||
public? final? fun foo2(): R|kotlin/Unit| {
|
||||
a#?.{ {
|
||||
lval <receiver>: <implicit> = $subj$
|
||||
lval <unary-result>: <implicit> = R|<local>/<receiver>|.b#.inc#()
|
||||
R|<local>/<receiver>|.b# = R|<local>/<unary-result>|
|
||||
R|<local>/<unary-result>|
|
||||
}
|
||||
}
|
||||
a#?.{ $subj$.b# }?.{ {
|
||||
lval <receiver>: <implicit> = $subj$
|
||||
lval <unary-result>: <implicit> = R|<local>/<receiver>|.c#.inc#()
|
||||
R|<local>/<receiver>|.c# = R|<local>/<unary-result>|
|
||||
R|<local>/<unary-result>|
|
||||
}
|
||||
}
|
||||
lval <receiver>: <implicit> = a#?.{ $subj$.b# }
|
||||
lval <unary-result>: <implicit> = R|<local>/<receiver>|.c#.inc#()
|
||||
R|<local>/<receiver>|.c# = R|<local>/<unary-result>|
|
||||
R|<local>/<unary-result>|
|
||||
a#?.{ {
|
||||
lval <array>: <implicit> = $subj$.b#
|
||||
lval <index0>: <implicit> = IntegerLiteral(0)
|
||||
lval <unary-result>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|).inc#()
|
||||
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary-result>|)
|
||||
R|<local>/<unary-result>|
|
||||
}
|
||||
}
|
||||
a#?.{ $subj$.b# }?.{ {
|
||||
lval <array>: <implicit> = $subj$.c#
|
||||
lval <index0>: <implicit> = IntegerLiteral(0)
|
||||
lval <unary-result>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|).inc#()
|
||||
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary-result>|)
|
||||
R|<local>/<unary-result>|
|
||||
}
|
||||
}
|
||||
lval <array>: <implicit> = a#?.{ $subj$.b# }.c#
|
||||
lval <index0>: <implicit> = IntegerLiteral(0)
|
||||
lval <unary-result>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|).inc#()
|
||||
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary-result>|)
|
||||
R|<local>/<unary-result>|
|
||||
a#?.{ {
|
||||
lval <array>: <implicit> = $subj$.b#.get#(IntegerLiteral(0))
|
||||
lval <index0>: <implicit> = IntegerLiteral(0)
|
||||
lval <unary-result>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|).inc#()
|
||||
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary-result>|)
|
||||
R|<local>/<unary-result>|
|
||||
}
|
||||
}
|
||||
a#?.{ $subj$.b# }?.{ {
|
||||
lval <array>: <implicit> = $subj$.c#.get#(IntegerLiteral(0))
|
||||
lval <index0>: <implicit> = IntegerLiteral(0)
|
||||
lval <unary-result>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|).inc#()
|
||||
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary-result>|)
|
||||
R|<local>/<unary-result>|
|
||||
}
|
||||
}
|
||||
lval <array>: <implicit> = a#?.{ $subj$.b# }.c#.get#(IntegerLiteral(0))
|
||||
lval <index0>: <implicit> = IntegerLiteral(0)
|
||||
lval <unary-result>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index0>|).inc#()
|
||||
R|<local>/<array>|.set#(R|<local>/<index0>|, R|<local>/<unary-result>|)
|
||||
R|<local>/<unary-result>|
|
||||
lval <receiver>: <implicit> = a#?.{ $subj$.b# }
|
||||
lval <unary-result>: <implicit> = R|<local>/<receiver>|.d#().inc#()
|
||||
R|<local>/<receiver>|.d# = R|<local>/<unary-result>|
|
||||
R|<local>/<unary-result>|
|
||||
}
|
||||
+15
@@ -393,6 +393,21 @@ public class RawFirBuilderLazyBodiesTestCaseGenerated extends AbstractRawFirBuil
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/qualifierWithTypeArguments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("safeCallsWithAssignment.kt")
|
||||
public void testSafeCallsWithAssignment() throws Exception {
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAssignment.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("safeCallsWithAugmentedAssignment.kt")
|
||||
public void testSafeCallsWithAugmentedAssignment() throws Exception {
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAugmentedAssignment.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("safeCallsWithUnaryOperators.kt")
|
||||
public void testSafeCallsWithUnaryOperators() throws Exception {
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithUnaryOperators.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simpleReturns.kt")
|
||||
public void testSimpleReturns() throws Exception {
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/simpleReturns.kt");
|
||||
|
||||
+15
@@ -393,6 +393,21 @@ public class RawFirBuilderTestCaseGenerated extends AbstractRawFirBuilderTestCas
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/qualifierWithTypeArguments.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("safeCallsWithAssignment.kt")
|
||||
public void testSafeCallsWithAssignment() throws Exception {
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAssignment.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("safeCallsWithAugmentedAssignment.kt")
|
||||
public void testSafeCallsWithAugmentedAssignment() throws Exception {
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAugmentedAssignment.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("safeCallsWithUnaryOperators.kt")
|
||||
public void testSafeCallsWithUnaryOperators() throws Exception {
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithUnaryOperators.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("simpleReturns.kt")
|
||||
public void testSimpleReturns() throws Exception {
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/simpleReturns.kt");
|
||||
|
||||
+99
-39
@@ -489,7 +489,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
|
||||
diagnostic = ConeSimpleDiagnostic("Inc/dec without operand", DiagnosticKind.Syntax)
|
||||
}
|
||||
|
||||
if (unwrappedReceiver.elementType == DOT_QUALIFIED_EXPRESSION) {
|
||||
if (unwrappedReceiver.elementType == DOT_QUALIFIED_EXPRESSION || unwrappedReceiver.elementType == SAFE_ACCESS_EXPRESSION) {
|
||||
return generateIncrementOrDecrementBlockForQualifiedAccess(
|
||||
wholeExpression,
|
||||
operationReference,
|
||||
@@ -670,35 +670,35 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
|
||||
private fun generateIncrementOrDecrementBlockForQualifiedAccess(
|
||||
wholeExpression: T,
|
||||
operationReference: T?,
|
||||
receiverForOperation: T,
|
||||
receiverForOperation: T, // a.b
|
||||
callName: Name,
|
||||
prefix: Boolean,
|
||||
convert: T.() -> FirExpression
|
||||
): FirExpression {
|
||||
return buildBlock {
|
||||
val argumentReceiver = receiverForOperation.receiverExpression // a
|
||||
return buildBlockProbablyUnderSafeCall(
|
||||
receiverForOperation,
|
||||
convert,
|
||||
receiverForOperation.toFirSourceElement(),
|
||||
) { qualifiedFir ->
|
||||
val receiverFir = (qualifiedFir as? FirQualifiedAccess)?.explicitReceiver ?: buildErrorExpression {
|
||||
source = receiverForOperation.toFirSourceElement()
|
||||
diagnostic = ConeSimpleDiagnostic("Qualified expression without selector", DiagnosticKind.Syntax)
|
||||
}
|
||||
|
||||
val baseSource = wholeExpression?.toFirSourceElement()
|
||||
val desugaredSource = baseSource?.fakeElement(KtFakeSourceElementKind.DesugaredIncrementOrDecrement)
|
||||
source = desugaredSource
|
||||
|
||||
val argumentReceiver = receiverForOperation.receiverExpression
|
||||
val argumentSelector = receiverForOperation.selectorExpression
|
||||
|
||||
val argumentReceiverVariable = generateTemporaryVariable(
|
||||
baseModuleData,
|
||||
argumentReceiver?.toFirSourceElement(),
|
||||
Name.special("<receiver>"),
|
||||
argumentReceiver?.convert() ?: buildErrorExpression {
|
||||
source = receiverForOperation.toFirSourceElement()
|
||||
diagnostic = ConeSimpleDiagnostic("Qualified expression without receiver", DiagnosticKind.Syntax)
|
||||
}
|
||||
initializer = receiverFir,
|
||||
).also { statements += it }
|
||||
|
||||
val firArgument = generateResolvedAccessExpression(argumentReceiverVariable.source, argumentReceiverVariable).let { receiver ->
|
||||
val firArgumentSelector = argumentSelector?.convert() ?: buildErrorExpression {
|
||||
source = receiverForOperation.toFirSourceElement()
|
||||
diagnostic = ConeSimpleDiagnostic("Qualified expression without selector", DiagnosticKind.Syntax)
|
||||
}
|
||||
firArgumentSelector.also { if (it is FirQualifiedAccessExpression) it.replaceExplicitReceiver(receiver) }
|
||||
qualifiedFir.also { if (it is FirQualifiedAccessExpression) it.replaceExplicitReceiver(receiver) }
|
||||
}
|
||||
|
||||
putIncrementOrDecrementStatements(
|
||||
@@ -759,12 +759,14 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
|
||||
prefix: Boolean,
|
||||
convert: T.() -> FirExpression
|
||||
): FirExpression {
|
||||
return buildBlock {
|
||||
val array = receiver.arrayExpression
|
||||
return buildBlockProbablyUnderSafeCall(
|
||||
array, convert, receiver.toFirSourceElement(),
|
||||
) { arrayReceiver ->
|
||||
val baseSource = wholeExpression?.toFirSourceElement()
|
||||
val desugaredSource = baseSource?.fakeElement(KtFakeSourceElementKind.DesugaredIncrementOrDecrement)
|
||||
source = desugaredSource
|
||||
|
||||
val array = receiver.arrayExpression
|
||||
val indices = receiver.indexExpressions
|
||||
requireNotNull(indices) { "No indices in ${wholeExpression.asText}" }
|
||||
|
||||
@@ -772,10 +774,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
|
||||
baseModuleData,
|
||||
array?.toFirSourceElement(),
|
||||
Name.special("<array>"),
|
||||
array?.convert() ?: buildErrorExpression {
|
||||
source = receiver.toFirSourceElement()
|
||||
diagnostic = ConeSimpleDiagnostic("No array expression", DiagnosticKind.Syntax)
|
||||
}
|
||||
initializer = arrayReceiver,
|
||||
).also { statements += it }
|
||||
|
||||
val indexVariables = indices.mapIndexed { i, index ->
|
||||
@@ -829,6 +828,42 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if `receiver` is a safe call a?.f(...), insert a block under safe call
|
||||
// a?.{ val receiver = $subj$.f() ... } where `...` is generated by `init(FIR<$subj$.f()>)`
|
||||
//
|
||||
// Otherwise just returns buildBlock { init(FIR<receiver>)) }
|
||||
private fun buildBlockProbablyUnderSafeCall(
|
||||
receiver: T?,
|
||||
convert: T.() -> FirExpression,
|
||||
sourceElementForError: KtSourceElement?,
|
||||
init: FirBlockBuilder.(receiver: FirExpression) -> Unit = {}
|
||||
): FirExpression {
|
||||
val receiverFir = receiver?.convert() ?: buildErrorExpression {
|
||||
source = sourceElementForError
|
||||
diagnostic = ConeSimpleDiagnostic("No receiver expression", DiagnosticKind.Syntax)
|
||||
}
|
||||
|
||||
if (receiverFir is FirSafeCallExpression) {
|
||||
receiverFir.replaceSelector(
|
||||
buildBlock {
|
||||
init(
|
||||
receiverFir.selector as? FirExpression ?: buildErrorExpression {
|
||||
source = sourceElementForError
|
||||
diagnostic = ConeSimpleDiagnostic("Safe call selector expected to be an expression here", DiagnosticKind.Syntax)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
return receiverFir
|
||||
}
|
||||
|
||||
return buildBlock {
|
||||
init(receiverFir)
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirQualifiedAccessBuilder.initializeLValue(
|
||||
left: T?,
|
||||
convertQualified: T.() -> FirQualifiedAccess?
|
||||
@@ -893,26 +928,51 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
|
||||
return if (operation == FirOperation.ASSIGN) {
|
||||
val result = unwrappedLhs.convert()
|
||||
(result.annotations as MutableList<FirAnnotation>) += annotations
|
||||
result
|
||||
result.pullUpSafeCallIfNecessary()
|
||||
} else {
|
||||
generateAugmentedArraySetCall(unwrappedLhs, baseSource, arrayAccessSource, operation, annotations, rhsAST, convert)
|
||||
val receiver = unwrappedLhs.convert()
|
||||
|
||||
if (receiver is FirSafeCallExpression) {
|
||||
receiver.replaceSelector(
|
||||
generateAugmentedArraySetCall(
|
||||
receiver.selector as FirExpression, baseSource, arrayAccessSource, operation, annotations, rhsAST, convert
|
||||
)
|
||||
)
|
||||
receiver
|
||||
} else {
|
||||
generateAugmentedArraySetCall(receiver, baseSource, arrayAccessSource, operation, annotations, rhsAST, convert)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (operation in FirOperation.ASSIGNMENTS && operation != FirOperation.ASSIGN) {
|
||||
return buildAssignmentOperatorStatement {
|
||||
val lhsReceiver = this@generateAssignment?.convert()
|
||||
|
||||
val receiverToUse =
|
||||
if (lhsReceiver is FirSafeCallExpression)
|
||||
lhsReceiver.selector as? FirExpression
|
||||
else
|
||||
lhsReceiver
|
||||
|
||||
val result = buildAssignmentOperatorStatement {
|
||||
source = baseSource
|
||||
this.operation = operation
|
||||
leftArgument = this@generateAssignment?.convert()
|
||||
?: buildErrorExpression {
|
||||
source = null
|
||||
diagnostic = ConeSimpleDiagnostic(
|
||||
"Unsupported left value of assignment: ${baseSource?.psi?.text}", DiagnosticKind.ExpressionExpected
|
||||
)
|
||||
}
|
||||
leftArgument = receiverToUse ?: buildErrorExpression {
|
||||
source = null
|
||||
diagnostic = ConeSimpleDiagnostic(
|
||||
"Unsupported left value of assignment: ${baseSource?.psi?.text}", DiagnosticKind.ExpressionExpected
|
||||
)
|
||||
}
|
||||
rightArgument = rhsExpression
|
||||
this.annotations += annotations
|
||||
}
|
||||
|
||||
return if (lhsReceiver is FirSafeCallExpression) {
|
||||
lhsReceiver.replaceSelector(result)
|
||||
lhsReceiver
|
||||
} else {
|
||||
result
|
||||
}
|
||||
}
|
||||
require(operation == FirOperation.ASSIGN)
|
||||
|
||||
@@ -956,7 +1016,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
|
||||
}
|
||||
|
||||
private fun generateAugmentedArraySetCall(
|
||||
unwrappedReceiver: T,
|
||||
receiver: FirExpression, // a.get(x,y)
|
||||
baseSource: KtSourceElement?,
|
||||
arrayAccessSource: KtSourceElement?,
|
||||
operation: FirOperation,
|
||||
@@ -967,15 +1027,15 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
|
||||
return buildAugmentedArraySetCall {
|
||||
source = baseSource
|
||||
this.operation = operation
|
||||
assignCall = generateAugmentedCallForAugmentedArraySetCall(unwrappedReceiver, baseSource, operation, rhs, convert)
|
||||
assignCall = generateAugmentedCallForAugmentedArraySetCall(receiver, baseSource, operation, rhs, convert)
|
||||
setGetBlock =
|
||||
generateSetGetBlockForAugmentedArraySetCall(unwrappedReceiver, baseSource, arrayAccessSource, operation, rhs, convert)
|
||||
generateSetGetBlockForAugmentedArraySetCall(receiver, baseSource, arrayAccessSource, operation, rhs, convert)
|
||||
this.annotations += annotations
|
||||
}
|
||||
}
|
||||
|
||||
private fun generateAugmentedCallForAugmentedArraySetCall(
|
||||
unwrappedReceiver: T,
|
||||
receiver: FirExpression, // a.get(x,y)
|
||||
baseSource: KtSourceElement?,
|
||||
operation: FirOperation,
|
||||
rhs: T?,
|
||||
@@ -990,7 +1050,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
|
||||
calleeReference = buildSimpleNamedReference {
|
||||
name = FirOperationNameConventions.ASSIGNMENTS.getValue(operation)
|
||||
}
|
||||
explicitReceiver = unwrappedReceiver.convert()
|
||||
explicitReceiver = receiver
|
||||
argumentList = buildArgumentList {
|
||||
arguments += rhs?.convert() ?: buildErrorExpression(
|
||||
null,
|
||||
@@ -1003,7 +1063,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
|
||||
|
||||
|
||||
private fun generateSetGetBlockForAugmentedArraySetCall(
|
||||
unwrappedReceiver: T,
|
||||
receiver: FirExpression,
|
||||
baseSource: KtSourceElement?,
|
||||
arrayAccessSource: KtSourceElement?,
|
||||
operation: FirOperation,
|
||||
@@ -1020,7 +1080,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
|
||||
* }
|
||||
*/
|
||||
return buildBlock {
|
||||
val baseCall = unwrappedReceiver.convert() as FirFunctionCall
|
||||
val baseCall = receiver as FirFunctionCall
|
||||
|
||||
val arrayVariable = generateTemporaryVariable(
|
||||
baseModuleData,
|
||||
@@ -1105,7 +1165,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
|
||||
classTypeRefWithCorrectSourceKind: FirTypeRef,
|
||||
firPropertyReturnTypeRefWithCorrectSourceKind: FirTypeRef
|
||||
) =
|
||||
buildPropertyAccessExpression {
|
||||
buildPropertyAccessExpression {
|
||||
source = parameterSource
|
||||
typeRef = firPropertyReturnTypeRefWithCorrectSourceKind
|
||||
dispatchReceiver = buildThisReceiverExpression {
|
||||
|
||||
+18
-2
@@ -566,7 +566,10 @@ private fun FirExpression.checkReceiver(name: String?): Boolean {
|
||||
return receiverName == name
|
||||
}
|
||||
|
||||
fun FirQualifiedAccess.wrapWithSafeCall(receiver: FirExpression, source: KtSourceElement): FirSafeCallExpression {
|
||||
// this = .f(...)
|
||||
// receiver = <expr>
|
||||
// Returns safe call <expr>?.{ f(...) }
|
||||
fun FirQualifiedAccess.createSafeCall(receiver: FirExpression, source: KtSourceElement): FirSafeCallExpression {
|
||||
val checkedSafeCallSubject = buildCheckedSafeCallSubject {
|
||||
@OptIn(FirContractViolation::class)
|
||||
this.originalReceiverRef = FirExpressionRef<FirExpression>().apply {
|
||||
@@ -582,11 +585,24 @@ fun FirQualifiedAccess.wrapWithSafeCall(receiver: FirExpression, source: KtSourc
|
||||
this.checkedSubjectRef = FirExpressionRef<FirCheckedSafeCallSubject>().apply {
|
||||
bind(checkedSafeCallSubject)
|
||||
}
|
||||
this.selector = this@wrapWithSafeCall
|
||||
this.selector = this@createSafeCall
|
||||
this.source = source
|
||||
}
|
||||
}
|
||||
|
||||
// Turns (a?.b).f(...) to a?.{ b.f(...) ) -- for any qualified access `.f(...)`
|
||||
// Other patterns remain unchanged
|
||||
fun FirExpression.pullUpSafeCallIfNecessary(): FirExpression {
|
||||
if (this !is FirQualifiedAccess) return this
|
||||
val safeCall = explicitReceiver as? FirSafeCallExpression ?: return this
|
||||
val safeCallSelector = safeCall.selector as? FirExpression ?: return this
|
||||
|
||||
replaceExplicitReceiver(safeCallSelector)
|
||||
safeCall.replaceSelector(this)
|
||||
|
||||
return safeCall
|
||||
}
|
||||
|
||||
fun List<FirAnnotationCall>.filterUseSiteTarget(target: AnnotationUseSiteTarget): List<FirAnnotationCall> =
|
||||
mapNotNull {
|
||||
if (it.useSiteTarget != target) null
|
||||
|
||||
@@ -392,7 +392,7 @@ fun FirSafeCallExpression.propagateTypeFromQualifiedAccessAfterNullCheck(
|
||||
session: FirSession,
|
||||
) {
|
||||
val receiverType = nullableReceiverExpression.typeRef.coneTypeSafe<ConeKotlinType>()
|
||||
val typeAfterNullCheck = (selector as? FirQualifiedAccess)?.expressionTypeOrUnitForAssignment() ?: return
|
||||
val typeAfterNullCheck = selector.expressionTypeOrUnitForAssignment() ?: return
|
||||
val isReceiverActuallyNullable = if (session.languageVersionSettings.supportsFeature(LanguageFeature.SafeCallsAreAlwaysNullable)) {
|
||||
true
|
||||
} else {
|
||||
@@ -409,7 +409,7 @@ fun FirSafeCallExpression.propagateTypeFromQualifiedAccessAfterNullCheck(
|
||||
session.lookupTracker?.recordTypeResolveAsLookup(resolvedTypeRef, source, null)
|
||||
}
|
||||
|
||||
private fun FirQualifiedAccess.expressionTypeOrUnitForAssignment(): ConeKotlinType? {
|
||||
private fun FirStatement.expressionTypeOrUnitForAssignment(): ConeKotlinType? {
|
||||
if (this is FirExpression) return typeRef.coneTypeSafe()
|
||||
|
||||
require(this is FirVariableAssignment) {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
// WITH_STDLIB
|
||||
// WITH_COROUTINES
|
||||
// IGNORE_BACKEND: JVM
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// FIR status: VARIABLE_EXPECTED at b?.a += ... Not sure FIR supports this (contact Dmitry Novozhilov)
|
||||
|
||||
import kotlin.coroutines.*
|
||||
import helpers.*
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
|
||||
var cnt = 0
|
||||
|
||||
class A
|
||||
|
||||
var A?.b: A?
|
||||
get() {
|
||||
return this
|
||||
}
|
||||
set(v) {
|
||||
cnt++
|
||||
}
|
||||
|
||||
var A?.c: A?
|
||||
get() {
|
||||
return this
|
||||
}
|
||||
set(v) {
|
||||
cnt++
|
||||
}
|
||||
|
||||
operator fun A?.get(i: Int): A? = this
|
||||
operator fun A?.set(i: Int, v: A?): A? {
|
||||
cnt++
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
operator fun A?.plus(a: A?) = this
|
||||
|
||||
fun test(a: A?) {
|
||||
a?.b += null
|
||||
a?.b?.c += null
|
||||
a?.b.c += null // ".c" will be called anyway
|
||||
|
||||
a?.b[0] += null
|
||||
a?.b?.c[0] += null
|
||||
a?.b.c[0] += null // ".c" will be called anyway
|
||||
|
||||
a?.b[0][0] += null
|
||||
a?.b?.c[0][0] += null
|
||||
a?.b.c[0][0] += null // ".c" will be called anyway
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
test(null)
|
||||
if (cnt != 3) return "fail 1: $cnt"
|
||||
|
||||
cnt = 0
|
||||
test(A())
|
||||
if (cnt != 9) return "fail 2: $cnt"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
|
||||
var cnt = 0
|
||||
|
||||
class A
|
||||
|
||||
var A?.b: A?
|
||||
get() {
|
||||
return this
|
||||
}
|
||||
set(v) {
|
||||
cnt++
|
||||
}
|
||||
|
||||
var A?.c: A?
|
||||
get() {
|
||||
return this
|
||||
}
|
||||
set(v) {
|
||||
cnt++
|
||||
}
|
||||
|
||||
operator fun A?.get(i: Int): A? = this
|
||||
|
||||
operator fun A?.plusAssign(a: A?) {
|
||||
cnt++
|
||||
}
|
||||
|
||||
fun test(a: A?) {
|
||||
a?.b += null
|
||||
a?.b?.c += null
|
||||
a?.b.c += null // ".c" will be called anyway
|
||||
|
||||
a?.b[0] += null
|
||||
a?.b?.c[0] += null
|
||||
a?.b.c[0] += null // ".c" will be called anyway
|
||||
|
||||
a?.b[0][0] += null
|
||||
a?.b?.c[0][0] += null
|
||||
a?.b.c[0][0] += null // ".c" will be called anyway
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
test(null)
|
||||
if (cnt != 3) return "fail 1: $cnt"
|
||||
|
||||
cnt = 0
|
||||
test(A())
|
||||
if (cnt != 9) return "fail 2: $cnt"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
|
||||
var cnt = 0
|
||||
|
||||
class A
|
||||
|
||||
var A?.b: A?
|
||||
get() {
|
||||
return this
|
||||
}
|
||||
set(v) {
|
||||
cnt++
|
||||
}
|
||||
|
||||
var A?.c: A?
|
||||
get() {
|
||||
return this
|
||||
}
|
||||
set(v) {
|
||||
cnt++
|
||||
}
|
||||
|
||||
operator fun A?.get(i: Int): A? = this
|
||||
operator fun A?.set(i: Int, v: A?): A? {
|
||||
cnt++
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
operator fun A?.inc(): A? {
|
||||
return this
|
||||
}
|
||||
|
||||
fun test(a: A?) {
|
||||
a?.b++
|
||||
a?.b?.c++
|
||||
a?.b.c++ // ".c" will be called anyway
|
||||
|
||||
a?.b[0]++
|
||||
a?.b?.c[0]++
|
||||
a?.b.c[0]++ // ".c" will be called anyway
|
||||
|
||||
a?.b[0][0]++
|
||||
a?.b?.c[0][0]++
|
||||
a?.b.c[0][0]++ // ".c" will be called anyway
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
test(null)
|
||||
if (cnt != 3) return "fail 1: $cnt"
|
||||
|
||||
cnt = 0
|
||||
test(A())
|
||||
if (cnt != 9) return "fail 2: $cnt"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
|
||||
var cnt = 0
|
||||
|
||||
class A
|
||||
|
||||
var A?.b: A?
|
||||
get() {
|
||||
return this
|
||||
}
|
||||
set(v) {
|
||||
cnt++
|
||||
}
|
||||
|
||||
var A?.c: A?
|
||||
get() {
|
||||
return this
|
||||
}
|
||||
set(v) {
|
||||
cnt++
|
||||
}
|
||||
|
||||
operator fun A?.get(i: Int): A? = this
|
||||
operator fun A?.set(i: Int, v: A?): A? {
|
||||
cnt++
|
||||
return this
|
||||
}
|
||||
|
||||
operator fun A?.inc(): A? {
|
||||
return this
|
||||
}
|
||||
|
||||
fun test(a: A?) {
|
||||
++a?.b
|
||||
++a?.b?.c
|
||||
++a?.b.c // ".c" will be called anyway
|
||||
|
||||
++a?.b[0]
|
||||
++a?.b?.c[0]
|
||||
++a?.b.c[0] // ".c" will be called anyway
|
||||
|
||||
++a?.b[0][0]
|
||||
++a?.b?.c[0][0]
|
||||
++a?.b.c[0][0] // ".c" will be called anyway
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
test(null)
|
||||
if (cnt != 3) return "fail 1: $cnt"
|
||||
|
||||
cnt = 0
|
||||
test(A())
|
||||
if (cnt != 9) return "fail 2: $cnt"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
|
||||
var cnt = 0
|
||||
|
||||
class A
|
||||
|
||||
var A?.b: A?
|
||||
get() {
|
||||
return this
|
||||
}
|
||||
set(v) {
|
||||
cnt++
|
||||
}
|
||||
|
||||
var A?.c: A?
|
||||
get() {
|
||||
return this
|
||||
}
|
||||
set(v) {
|
||||
cnt++
|
||||
}
|
||||
|
||||
operator fun A?.get(i: Int): A? = this
|
||||
operator fun A?.set(i: Int, v: A?): A? {
|
||||
cnt++
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
fun test(a: A?) {
|
||||
a?.b = null
|
||||
a?.b?.c = null
|
||||
a?.b.c = null // ".c" will be called anyway
|
||||
|
||||
a?.b[0] = null
|
||||
a?.b?.c[0] = null
|
||||
a?.b.c[0] = null // ".c" will be called anyway
|
||||
|
||||
a?.b[0][0] = null
|
||||
a?.b?.c[0][0] = null
|
||||
a?.b.c[0][0] = null // ".c" will be called anyway
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
test(null)
|
||||
if (cnt != 3) return "fail 1: $cnt"
|
||||
|
||||
cnt = 0
|
||||
test(A())
|
||||
if (cnt != 9) return "fail 2: $cnt"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+30
@@ -41267,12 +41267,36 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/safeCall"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("augmentedAssigmentPlus.kt")
|
||||
public void testAugmentedAssigmentPlus() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/safeCall/augmentedAssigmentPlus.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("augmentedAssigmentPlusAssign.kt")
|
||||
public void testAugmentedAssigmentPlusAssign() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/safeCall/augmentedAssigmentPlusAssign.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("genericNull.kt")
|
||||
public void testGenericNull() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/safeCall/genericNull.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("incrementPostfix.kt")
|
||||
public void testIncrementPostfix() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/safeCall/incrementPostfix.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("incrementPrefix.kt")
|
||||
public void testIncrementPrefix() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/safeCall/incrementPrefix.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("kt1572.kt")
|
||||
public void testKt1572() throws Exception {
|
||||
@@ -41368,6 +41392,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
public void testSafeCallWithElvisFolding() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/safeCall/safeCallWithElvisFolding.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("withAssignment.kt")
|
||||
public void testWithAssignment() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/safeCall/withAssignment.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@Nested
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ class A() {
|
||||
|
||||
fun case1() {
|
||||
var b: Case1? = Case1()
|
||||
<!UNSAFE_CALL!>--<!>b?.a
|
||||
--b?.a
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ class A() {
|
||||
|
||||
fun case1() {
|
||||
var b: Case1? = Case1()
|
||||
<!UNSAFE_CALL!>++<!>b?.a
|
||||
++b?.a
|
||||
}
|
||||
|
||||
|
||||
|
||||
+6
-6
@@ -21,15 +21,15 @@ package testPackCase2
|
||||
|
||||
fun case2(a: A?, c: C) {
|
||||
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>a?.b += c<!>
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase2.B.plusAssign; typeCall: operator function")!>a?.b += c<!>
|
||||
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>plusAssign(c)<!>
|
||||
|
||||
val x = {
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>a?.b += c<!>
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase2.B.plusAssign; typeCall: operator function")!>a?.b += c<!>
|
||||
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>plusAssign(c)<!>
|
||||
}()
|
||||
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>a?.b += { c }()<!>
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase2.B.plusAssign; typeCall: operator function")!>a?.b += { c }()<!>
|
||||
|
||||
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>plusAssign({ c }())<!>
|
||||
|
||||
@@ -60,15 +60,15 @@ package testPackCase3
|
||||
|
||||
fun case3(a: A?, c: C) {
|
||||
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>a?.b += c<!>
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase3.B.plusAssign; typeCall: operator function")!>a?.b += c<!>
|
||||
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>plusAssign(c)<!>
|
||||
|
||||
val x = {
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>a?.b += c<!>
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase3.B.plusAssign; typeCall: operator function")!>a?.b += c<!>
|
||||
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>plusAssign(c)<!>
|
||||
}()
|
||||
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>a?.b += { c }()<!>
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase3.B.plusAssign; typeCall: operator function")!>a?.b += { c }()<!>
|
||||
|
||||
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>plusAssign({ c }())<!>
|
||||
|
||||
|
||||
+6
-6
@@ -43,15 +43,15 @@ package testPackCase2
|
||||
|
||||
fun case2(a: A?, c: C) {
|
||||
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>a?.b += c<!>
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase2.B.plusAssign; typeCall: operator function")!>a?.b += c<!>
|
||||
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>plusAssign(c)<!>
|
||||
|
||||
val x = {
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>a?.b += c<!>
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase2.B.plusAssign; typeCall: operator function")!>a?.b += c<!>
|
||||
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>plusAssign(c)<!>
|
||||
}()
|
||||
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>a?.b += { c }()<!>
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase2.B.plusAssign; typeCall: operator function")!>a?.b += { c }()<!>
|
||||
|
||||
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>plusAssign({ c }())<!>
|
||||
|
||||
@@ -81,15 +81,15 @@ package testPackCase3
|
||||
|
||||
fun case3(a: A?, c: C) {
|
||||
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>a?.b += c<!>
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase3.B.plusAssign; typeCall: operator function")!>a?.b += c<!>
|
||||
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>plusAssign(c)<!>
|
||||
|
||||
val x = {
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>a?.b += c<!>
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase3.B.plusAssign; typeCall: operator function")!>a?.b += c<!>
|
||||
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>plusAssign(c)<!>
|
||||
}()
|
||||
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>a?.b += { c }()<!>
|
||||
<!DEBUG_INFO_CALL("fqName: testPackCase3.B.plusAssign; typeCall: operator function")!>a?.b += { c }()<!>
|
||||
|
||||
a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>plusAssign({ c }())<!>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// TESTCASE NUMBER: 1
|
||||
fun case_1(x: Class?) {
|
||||
x!!
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("Class? & 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?")!>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()
|
||||
}
|
||||
|
||||
+18
@@ -433,6 +433,24 @@ public class FirVisualizerForRawFirDataGenerated extends AbstractFirVisualizerTe
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/qualifierWithTypeArguments.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("safeCallsWithAssignment.kt")
|
||||
public void testSafeCallsWithAssignment() throws Exception {
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAssignment.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("safeCallsWithAugmentedAssignment.kt")
|
||||
public void testSafeCallsWithAugmentedAssignment() throws Exception {
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAugmentedAssignment.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("safeCallsWithUnaryOperators.kt")
|
||||
public void testSafeCallsWithUnaryOperators() throws Exception {
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithUnaryOperators.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("simpleReturns.kt")
|
||||
public void testSimpleReturns() throws Exception {
|
||||
|
||||
+18
@@ -433,6 +433,24 @@ public class PsiVisualizerForRawFirDataGenerated extends AbstractPsiVisualizerTe
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/qualifierWithTypeArguments.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("safeCallsWithAssignment.kt")
|
||||
public void testSafeCallsWithAssignment() throws Exception {
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAssignment.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("safeCallsWithAugmentedAssignment.kt")
|
||||
public void testSafeCallsWithAugmentedAssignment() throws Exception {
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAugmentedAssignment.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("safeCallsWithUnaryOperators.kt")
|
||||
public void testSafeCallsWithUnaryOperators() throws Exception {
|
||||
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithUnaryOperators.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("simpleReturns.kt")
|
||||
public void testSimpleReturns() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user