FIR: Rework processing AugmentedArraySetCall

Previously (few commits earlier), it contained two versions
of receiver (lhs) generated separately for each desugaring version
that looked a bit redundant.

Now, at FIR building stage we just don't create desugaring sub-trees,
instead they are being built during bodies transformation and that seems
to be much convenient there, since we don't need to reverse-engineer
get-set-operator version to check if containing calls are successful
(as we just built those calls and retain them)

Semantically, this changes may only change how data flow works
for such statements (see changed compatibilityResolveWithVarargAndOperatorCall.kt)

^KT-50861 Relates
This commit is contained in:
Denis.Zharkov
2022-01-31 11:19:26 +03:00
parent 772579143b
commit adb9dfb256
16 changed files with 306 additions and 244 deletions
@@ -7,9 +7,9 @@ package org.jetbrains.kotlin.fir.lightTree.fir
import org.jetbrains.kotlin.KtSourceElement
import org.jetbrains.kotlin.fir.FirModuleData
import org.jetbrains.kotlin.fir.builder.generateTemporaryVariable
import org.jetbrains.kotlin.fir.declarations.FirVariable
import org.jetbrains.kotlin.fir.expressions.FirExpression
import org.jetbrains.kotlin.fir.generateTemporaryVariable
import org.jetbrains.kotlin.fir.lightTree.converter.generateDestructuringBlock
import org.jetbrains.kotlin.fir.lightTree.fir.modifier.Modifier
@@ -3,11 +3,11 @@ FILE: safeCallsWithAugmentedAssignment.kt
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#?.{ ArraySet:[$subj$.b#.get#(IntegerLiteral(0)) += IntegerLiteral(1)] }
a#?.{ $subj$.b# }?.{ ArraySet:[$subj$.c#.get#(IntegerLiteral(0)) += IntegerLiteral(1)] }
ArraySet:[a#?.{ $subj$.b# }.c#.get#(IntegerLiteral(0)) += IntegerLiteral(1)]
a#?.{ ArraySet:[$subj$.b#.get#(IntegerLiteral(0)).get#(IntegerLiteral(0)) += IntegerLiteral(1)] }
a#?.{ $subj$.b# }?.{ ArraySet:[$subj$.c#.get#(IntegerLiteral(0)).get#(IntegerLiteral(0)) += IntegerLiteral(1)] }
ArraySet:[a#?.{ $subj$.b# }.c#.get#(IntegerLiteral(0)).get#(IntegerLiteral(0)) += IntegerLiteral(1)]
+=(a#?.{ $subj$.b# }.d#(), IntegerLiteral(1))
}
@@ -1024,123 +1024,28 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
rhs: T?,
convert: T.() -> FirExpression
): FirStatement {
require(receiver is FirFunctionCall) {
"Array access should be desugared to a function call, but $receiver is found"
}
return buildAugmentedArraySetCall {
source = baseSource
this.operation = operation
assignCall = generateAugmentedCallForAugmentedArraySetCall(receiver, baseSource, operation, rhs, convert)
setGetBlock =
generateSetGetBlockForAugmentedArraySetCall(receiver, baseSource, arrayAccessSource, operation, rhs, convert)
this.annotations += annotations
}
}
private fun generateAugmentedCallForAugmentedArraySetCall(
receiver: FirExpression, // a.get(x,y)
baseSource: KtSourceElement?,
operation: FirOperation,
rhs: T?,
convert: T.() -> FirExpression
): FirFunctionCall {
/*
* Desugarings of a[x, y] += z to
* a.get(x, y).plusAssign(z)
*/
return buildFunctionCall {
source = baseSource?.fakeElement(KtFakeSourceElementKind.DesugaredCompoundAssignment)
calleeReference = buildSimpleNamedReference {
name = FirOperationNameConventions.ASSIGNMENTS.getValue(operation)
}
explicitReceiver = receiver
argumentList = buildArgumentList {
arguments += rhs?.convert() ?: buildErrorExpression(
null,
ConeSimpleDiagnostic("No value for array set", DiagnosticKind.Syntax)
)
}
origin = FirFunctionCallOrigin.Operator
}
}
private fun generateSetGetBlockForAugmentedArraySetCall(
receiver: FirExpression,
baseSource: KtSourceElement?,
arrayAccessSource: KtSourceElement?,
operation: FirOperation,
rhs: T?,
convert: T.() -> FirExpression
): FirBlock {
/*
* Desugarings of a[x, y] += z to
* {
* val tmp_a = a
* val tmp_x = x
* val tmp_y = y
* tmp_a.set(tmp_x, tmp_a.get(tmp_x, tmp_y).plus(z))
* }
*/
return buildBlock {
val baseCall = receiver as FirFunctionCall
val arrayVariable = generateTemporaryVariable(
baseModuleData,
source = null,
specialName = "<array>",
initializer = baseCall.explicitReceiver ?: buildErrorExpression {
source = baseSource?.fakeElement(KtFakeSourceElementKind.DesugaredCompoundAssignment)
diagnostic = ConeSimpleDiagnostic("No receiver for array access", DiagnosticKind.Syntax)
}
this.lhsGetCall = receiver
this.rhs = rhs?.convert() ?: buildErrorExpression(
null,
ConeSimpleDiagnostic("No value for array set", DiagnosticKind.Syntax)
)
statements += arrayVariable
val indexVariables = baseCall.arguments.mapIndexed { i, index ->
generateTemporaryVariable(baseModuleData, source = null, specialName = "<index_$i>", initializer = index)
}
statements += indexVariables
statements += buildFunctionCall {
source = baseSource?.fakeElement(KtFakeSourceElementKind.DesugaredCompoundAssignment)
explicitReceiver = arrayVariable.toQualifiedAccess()
calleeReference = buildSimpleNamedReference {
name = OperatorNameConventions.SET
}
origin = FirFunctionCallOrigin.Operator
argumentList = buildArgumentList {
for (indexVariable in indexVariables) {
arguments += indexVariable.toQualifiedAccess()
}
val getCall = buildFunctionCall {
source = arrayAccessSource?.fakeElement(KtFakeSourceElementKind.DesugaredCompoundAssignment)
explicitReceiver = arrayVariable.toQualifiedAccess()
calleeReference = buildSimpleNamedReference {
name = OperatorNameConventions.GET
}
argumentList = buildArgumentList {
for (indexVariable in indexVariables) {
arguments += indexVariable.toQualifiedAccess()
}
}
origin = FirFunctionCallOrigin.Operator
}
val operatorCall = buildFunctionCall {
calleeReference = buildSimpleNamedReference {
name = FirOperationNameConventions.ASSIGNMENTS_TO_SIMPLE_OPERATOR.getValue(operation)
}
explicitReceiver = getCall
argumentList = buildArgumentList {
arguments += rhs?.convert() ?: buildErrorExpression(
null,
ConeSimpleDiagnostic(
"No value for array set",
DiagnosticKind.Syntax
)
)
}
origin = FirFunctionCallOrigin.Operator
}
arguments += operatorCall
}
}
// Second copy of rhs is used because we analyze it twice in different contexts
// and now they should be different expressions instances to make everything work properly.
// But this lead to exponential time already at FIR building stage,
// so we hope this hack will be removed with KT-50861
this.rhs2 = rhs?.convert() ?: buildErrorExpression(
null,
ConeSimpleDiagnostic("No value for array set", DiagnosticKind.Syntax)
)
this.arrayAccessSource = arrayAccessSource
this.annotations += annotations
}
}
@@ -1262,14 +1167,6 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
initContainingClassAttr(context)
}
private fun FirVariable.toQualifiedAccess(): FirQualifiedAccessExpression = buildPropertyAccessExpression {
calleeReference = buildResolvedNamedReference {
source = this@toQualifiedAccess.source?.fakeElement(KtFakeSourceElementKind.ReferenceInAtomicQualifiedAccess)
name = this@toQualifiedAccess.name
resolvedSymbol = this@toQualifiedAccess.symbol
}
}
protected inline fun <R> withDefaultSourceElementKind(newDefault: KtSourceElementKind, action: () -> R): R {
val currentForced = context.forcedElementSourceKind
context.forcedElementSourceKind = newDefault
@@ -285,50 +285,6 @@ fun generateResolvedAccessExpression(source: KtSourceElement?, variable: FirVari
}
}
fun generateTemporaryVariable(
moduleData: FirModuleData,
source: KtSourceElement?,
name: Name,
initializer: FirExpression,
typeRef: FirTypeRef? = null,
extractedAnnotations: Collection<FirAnnotation>? = null,
): FirVariable =
buildProperty {
this.source = source
this.moduleData = moduleData
origin = FirDeclarationOrigin.Source
returnTypeRef = typeRef ?: buildImplicitTypeRef {
this.source = source
}
this.name = name
this.initializer = initializer
symbol = FirPropertySymbol(name)
isVar = false
isLocal = true
status = FirDeclarationStatusImpl(Visibilities.Local, Modality.FINAL)
if (extractedAnnotations != null) {
// LT extracts annotations ahead.
// PSI extracts annotations on demand. Use a similar util in [PsiConversionUtils]
annotations.addAll(extractedAnnotations)
}
}
fun generateTemporaryVariable(
moduleData: FirModuleData,
source: KtSourceElement?,
specialName: String,
initializer: FirExpression,
extractedAnnotations: Collection<FirAnnotation>? = null,
): FirVariable =
generateTemporaryVariable(
moduleData,
source,
Name.special("<$specialName>"),
initializer,
null,
extractedAnnotations,
)
val FirClassBuilder.ownerRegularOrAnonymousObjectSymbol
get() = when (this) {
is FirAnonymousObjectBuilder -> symbol