FIR: Change semantics for combination of safe calls and operators

^KT-41034 Fixed
This commit is contained in:
Denis.Zharkov
2022-01-26 18:19:17 +03:00
parent 0dd5042e62
commit 772579143b
34 changed files with 811 additions and 68 deletions
@@ -433,6 +433,24 @@ public class FirLazyBodiesCalculatorTestGenerated extends AbstractFirLazyBodiesC
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/qualifierWithTypeArguments.kt"); 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 @Test
@TestMetadata("simpleReturns.kt") @TestMetadata("simpleReturns.kt")
public void testSimpleReturns() throws Exception { public void testSimpleReturns() throws Exception {
@@ -391,7 +391,9 @@ class Fir2IrVisitor(
) )
return conversionScope.withSafeCallSubject(receiverVariable) { 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) components.createSafeCallConstruction(receiverVariable, variableSymbol, afterNotNullCheck)
} }
} }
@@ -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); 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 @Test
@TestMetadata("genericNull.kt") @TestMetadata("genericNull.kt")
public void testGenericNull() throws Exception { public void testGenericNull() throws Exception {
runTest("compiler/testData/codegen/box/safeCall/genericNull.kt"); 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 @Test
@TestMetadata("kt1572.kt") @TestMetadata("kt1572.kt")
public void testKt1572() throws Exception { public void testKt1572() throws Exception {
@@ -41368,6 +41392,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
public void testSafeCallWithElvisFolding() throws Exception { public void testSafeCallWithElvisFolding() throws Exception {
runTest("compiler/testData/codegen/box/safeCall/safeCallWithElvisFolding.kt"); 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 @Nested
@@ -12,8 +12,8 @@ import org.jetbrains.kotlin.ElementTypeUtils.getOperationSymbol
import org.jetbrains.kotlin.ElementTypeUtils.isExpression import org.jetbrains.kotlin.ElementTypeUtils.isExpression
import org.jetbrains.kotlin.KtFakeSourceElementKind import org.jetbrains.kotlin.KtFakeSourceElementKind
import org.jetbrains.kotlin.KtLightSourceElement import org.jetbrains.kotlin.KtLightSourceElement
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.KtNodeTypes.* import org.jetbrains.kotlin.KtNodeTypes.*
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.fakeElement import org.jetbrains.kotlin.fakeElement
@@ -556,7 +556,7 @@ class ExpressionsConverter(
if (isSafe) { if (isSafe) {
@OptIn(FirImplementationDetail::class) @OptIn(FirImplementationDetail::class)
it.replaceSource(dotQualifiedExpression.toFirSourceElement(KtFakeSourceElementKind.DesugaredSafeCallExpression)) it.replaceSource(dotQualifiedExpression.toFirSourceElement(KtFakeSourceElementKind.DesugaredSafeCallExpression))
return it.wrapWithSafeCall( return it.createSafeCall(
firReceiver!!, firReceiver!!,
dotQualifiedExpression.toFirSourceElement() dotQualifiedExpression.toFirSourceElement()
) )
@@ -918,7 +918,7 @@ class ExpressionsConverter(
* @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parseArrayAccess * @see org.jetbrains.kotlin.parsing.KotlinExpressionParsing.parseArrayAccess
* @see org.jetbrains.kotlin.fir.builder.RawFirBuilder.Visitor.visitArrayAccessExpression * @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 var firExpression: FirExpression? = null
val indices: MutableList<FirExpression> = mutableListOf() val indices: MutableList<FirExpression> = mutableListOf()
arrayAccess.forEachChildren { arrayAccess.forEachChildren {
@@ -942,7 +942,7 @@ class ExpressionsConverter(
getArgument?.let { arguments += it } getArgument?.let { arguments += it }
} }
origin = FirFunctionCallOrigin.Operator origin = FirFunctionCallOrigin.Operator
} }.pullUpSafeCallIfNecessary()
} }
/** /**
@@ -393,6 +393,21 @@ public class LightTree2FirConverterTestCaseGenerated extends AbstractLightTree2F
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/qualifierWithTypeArguments.kt"); 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") @TestMetadata("simpleReturns.kt")
public void testSimpleReturns() throws Exception { public void testSimpleReturns() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/simpleReturns.kt"); 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.*
import org.jetbrains.kotlin.builtins.StandardNames.BACKING_FIELD import org.jetbrains.kotlin.builtins.StandardNames.BACKING_FIELD
import org.jetbrains.kotlin.builtins.StandardNames.DEFAULT_VALUE_PARAMETER 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.descriptors.annotations.AnnotationUseSiteTarget.*
import org.jetbrains.kotlin.fir.* import org.jetbrains.kotlin.fir.*
import org.jetbrains.kotlin.fir.contracts.FirContractDescription import org.jetbrains.kotlin.fir.contracts.FirContractDescription
@@ -2354,7 +2357,7 @@ open class RawFirBuilder(
} }
} }
origin = FirFunctionCallOrigin.Operator origin = FirFunctionCallOrigin.Operator
} }.pullUpSafeCallIfNecessary()
} }
override fun visitQualifiedExpression(expression: KtQualifiedExpression, data: Unit): FirElement { override fun visitQualifiedExpression(expression: KtQualifiedExpression, data: Unit): FirElement {
@@ -2369,7 +2372,7 @@ open class RawFirBuilder(
if (expression is KtSafeQualifiedExpression) { if (expression is KtSafeQualifiedExpression) {
@OptIn(FirImplementationDetail::class) @OptIn(FirImplementationDetail::class)
firSelector.replaceSource(expression.toFirSourceElement(KtFakeSourceElementKind.DesugaredSafeCallExpression)) firSelector.replaceSource(expression.toFirSourceElement(KtFakeSourceElementKind.DesugaredSafeCallExpression))
return firSelector.wrapWithSafeCall( return firSelector.createSafeCall(
receiver, receiver,
expression.toFirSourceElement() expression.toFirSourceElement()
) )
@@ -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
}
@@ -0,0 +1,2 @@
FILE: safeCallsWithAssignment.kt
public? final? fun foo(): R|kotlin/Unit| { LAZY_BLOCK }
@@ -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)
}
@@ -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
}
@@ -0,0 +1,2 @@
FILE: safeCallsWithAugmentedAssignment.kt
public? final? fun foo(): R|kotlin/Unit| { LAZY_BLOCK }
@@ -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))
}
@@ -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()
}
@@ -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 }
@@ -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>|
}
@@ -393,6 +393,21 @@ public class RawFirBuilderLazyBodiesTestCaseGenerated extends AbstractRawFirBuil
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/qualifierWithTypeArguments.kt"); 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") @TestMetadata("simpleReturns.kt")
public void testSimpleReturns() throws Exception { public void testSimpleReturns() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/simpleReturns.kt"); runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/simpleReturns.kt");
@@ -393,6 +393,21 @@ public class RawFirBuilderTestCaseGenerated extends AbstractRawFirBuilderTestCas
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/qualifierWithTypeArguments.kt"); 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") @TestMetadata("simpleReturns.kt")
public void testSimpleReturns() throws Exception { public void testSimpleReturns() throws Exception {
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/simpleReturns.kt"); runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/simpleReturns.kt");
@@ -489,7 +489,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
diagnostic = ConeSimpleDiagnostic("Inc/dec without operand", DiagnosticKind.Syntax) 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( return generateIncrementOrDecrementBlockForQualifiedAccess(
wholeExpression, wholeExpression,
operationReference, operationReference,
@@ -670,35 +670,35 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
private fun generateIncrementOrDecrementBlockForQualifiedAccess( private fun generateIncrementOrDecrementBlockForQualifiedAccess(
wholeExpression: T, wholeExpression: T,
operationReference: T?, operationReference: T?,
receiverForOperation: T, receiverForOperation: T, // a.b
callName: Name, callName: Name,
prefix: Boolean, prefix: Boolean,
convert: T.() -> FirExpression convert: T.() -> FirExpression
): 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 baseSource = wholeExpression?.toFirSourceElement()
val desugaredSource = baseSource?.fakeElement(KtFakeSourceElementKind.DesugaredIncrementOrDecrement) val desugaredSource = baseSource?.fakeElement(KtFakeSourceElementKind.DesugaredIncrementOrDecrement)
source = desugaredSource source = desugaredSource
val argumentReceiver = receiverForOperation.receiverExpression
val argumentSelector = receiverForOperation.selectorExpression
val argumentReceiverVariable = generateTemporaryVariable( val argumentReceiverVariable = generateTemporaryVariable(
baseModuleData, baseModuleData,
argumentReceiver?.toFirSourceElement(), argumentReceiver?.toFirSourceElement(),
Name.special("<receiver>"), Name.special("<receiver>"),
argumentReceiver?.convert() ?: buildErrorExpression { initializer = receiverFir,
source = receiverForOperation.toFirSourceElement()
diagnostic = ConeSimpleDiagnostic("Qualified expression without receiver", DiagnosticKind.Syntax)
}
).also { statements += it } ).also { statements += it }
val firArgument = generateResolvedAccessExpression(argumentReceiverVariable.source, argumentReceiverVariable).let { receiver -> val firArgument = generateResolvedAccessExpression(argumentReceiverVariable.source, argumentReceiverVariable).let { receiver ->
val firArgumentSelector = argumentSelector?.convert() ?: buildErrorExpression { qualifiedFir.also { if (it is FirQualifiedAccessExpression) it.replaceExplicitReceiver(receiver) }
source = receiverForOperation.toFirSourceElement()
diagnostic = ConeSimpleDiagnostic("Qualified expression without selector", DiagnosticKind.Syntax)
}
firArgumentSelector.also { if (it is FirQualifiedAccessExpression) it.replaceExplicitReceiver(receiver) }
} }
putIncrementOrDecrementStatements( putIncrementOrDecrementStatements(
@@ -759,12 +759,14 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
prefix: Boolean, prefix: Boolean,
convert: T.() -> FirExpression convert: T.() -> FirExpression
): FirExpression { ): FirExpression {
return buildBlock { val array = receiver.arrayExpression
return buildBlockProbablyUnderSafeCall(
array, convert, receiver.toFirSourceElement(),
) { arrayReceiver ->
val baseSource = wholeExpression?.toFirSourceElement() val baseSource = wholeExpression?.toFirSourceElement()
val desugaredSource = baseSource?.fakeElement(KtFakeSourceElementKind.DesugaredIncrementOrDecrement) val desugaredSource = baseSource?.fakeElement(KtFakeSourceElementKind.DesugaredIncrementOrDecrement)
source = desugaredSource source = desugaredSource
val array = receiver.arrayExpression
val indices = receiver.indexExpressions val indices = receiver.indexExpressions
requireNotNull(indices) { "No indices in ${wholeExpression.asText}" } requireNotNull(indices) { "No indices in ${wholeExpression.asText}" }
@@ -772,10 +774,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
baseModuleData, baseModuleData,
array?.toFirSourceElement(), array?.toFirSourceElement(),
Name.special("<array>"), Name.special("<array>"),
array?.convert() ?: buildErrorExpression { initializer = arrayReceiver,
source = receiver.toFirSourceElement()
diagnostic = ConeSimpleDiagnostic("No array expression", DiagnosticKind.Syntax)
}
).also { statements += it } ).also { statements += it }
val indexVariables = indices.mapIndexed { i, index -> 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( private fun FirQualifiedAccessBuilder.initializeLValue(
left: T?, left: T?,
convertQualified: T.() -> FirQualifiedAccess? convertQualified: T.() -> FirQualifiedAccess?
@@ -893,26 +928,51 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
return if (operation == FirOperation.ASSIGN) { return if (operation == FirOperation.ASSIGN) {
val result = unwrappedLhs.convert() val result = unwrappedLhs.convert()
(result.annotations as MutableList<FirAnnotation>) += annotations (result.annotations as MutableList<FirAnnotation>) += annotations
result result.pullUpSafeCallIfNecessary()
} else { } 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) { 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 source = baseSource
this.operation = operation this.operation = operation
leftArgument = this@generateAssignment?.convert() leftArgument = receiverToUse ?: buildErrorExpression {
?: buildErrorExpression { source = null
source = null diagnostic = ConeSimpleDiagnostic(
diagnostic = ConeSimpleDiagnostic( "Unsupported left value of assignment: ${baseSource?.psi?.text}", DiagnosticKind.ExpressionExpected
"Unsupported left value of assignment: ${baseSource?.psi?.text}", DiagnosticKind.ExpressionExpected )
) }
}
rightArgument = rhsExpression rightArgument = rhsExpression
this.annotations += annotations this.annotations += annotations
} }
return if (lhsReceiver is FirSafeCallExpression) {
lhsReceiver.replaceSelector(result)
lhsReceiver
} else {
result
}
} }
require(operation == FirOperation.ASSIGN) require(operation == FirOperation.ASSIGN)
@@ -956,7 +1016,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
} }
private fun generateAugmentedArraySetCall( private fun generateAugmentedArraySetCall(
unwrappedReceiver: T, receiver: FirExpression, // a.get(x,y)
baseSource: KtSourceElement?, baseSource: KtSourceElement?,
arrayAccessSource: KtSourceElement?, arrayAccessSource: KtSourceElement?,
operation: FirOperation, operation: FirOperation,
@@ -967,15 +1027,15 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
return buildAugmentedArraySetCall { return buildAugmentedArraySetCall {
source = baseSource source = baseSource
this.operation = operation this.operation = operation
assignCall = generateAugmentedCallForAugmentedArraySetCall(unwrappedReceiver, baseSource, operation, rhs, convert) assignCall = generateAugmentedCallForAugmentedArraySetCall(receiver, baseSource, operation, rhs, convert)
setGetBlock = setGetBlock =
generateSetGetBlockForAugmentedArraySetCall(unwrappedReceiver, baseSource, arrayAccessSource, operation, rhs, convert) generateSetGetBlockForAugmentedArraySetCall(receiver, baseSource, arrayAccessSource, operation, rhs, convert)
this.annotations += annotations this.annotations += annotations
} }
} }
private fun generateAugmentedCallForAugmentedArraySetCall( private fun generateAugmentedCallForAugmentedArraySetCall(
unwrappedReceiver: T, receiver: FirExpression, // a.get(x,y)
baseSource: KtSourceElement?, baseSource: KtSourceElement?,
operation: FirOperation, operation: FirOperation,
rhs: T?, rhs: T?,
@@ -990,7 +1050,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
calleeReference = buildSimpleNamedReference { calleeReference = buildSimpleNamedReference {
name = FirOperationNameConventions.ASSIGNMENTS.getValue(operation) name = FirOperationNameConventions.ASSIGNMENTS.getValue(operation)
} }
explicitReceiver = unwrappedReceiver.convert() explicitReceiver = receiver
argumentList = buildArgumentList { argumentList = buildArgumentList {
arguments += rhs?.convert() ?: buildErrorExpression( arguments += rhs?.convert() ?: buildErrorExpression(
null, null,
@@ -1003,7 +1063,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
private fun generateSetGetBlockForAugmentedArraySetCall( private fun generateSetGetBlockForAugmentedArraySetCall(
unwrappedReceiver: T, receiver: FirExpression,
baseSource: KtSourceElement?, baseSource: KtSourceElement?,
arrayAccessSource: KtSourceElement?, arrayAccessSource: KtSourceElement?,
operation: FirOperation, operation: FirOperation,
@@ -1020,7 +1080,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
* } * }
*/ */
return buildBlock { return buildBlock {
val baseCall = unwrappedReceiver.convert() as FirFunctionCall val baseCall = receiver as FirFunctionCall
val arrayVariable = generateTemporaryVariable( val arrayVariable = generateTemporaryVariable(
baseModuleData, baseModuleData,
@@ -1105,7 +1165,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
classTypeRefWithCorrectSourceKind: FirTypeRef, classTypeRefWithCorrectSourceKind: FirTypeRef,
firPropertyReturnTypeRefWithCorrectSourceKind: FirTypeRef firPropertyReturnTypeRefWithCorrectSourceKind: FirTypeRef
) = ) =
buildPropertyAccessExpression { buildPropertyAccessExpression {
source = parameterSource source = parameterSource
typeRef = firPropertyReturnTypeRefWithCorrectSourceKind typeRef = firPropertyReturnTypeRefWithCorrectSourceKind
dispatchReceiver = buildThisReceiverExpression { dispatchReceiver = buildThisReceiverExpression {
@@ -566,7 +566,10 @@ private fun FirExpression.checkReceiver(name: String?): Boolean {
return receiverName == name 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 { val checkedSafeCallSubject = buildCheckedSafeCallSubject {
@OptIn(FirContractViolation::class) @OptIn(FirContractViolation::class)
this.originalReceiverRef = FirExpressionRef<FirExpression>().apply { this.originalReceiverRef = FirExpressionRef<FirExpression>().apply {
@@ -582,11 +585,24 @@ fun FirQualifiedAccess.wrapWithSafeCall(receiver: FirExpression, source: KtSourc
this.checkedSubjectRef = FirExpressionRef<FirCheckedSafeCallSubject>().apply { this.checkedSubjectRef = FirExpressionRef<FirCheckedSafeCallSubject>().apply {
bind(checkedSafeCallSubject) bind(checkedSafeCallSubject)
} }
this.selector = this@wrapWithSafeCall this.selector = this@createSafeCall
this.source = source 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> = fun List<FirAnnotationCall>.filterUseSiteTarget(target: AnnotationUseSiteTarget): List<FirAnnotationCall> =
mapNotNull { mapNotNull {
if (it.useSiteTarget != target) null if (it.useSiteTarget != target) null
@@ -392,7 +392,7 @@ fun FirSafeCallExpression.propagateTypeFromQualifiedAccessAfterNullCheck(
session: FirSession, session: FirSession,
) { ) {
val receiverType = nullableReceiverExpression.typeRef.coneTypeSafe<ConeKotlinType>() 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)) { val isReceiverActuallyNullable = if (session.languageVersionSettings.supportsFeature(LanguageFeature.SafeCallsAreAlwaysNullable)) {
true true
} else { } else {
@@ -409,7 +409,7 @@ fun FirSafeCallExpression.propagateTypeFromQualifiedAccessAfterNullCheck(
session.lookupTracker?.recordTypeResolveAsLookup(resolvedTypeRef, source, null) session.lookupTracker?.recordTypeResolveAsLookup(resolvedTypeRef, source, null)
} }
private fun FirQualifiedAccess.expressionTypeOrUnitForAssignment(): ConeKotlinType? { private fun FirStatement.expressionTypeOrUnitForAssignment(): ConeKotlinType? {
if (this is FirExpression) return typeRef.coneTypeSafe() if (this is FirExpression) return typeRef.coneTypeSafe()
require(this is FirVariableAssignment) { require(this is FirVariableAssignment) {
-2
View File
@@ -1,8 +1,6 @@
// WITH_STDLIB // WITH_STDLIB
// WITH_COROUTINES // WITH_COROUTINES
// IGNORE_BACKEND: JVM // 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 kotlin.coroutines.*
import helpers.* 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"
}
@@ -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); 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 @Test
@TestMetadata("genericNull.kt") @TestMetadata("genericNull.kt")
public void testGenericNull() throws Exception { public void testGenericNull() throws Exception {
runTest("compiler/testData/codegen/box/safeCall/genericNull.kt"); 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 @Test
@TestMetadata("kt1572.kt") @TestMetadata("kt1572.kt")
public void testKt1572() throws Exception { public void testKt1572() throws Exception {
@@ -41368,6 +41392,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
public void testSafeCallWithElvisFolding() throws Exception { public void testSafeCallWithElvisFolding() throws Exception {
runTest("compiler/testData/codegen/box/safeCall/safeCallWithElvisFolding.kt"); 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 @Nested
@@ -15,7 +15,7 @@ class A() {
fun case1() { fun case1() {
var b: Case1? = Case1() var b: Case1? = Case1()
<!UNSAFE_CALL!>--<!>b?.a --b?.a
} }
@@ -15,7 +15,7 @@ class A() {
fun case1() { fun case1() {
var b: Case1? = Case1() var b: Case1? = Case1()
<!UNSAFE_CALL!>++<!>b?.a ++b?.a
} }
@@ -21,15 +21,15 @@ package testPackCase2
fun case2(a: A?, c: C) { 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)<!> a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>plusAssign(c)<!>
val x = { 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)<!> 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 }())<!> 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) { 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)<!> a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>plusAssign(c)<!>
val x = { 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)<!> 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 }())<!> a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>plusAssign({ c }())<!>
@@ -43,15 +43,15 @@ package testPackCase2
fun case2(a: A?, c: C) { 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)<!> a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase2.plusAssign; typeCall: operator extension function")!>plusAssign(c)<!>
val x = { 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)<!> 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 }())<!> 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) { 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)<!> a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>plusAssign(c)<!>
val x = { 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)<!> 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 }())<!> a?.b.<!DEBUG_INFO_CALL("fqName: testPackCase3.plusAssign; typeCall: operator extension function")!>plusAssign({ c }())<!>
@@ -5,7 +5,7 @@
// TESTCASE NUMBER: 1 // TESTCASE NUMBER: 1
fun case_1(x: Class?) { fun case_1(x: Class?) {
x!! 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<!>
<!DEBUG_INFO_EXPRESSION_TYPE("Class? & Class")!>x<!>[0].inv() <!DEBUG_INFO_EXPRESSION_TYPE("Class? & Class")!>x<!>[0].inv()
} }
@@ -433,6 +433,24 @@ public class FirVisualizerForRawFirDataGenerated extends AbstractFirVisualizerTe
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/qualifierWithTypeArguments.kt"); 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 @Test
@TestMetadata("simpleReturns.kt") @TestMetadata("simpleReturns.kt")
public void testSimpleReturns() throws Exception { public void testSimpleReturns() throws Exception {
@@ -433,6 +433,24 @@ public class PsiVisualizerForRawFirDataGenerated extends AbstractPsiVisualizerTe
runTest("compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/qualifierWithTypeArguments.kt"); 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 @Test
@TestMetadata("simpleReturns.kt") @TestMetadata("simpleReturns.kt")
public void testSimpleReturns() throws Exception { public void testSimpleReturns() throws Exception {