[FIR] Let prefix inc/dec call getter twice for compatibility with K1

#KT-57179 Fixed
This commit is contained in:
Kirill Rakhman
2023-03-15 17:24:40 +01:00
committed by Space Team
parent fea344faa4
commit 45d2424ad8
50 changed files with 1391 additions and 794 deletions
@@ -711,6 +711,18 @@ public class FirLightTreeOldFrontendDiagnosticsTestGenerated extends AbstractFir
runTest("compiler/testData/diagnostics/tests/PackageQualified.kt");
}
@Test
@TestMetadata("prefixIncReturnType.kt")
public void testPrefixIncReturnType() throws Exception {
runTest("compiler/testData/diagnostics/tests/prefixIncReturnType.kt");
}
@Test
@TestMetadata("prefixIncSmartCast.kt")
public void testPrefixIncSmartCast() throws Exception {
runTest("compiler/testData/diagnostics/tests/prefixIncSmartCast.kt");
}
@Test
@TestMetadata("PrimaryConstructors.kt")
public void testPrimaryConstructors() throws Exception {
@@ -711,6 +711,18 @@ public class FirPsiOldFrontendDiagnosticsTestGenerated extends AbstractFirPsiDia
runTest("compiler/testData/diagnostics/tests/PackageQualified.kt");
}
@Test
@TestMetadata("prefixIncReturnType.kt")
public void testPrefixIncReturnType() throws Exception {
runTest("compiler/testData/diagnostics/tests/prefixIncReturnType.kt");
}
@Test
@TestMetadata("prefixIncSmartCast.kt")
public void testPrefixIncSmartCast() throws Exception {
runTest("compiler/testData/diagnostics/tests/prefixIncSmartCast.kt");
}
@Test
@TestMetadata("PrimaryConstructors.kt")
public void testPrimaryConstructors() throws Exception {
@@ -141,6 +141,12 @@ class ErrorNodeDiagnosticCollectorComponent(
// See FirForLoopChecker
return
}
// Prefix inc/dec on array access will have two calls to .get(...), don't report for the second one.
if (source.kind == KtFakeSourceElementKind.DesugaredPrefixSecondGetReference) {
return
}
for (coneDiagnostic in diagnostic.toFirDiagnostics(session, source, qualifiedAccessSource)) {
reporter.report(coneDiagnostic, context)
}
@@ -653,6 +653,13 @@ class Fir2IrVisitor(
return callGenerator.convertToIrSetCall(variableAssignment, explicitReceiverExpression)
}
override fun visitDesugaredAssignmentValueReferenceExpression(
desugaredAssignmentValueReferenceExpression: FirDesugaredAssignmentValueReferenceExpression,
data: Any?
): IrElement {
return desugaredAssignmentValueReferenceExpression.expressionRef.value.accept(this, null)
}
override fun <T> visitConstExpression(constExpression: FirConstExpression<T>, data: Any?): IrElement {
return constExpression.toIrConst(constExpression.typeRef.toIrType())
}
@@ -26933,12 +26933,6 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr
runTest("compiler/testData/codegen/box/intrinsics/prefixIncDec.kt");
}
@Test
@TestMetadata("prefixIncDecFir.kt")
public void testPrefixIncDecFir() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/prefixIncDecFir.kt");
}
@Test
@TestMetadata("rangeFromCollection.kt")
public void testRangeFromCollection() throws Exception {
@@ -49309,24 +49303,12 @@ public class FirLightTreeBlackBoxCodegenTestGenerated extends AbstractFirLightTr
runTest("compiler/testData/codegen/box/statics/incInClassObject.kt");
}
@Test
@TestMetadata("incInClassObjectFir.kt")
public void testIncInClassObjectFir() throws Exception {
runTest("compiler/testData/codegen/box/statics/incInClassObjectFir.kt");
}
@Test
@TestMetadata("incInObject.kt")
public void testIncInObject() throws Exception {
runTest("compiler/testData/codegen/box/statics/incInObject.kt");
}
@Test
@TestMetadata("incInObjectFir.kt")
public void testIncInObjectFir() throws Exception {
runTest("compiler/testData/codegen/box/statics/incInObjectFir.kt");
}
@Test
@TestMetadata("inheritedPropertyInClassObject.kt")
public void testInheritedPropertyInClassObject() throws Exception {
@@ -26933,12 +26933,6 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo
runTest("compiler/testData/codegen/box/intrinsics/prefixIncDec.kt");
}
@Test
@TestMetadata("prefixIncDecFir.kt")
public void testPrefixIncDecFir() throws Exception {
runTest("compiler/testData/codegen/box/intrinsics/prefixIncDecFir.kt");
}
@Test
@TestMetadata("rangeFromCollection.kt")
public void testRangeFromCollection() throws Exception {
@@ -49309,24 +49303,12 @@ public class FirPsiBlackBoxCodegenTestGenerated extends AbstractFirPsiBlackBoxCo
runTest("compiler/testData/codegen/box/statics/incInClassObject.kt");
}
@Test
@TestMetadata("incInClassObjectFir.kt")
public void testIncInClassObjectFir() throws Exception {
runTest("compiler/testData/codegen/box/statics/incInClassObjectFir.kt");
}
@Test
@TestMetadata("incInObject.kt")
public void testIncInObject() throws Exception {
runTest("compiler/testData/codegen/box/statics/incInObject.kt");
}
@Test
@TestMetadata("incInObjectFir.kt")
public void testIncInObjectFir() throws Exception {
runTest("compiler/testData/codegen/box/statics/incInObjectFir.kt");
}
@Test
@TestMetadata("inheritedPropertyInClassObject.kt")
public void testInheritedPropertyInClassObject() throws Exception {
@@ -54,44 +54,38 @@ FILE: safeCallsWithUnaryOperators.kt
a#?.{ {
lval <array>: <implicit> = $subj$.b#
lval <index_0>: <implicit> = IntegerLiteral(0)
lval <unary-result>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index_0>|).inc#()
R|<local>/<array>|.set#(R|<local>/<index_0>|, R|<local>/<unary-result>|)
R|<local>/<unary-result>|
R|<local>/<array>|.set#(R|<local>/<index_0>|, R|<local>/<array>|.get#(R|<local>/<index_0>|).inc#())
R|<local>/<array>|.get#(R|<local>/<index_0>|)
}
}
a#?.{ $subj$.b# }?.{ {
lval <array>: <implicit> = $subj$.c#
lval <index_0>: <implicit> = IntegerLiteral(0)
lval <unary-result>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index_0>|).inc#()
R|<local>/<array>|.set#(R|<local>/<index_0>|, R|<local>/<unary-result>|)
R|<local>/<unary-result>|
R|<local>/<array>|.set#(R|<local>/<index_0>|, R|<local>/<array>|.get#(R|<local>/<index_0>|).inc#())
R|<local>/<array>|.get#(R|<local>/<index_0>|)
}
}
lval <array>: <implicit> = a#?.{ $subj$.b# }.c#
lval <index_0>: <implicit> = IntegerLiteral(0)
lval <unary-result>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index_0>|).inc#()
R|<local>/<array>|.set#(R|<local>/<index_0>|, R|<local>/<unary-result>|)
R|<local>/<unary-result>|
R|<local>/<array>|.set#(R|<local>/<index_0>|, R|<local>/<array>|.get#(R|<local>/<index_0>|).inc#())
R|<local>/<array>|.get#(R|<local>/<index_0>|)
a#?.{ {
lval <array>: <implicit> = $subj$.b#.get#(IntegerLiteral(0))
lval <index_0>: <implicit> = IntegerLiteral(0)
lval <unary-result>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index_0>|).inc#()
R|<local>/<array>|.set#(R|<local>/<index_0>|, R|<local>/<unary-result>|)
R|<local>/<unary-result>|
R|<local>/<array>|.set#(R|<local>/<index_0>|, R|<local>/<array>|.get#(R|<local>/<index_0>|).inc#())
R|<local>/<array>|.get#(R|<local>/<index_0>|)
}
}
a#?.{ $subj$.b# }?.{ {
lval <array>: <implicit> = $subj$.c#.get#(IntegerLiteral(0))
lval <index_0>: <implicit> = IntegerLiteral(0)
lval <unary-result>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index_0>|).inc#()
R|<local>/<array>|.set#(R|<local>/<index_0>|, R|<local>/<unary-result>|)
R|<local>/<unary-result>|
R|<local>/<array>|.set#(R|<local>/<index_0>|, R|<local>/<array>|.get#(R|<local>/<index_0>|).inc#())
R|<local>/<array>|.get#(R|<local>/<index_0>|)
}
}
lval <array>: <implicit> = a#?.{ $subj$.b# }.c#.get#(IntegerLiteral(0))
lval <index_0>: <implicit> = IntegerLiteral(0)
lval <unary-result>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index_0>|).inc#()
R|<local>/<array>|.set#(R|<local>/<index_0>|, R|<local>/<unary-result>|)
R|<local>/<unary-result>|
R|<local>/<array>|.set#(R|<local>/<index_0>|, R|<local>/<array>|.get#(R|<local>/<index_0>|).inc#())
R|<local>/<array>|.get#(R|<local>/<index_0>|)
++a#?.{ $subj$.b# }.d#()
}
@@ -37,9 +37,8 @@ FILE: unary.kt
lval x2: <implicit> = {
lval <array>: <implicit> = arr#
lval <index_0>: <implicit> = IntegerLiteral(1)
lval <unary-result>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index_0>|).inc#()
R|<local>/<array>|.set#(R|<local>/<index_0>|, R|<local>/<unary-result>|)
R|<local>/<unary-result>|
R|<local>/<array>|.set#(R|<local>/<index_0>|, R|<local>/<array>|.get#(R|<local>/<index_0>|).inc#())
R|<local>/<array>|.get#(R|<local>/<index_0>|)
}
}
@@ -64,9 +63,8 @@ FILE: unary.kt
lval x2: <implicit> = {
lval <array>: <implicit> = y#.arr#
lval <index_0>: <implicit> = IntegerLiteral(1)
lval <unary-result>: <implicit> = R|<local>/<array>|.get#(R|<local>/<index_0>|).inc#()
R|<local>/<array>|.set#(R|<local>/<index_0>|, R|<local>/<unary-result>|)
R|<local>/<unary-result>|
R|<local>/<array>|.set#(R|<local>/<index_0>|, R|<local>/<array>|.get#(R|<local>/<index_0>|).inc#())
R|<local>/<array>|.get#(R|<local>/<index_0>|)
}
}
@@ -471,32 +471,6 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
}
}
/**
* given:
* receiver++
*
* result:
* {
* val <unary> = receiver
* receiver = <unary>.inc()
* ^<unary>
* }
*
* given:
* ++receiver
*
* result:
* {
* val <unary-result> = receiver.inc()
* receiver = <unary-result>
* ^<unary-result>
* }
*
*/
// TODO:
// 1. Support receiver capturing for `a?.b++` (elementType == SAFE_ACCESS_EXPRESSION).
// 2. Add box test cases for #1 where receiver expression has side effects.
fun generateIncrementOrDecrementBlock(
// Used to obtain source-element or text
wholeExpression: T,
@@ -532,91 +506,6 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
}
/**
* given:
* receiver++
*
* result:
* {
* val <unary> = receiver
* val resultVar = <unary>.inc()
* appendAssignment(resultVar)
* ^<unary>
* }
*
* given:
* ++receiver
*
* result:
* {
* val <unary-result> = receiver.inc()
* val resultVar = <unary-result>
* appendAssignment(resultVar)
* ^<unary-result>
* }
*
*/
private fun FirBlockBuilder.putIncrementOrDecrementStatements(
receiver: FirExpression,
operationReference: T?,
callName: Name, // 'inc' or 'dec'
prefix: Boolean,
nameIfSimpleReference: Name?, // 'b' if whole expression is simple `b++` or `a.b++`, but not `a[1]++`
desugaredSource: KtSourceElement?,
appendAssignment: FirBlockBuilder.(resultInitializer: FirExpression, resultVar: FirVariable) -> Unit
) {
// initialValueVar is only used for postfix increment/decrement (stores the argument value before increment/decrement).
val initialValueVar = generateTemporaryVariable(
baseModuleData,
desugaredSource,
SpecialNames.UNARY,
receiver
)
// resultInitializer is the expression for `argument.inc()`
val resultInitializer = buildFunctionCall {
source = desugaredSource
calleeReference = buildSimpleNamedReference {
val kind = if (prefix) {
KtFakeSourceElementKind.DesugaredPrefixNameReference
} else {
KtFakeSourceElementKind.DesugaredPostfixNameReference
}
source = operationReference?.toFirSourceElement(kind)
name = callName
}
explicitReceiver = if (prefix) {
receiver
} else {
generateResolvedAccessExpression(desugaredSource, initialValueVar)
}
origin = FirFunctionCallOrigin.Operator
}
// resultVar is only used for prefix increment/decrement.
val resultVar = generateTemporaryVariable(
baseModuleData,
desugaredSource,
SpecialNames.UNARY_RESULT,
resultInitializer
)
if (prefix) {
if (nameIfSimpleReference != null) {
appendAssignment(resultInitializer, resultVar)
statements += generateAccessExpression(desugaredSource, desugaredSource, nameIfSimpleReference)
} else {
statements += resultVar
appendAssignment(resultInitializer, resultVar)
statements += generateResolvedAccessExpression(desugaredSource, resultVar)
}
} else {
statements += initialValueVar
appendAssignment(resultInitializer, resultVar)
statements += generateResolvedAccessExpression(desugaredSource, initialValueVar)
}
}
private fun T?.unwrap(): T? {
// NOTE: By removing surrounding parentheses and labels, FirLabels will NOT be created for those labels.
// This should be fine since the label is meaningless and unusable for a ++/-- argument or assignment LHS.
@@ -653,9 +542,8 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
* val <array> = a
* val <index0> = b
* val <index1> = c
* val <unary-result> = <array>.get(b, c).inc()
* <array>.set(b, c, <unary-result>)
* ^<unary-result>
* <array>.set(b, c, <array>.get(b, c).inc())
* ^<array>.get(b, c)
* }
*
*/
@@ -668,7 +556,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
convert: T.() -> FirExpression
): FirExpression {
val array = receiver.arrayExpression
return buildBlockProbablyUnderSafeCall(
return buildBlockPossiblyUnderSafeCall(
array, convert, receiver.toFirSourceElement(),
) { arrayReceiver ->
val baseSource = wholeExpression?.toFirSourceElement()
@@ -694,44 +582,74 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
).also { statements += it }
}
val firArgument = buildFunctionCall {
source = desugaredSource
calleeReference = buildSimpleNamedReference {
source = receiver?.toFirSourceElement(KtFakeSourceElementKind.ArrayAccessNameReference)
name = OperatorNameConventions.GET
}
explicitReceiver = generateResolvedAccessExpression(arrayVariable.source, arrayVariable)
argumentList = buildArgumentList {
for (indexVar in indexVariables) {
arguments += generateResolvedAccessExpression(indexVar.source, indexVar)
}
}
origin = FirFunctionCallOrigin.Operator
}
putIncrementOrDecrementStatements(
firArgument, operationReference, callName, prefix,
nameIfSimpleReference = null, desugaredSource
) { resultInitializer: FirExpression, resultVar: FirVariable ->
statements += buildFunctionCall {
fun buildGetCall(referenceSourceKind: KtFakeSourceElementKind = KtFakeSourceElementKind.ArrayAccessNameReference) =
buildFunctionCall {
source = desugaredSource
calleeReference = buildSimpleNamedReference {
source = receiver.toFirSourceElement()
name = OperatorNameConventions.SET
source = receiver?.toFirSourceElement(referenceSourceKind)
name = OperatorNameConventions.GET
}
explicitReceiver = generateResolvedAccessExpression(arrayVariable.source, arrayVariable)
argumentList = buildArgumentList {
for (indexVar in indexVariables) {
arguments += generateResolvedAccessExpression(indexVar.source, indexVar)
}
arguments += if (prefix) {
generateResolvedAccessExpression(source, resultVar)
} else {
resultInitializer
}
}
origin = FirFunctionCallOrigin.Operator
}
fun buildSetCall(argumentExpression: FirExpression) = buildFunctionCall {
source = desugaredSource
calleeReference = buildSimpleNamedReference {
source = receiver.toFirSourceElement()
name = OperatorNameConventions.SET
}
explicitReceiver = generateResolvedAccessExpression(arrayVariable.source, arrayVariable)
argumentList = buildArgumentList {
for (indexVar in indexVariables) {
arguments += generateResolvedAccessExpression(indexVar.source, indexVar)
}
arguments += argumentExpression
}
origin = FirFunctionCallOrigin.Operator
}
fun buildIncDecCall(kind: KtFakeSourceElementKind, receiver: FirExpression) = buildFunctionCall {
source = desugaredSource
calleeReference = buildSimpleNamedReference {
source = operationReference?.toFirSourceElement(kind)
name = callName
}
explicitReceiver = receiver
origin = FirFunctionCallOrigin.Operator
}
if (prefix) {
statements += buildSetCall(
buildIncDecCall(
KtFakeSourceElementKind.DesugaredPrefixNameReference,
buildGetCall()
)
)
statements += buildGetCall(KtFakeSourceElementKind.DesugaredPrefixSecondGetReference)
} else {
val initialValueVar = generateTemporaryVariable(
baseModuleData,
desugaredSource,
SpecialNames.UNARY,
buildGetCall()
)
statements += initialValueVar
statements += buildSetCall(
buildIncDecCall(
KtFakeSourceElementKind.DesugaredPostfixNameReference,
generateResolvedAccessExpression(desugaredSource, initialValueVar)
)
)
statements += generateResolvedAccessExpression(desugaredSource, initialValueVar)
}
}
}
@@ -741,7 +659,7 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
// a?.{ val receiver = $subj$.f() ... } where `...` is generated by `init(FIR<$subj$.f()>)`
//
// Otherwise just returns buildBlock { init(FIR<receiver>)) }
private fun buildBlockProbablyUnderSafeCall(
private fun buildBlockPossiblyUnderSafeCall(
receiver: T?,
convert: T.() -> FirExpression,
sourceElementForError: KtSourceElement?,
@@ -391,6 +391,16 @@ fun BodyResolveComponents.transformWhenSubjectExpressionUsingSmartcastInfo(
return builder.build()
}
fun BodyResolveComponents.transformDesugaredAssignmentValueUsingSmartcastInfo(
expression: FirDesugaredAssignmentValueReferenceExpression
): FirExpression {
val (stability, typesFromSmartCast) = dataFlowAnalyzer.getTypeUsingSmartcastInfo(expression.expressionRef.value) ?: return expression
val builder = transformExpressionUsingSmartcastInfo(
expression, stability, typesFromSmartCast
) ?: return expression
return builder.build()
}
private val ConeKotlinType.isKindOfNothing
get() = lowerBoundIfFlexible().let { it.isNothing || it.isNullableNothing }
@@ -750,26 +750,16 @@ open class FirExpressionsResolveTransformer(transformer: FirAbstractBodyResolveT
}
if (incrementDecrementExpression.isPrefix) {
val targetProperty = expression.calleeReference?.toResolvedPropertySymbol()?.fir
val operatorCall = buildAndResolveOperatorCall(expression)
// Special case for prefix inc/dec on local variable without delegate where unary-result variable generation is skipped.
if (targetProperty?.isLocal == true && targetProperty.delegate == null) {
// a = a.inc()
statements += buildAndResolveVariableAssignment(operatorCall)
// ^a
statements += targetProperty.toQualifiedAccess(fakeSource = desugaredSource, typeRef = noExpectedType)
// If inc() returns a subtype of its receiver type, the variable access should be smart-casted.
.transform<FirStatement, ResolutionMode>(transformer, withExpectedType(operatorCall.typeRef.coneType))
} else {
val unaryResultVariable = generateTemporaryVariable(SpecialNames.UNARY_RESULT, operatorCall)
// val <unary-result> = a.inc()
statements += unaryResultVariable
// a = <unary-result>
statements += buildAndResolveVariableAssignment(unaryResultVariable.toQualifiedAccess(fakeSource = desugaredSource))
// ^<unary-result>
statements += unaryResultVariable.toQualifiedAccess(fakeSource = desugaredSource)
// a = a.inc()
statements += buildAndResolveVariableAssignment(buildAndResolveOperatorCall(expression))
// ^a
statements += buildDesugaredAssignmentValueReferenceExpression {
source = ((expression as? FirErrorExpression)?.expression ?: expression).source
?.fakeElement(KtFakeSourceElementKind.DesugaredIncrementOrDecrement)
expressionRef = FirExpressionRef<FirExpression>().apply { bind(expression.unwrapSmartcastExpression()) }
}.let {
it.transform<FirStatement, ResolutionMode>(transformer, ResolutionMode.ContextIndependent)
components.transformDesugaredAssignmentValueUsingSmartcastInfo(it)
}
} else {
val unaryVariable = generateTemporaryVariable(SpecialNames.UNARY, expression)