diff --git a/compiler/fir/analysis-tests/testData/resolve/arrays/arraySetWithOperation.fir.txt b/compiler/fir/analysis-tests/testData/resolve/arrays/arraySetWithOperation.fir.txt index 63d6038e171..0d836700b8a 100644 --- a/compiler/fir/analysis-tests/testData/resolve/arrays/arraySetWithOperation.fir.txt +++ b/compiler/fir/analysis-tests/testData/resolve/arrays/arraySetWithOperation.fir.txt @@ -56,8 +56,8 @@ FILE: arraySetWithOperation.kt } public final fun test_3(a: R|A|): R|kotlin/Unit| { - ArraySet:[R|/a|.R|SubstitutionOverride|(Int(0)).R|/D.plusAssign|(R|/D.D|())] + ArraySet:[R|/a|.R|SubstitutionOverride|(Int(0)) += R|/D.D|()] } public final fun test_4(b: R|B|): R|kotlin/Unit| { - ArraySet:[R|/b|.#(Int(0)).#(R|/B.B|())] + ArraySet:[R|/b|.#(Int(0)) += R|/B.B|()] } diff --git a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/fir/DestructuringDeclaration.kt b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/fir/DestructuringDeclaration.kt index bf0f5606bff..fbacbd5bb76 100644 --- a/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/fir/DestructuringDeclaration.kt +++ b/compiler/fir/raw-fir/light-tree2fir/src/org/jetbrains/kotlin/fir/lightTree/fir/DestructuringDeclaration.kt @@ -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 diff --git a/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAugmentedAssignment.txt b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAugmentedAssignment.txt index 6c83c5484e9..cb67494976e 100644 --- a/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAugmentedAssignment.txt +++ b/compiler/fir/raw-fir/psi2fir/testData/rawBuilder/expressions/safeCallsWithAugmentedAssignment.txt @@ -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)) } diff --git a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt index 55aee6d472d..a8fe4fc7fac 100644 --- a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt +++ b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/BaseFirBuilder.kt @@ -1024,123 +1024,28 @@ abstract class BaseFirBuilder(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 = "", - 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 = "", 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(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 withDefaultSourceElementKind(newDefault: KtSourceElementKind, action: () -> R): R { val currentForced = context.forcedElementSourceKind context.forcedElementSourceKind = newDefault diff --git a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt index 10cf87bd701..0580ac0f20a 100644 --- a/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt +++ b/compiler/fir/raw-fir/raw-fir.common/src/org/jetbrains/kotlin/fir/builder/ConversionUtils.kt @@ -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? = 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? = null, -): FirVariable = - generateTemporaryVariable( - moduleData, - source, - Name.special("<$specialName>"), - initializer, - null, - extractedAnnotations, - ) - val FirClassBuilder.ownerRegularOrAnonymousObjectSymbol get() = when (this) { is FirAnonymousObjectBuilder -> symbol diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt index 9992407b5d8..24df504c075 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt @@ -13,9 +13,7 @@ import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.isLocal import org.jetbrains.kotlin.fir.diagnostics.* import org.jetbrains.kotlin.fir.expressions.* -import org.jetbrains.kotlin.fir.expressions.builder.buildErrorExpression -import org.jetbrains.kotlin.fir.expressions.builder.buildFunctionCall -import org.jetbrains.kotlin.fir.expressions.builder.buildVariableAssignment +import org.jetbrains.kotlin.fir.expressions.builder.* import org.jetbrains.kotlin.fir.expressions.impl.FirResolvedArgumentList import org.jetbrains.kotlin.fir.expressions.impl.toAnnotationArgumentMapping import org.jetbrains.kotlin.fir.references.* @@ -46,6 +44,7 @@ import org.jetbrains.kotlin.resolve.calls.tower.CandidateApplicability import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.ConstantValueKind import org.jetbrains.kotlin.types.TypeApproximatorConfiguration +import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.utils.addToStdlib.safeAs open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransformer) : FirPartialBodyResolveTransformer(transformer) { @@ -1043,32 +1042,16 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform augmentedArraySetCall.transformAnnotations(transformer, data) val operatorName = FirOperationNameConventions.ASSIGNMENTS.getValue(augmentedArraySetCall.operation) - val firstCalls = with(augmentedArraySetCall.setGetBlock.statements.last() as FirFunctionCall) setCall@{ - (annotations as MutableList) += augmentedArraySetCall.annotations - buildList { - add(this@setCall) - with(arguments.last() as FirFunctionCall) plusCall@{ - add(this@plusCall) - add(explicitReceiver as FirFunctionCall) - } - } - } - val secondCalls = listOf( - augmentedArraySetCall.assignCall, - augmentedArraySetCall.assignCall.explicitReceiver as FirFunctionCall - ) + val transformedLhsCall = + augmentedArraySetCall.lhsGetCall.transformSingle(transformer, ResolutionMode.ContextIndependent).takeIf { it.isSuccessful() } - val firstResult = augmentedArraySetCall.setGetBlock.transformSingle(transformer, ResolutionMode.ContextIndependent) - val secondResult = augmentedArraySetCall.assignCall.transformSingle(transformer, ResolutionMode.ContextIndependent) - - fun isSuccessful(functionCall: FirFunctionCall): Boolean = - functionCall.typeRef !is FirErrorTypeRef && functionCall.calleeReference is FirResolvedNamedReference - - val firstSucceed = firstCalls.all(::isSuccessful) - val secondSucceed = secondCalls.all(::isSuccessful) + val blockForGetSetVersion: FirBlock? = + transformedLhsCall?.let { augmentedArraySetCall.tryResolveAugmentedArraySetCallAsSetGetBlock(it) } + val assignResolvedCall: FirFunctionCall? = + transformedLhsCall?.let { augmentedArraySetCall.tryResolveWithOperatorAssignConvention(it) } val result: FirStatement = when { - firstSucceed && secondSucceed -> { + assignResolvedCall != null && blockForGetSetVersion != null -> { augmentedArraySetCall.also { it.replaceCalleeReference( buildErrorNamedReference { @@ -1079,16 +1062,19 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform ) } } - firstSucceed -> { + blockForGetSetVersion != null -> { //checking secondResult leave erroneous nodes in dfa graph, //we add another block so final type of expression will be correct //todo replace this hack with proper graph cleaning - transformer.components.dataFlowAnalyzer.enterBlock(augmentedArraySetCall.setGetBlock) - transformer.components.dataFlowAnalyzer.exitBlock(augmentedArraySetCall.setGetBlock) - firstResult + transformer.components.dataFlowAnalyzer.enterBlock(blockForGetSetVersion) + transformer.components.dataFlowAnalyzer.exitBlock(blockForGetSetVersion) + blockForGetSetVersion } - secondSucceed -> secondResult + assignResolvedCall != null -> assignResolvedCall else -> { + augmentedArraySetCall.rhs.transformSingle(transformer, ResolutionMode.ContextIndependent) + augmentedArraySetCall.rhs2.transformSingle(transformer, ResolutionMode.ContextIndependent) + augmentedArraySetCall.also { it.replaceCalleeReference( buildErrorNamedReference { @@ -1102,6 +1088,149 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform return result } + private fun FirFunctionCall.isSuccessful(): Boolean = + typeRef !is FirErrorTypeRef && calleeReference is FirResolvedNamedReference + + + /** + * Desugarings of a[x, y] += z to + * a.get(x, y).plusAssign(z) + * + * @return null if `plusAssign` is unresolved + * @return block defined as described above, otherwise + */ + private fun FirAugmentedArraySetCall.tryResolveWithOperatorAssignConvention(lhsGetCall: FirFunctionCall): FirFunctionCall? { + val assignCall = buildFunctionCall { + source = this@tryResolveWithOperatorAssignConvention.source?.fakeElement(KtFakeSourceElementKind.DesugaredCompoundAssignment) + calleeReference = buildSimpleNamedReference { + name = FirOperationNameConventions.ASSIGNMENTS.getValue(operation) + } + explicitReceiver = lhsGetCall + argumentList = buildArgumentList { + arguments += rhs2 + } + origin = FirFunctionCallOrigin.Operator + annotations += this@tryResolveWithOperatorAssignConvention.annotations + } + + val transformedCall = assignCall.transformSingle(transformer, ResolutionMode.ContextIndependent) + return transformedCall.takeIf { it.isSuccessful() } + } + + /** + * 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_y, tmp_a.get(tmp_x, tmp_y).plus(z)) + * } + * + * @return null if `set` or `plus` calls are unresolved + * @return block defined as described above, otherwise + */ + private fun FirAugmentedArraySetCall.tryResolveAugmentedArraySetCallAsSetGetBlock(lhsGetCall: FirFunctionCall): FirBlock? { + val arrayVariable = generateTemporaryVariable( + session.moduleData, + source = null, + specialName = "", + initializer = lhsGetCall.explicitReceiver ?: buildErrorExpression { + source = + this@tryResolveAugmentedArraySetCallAsSetGetBlock.source + ?.fakeElement(KtFakeSourceElementKind.DesugaredCompoundAssignment) + diagnostic = ConeSimpleDiagnostic("No receiver for array access", DiagnosticKind.Syntax) + } + ) + + val indexVariables = lhsGetCall.arguments.flatMap { + if (it is FirVarargArgumentsExpression) + it.arguments + else + listOf(it) + }.mapIndexed { i, index -> + generateTemporaryVariable(session.moduleData, source = null, specialName = "", initializer = index) + } + + val getCall = buildFunctionCall { + source = arrayAccessSource?.fakeElement(KtFakeSourceElementKind.DesugaredCompoundAssignment) + explicitReceiver = arrayVariable.toQualifiedAccess() + calleeReference = buildSimpleNamedReference { + name = OperatorNameConventions.GET + } + argumentList = buildArgumentList { + for (indexVariable in indexVariables) { + arguments += indexVariable.toQualifiedAccess() + } + } + origin = FirFunctionCallOrigin.Operator + } + + val operatorCall = buildFunctionCall { + calleeReference = buildSimpleNamedReference { + name = FirOperationNameConventions.ASSIGNMENTS_TO_SIMPLE_OPERATOR.getValue(operation) + } + explicitReceiver = getCall + argumentList = buildArgumentList { + arguments += rhs + } + origin = FirFunctionCallOrigin.Operator + } + + val setCall = buildFunctionCall { + source = + this@tryResolveAugmentedArraySetCallAsSetGetBlock.source + ?.fakeElement(KtFakeSourceElementKind.DesugaredCompoundAssignment) + explicitReceiver = arrayVariable.toQualifiedAccess() + calleeReference = buildSimpleNamedReference { + name = OperatorNameConventions.SET + } + origin = FirFunctionCallOrigin.Operator + argumentList = buildArgumentList { + for (indexVariable in indexVariables) { + arguments += indexVariable.toQualifiedAccess() + } + + arguments += operatorCall + } + + annotations += this@tryResolveAugmentedArraySetCallAsSetGetBlock.annotations + } + + arrayVariable.transformSingle(transformer, ResolutionMode.ContextIndependent) + indexVariables.forEach { it.transformSingle(transformer, ResolutionMode.ContextIndependent) } + val transformedSet = setCall.transformSingle(transformer, ResolutionMode.ContextIndependent) + + if (!transformedSet.isSuccessful()) return null + + val transformedOperator = + transformedSet.argumentList.arguments.last().let { + if (it is FirNamedArgumentExpression) + it.expression + else + it + } + + require(transformedOperator is FirFunctionCall) { + "Last argument of set call should be an operator but $transformedOperator found" + } + + if (!transformedOperator.isSuccessful()) return null + + return buildBlock { + statements += arrayVariable + statements += indexVariables + statements += setCall + }.also { + it.replaceTypeRef( + buildResolvedTypeRef { + source = this@tryResolveAugmentedArraySetCallAsSetGetBlock.source + type = session.builtinTypes.unitType.type + } + ) + } + } + + override fun transformArrayOfCall(arrayOfCall: FirArrayOfCall, data: ResolutionMode): FirStatement { if (data is ResolutionMode.ContextDependent) { arrayOfCall.transformChildren(transformer, data) diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/FirAugmentedArraySetCall.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/FirAugmentedArraySetCall.kt index bde1e2418b4..f8cc9ae6898 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/FirAugmentedArraySetCall.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/FirAugmentedArraySetCall.kt @@ -19,10 +19,12 @@ import org.jetbrains.kotlin.fir.visitors.* abstract class FirAugmentedArraySetCall : FirPureAbstractElement(), FirStatement { abstract override val source: KtSourceElement? abstract override val annotations: List - abstract val assignCall: FirFunctionCall - abstract val setGetBlock: FirBlock + abstract val lhsGetCall: FirFunctionCall + abstract val rhs: FirExpression + abstract val rhs2: FirExpression abstract val operation: FirOperation abstract val calleeReference: FirReference + abstract val arrayAccessSource: KtSourceElement? override fun accept(visitor: FirVisitor, data: D): R = visitor.visitAugmentedArraySetCall(this, data) diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/builder/FirAugmentedArraySetCallBuilder.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/builder/FirAugmentedArraySetCallBuilder.kt index 93ea6af4550..8f14e1763ad 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/builder/FirAugmentedArraySetCallBuilder.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/builder/FirAugmentedArraySetCallBuilder.kt @@ -11,7 +11,7 @@ import org.jetbrains.kotlin.fir.builder.FirAnnotationContainerBuilder import org.jetbrains.kotlin.fir.builder.FirBuilderDsl import org.jetbrains.kotlin.fir.expressions.FirAnnotation import org.jetbrains.kotlin.fir.expressions.FirAugmentedArraySetCall -import org.jetbrains.kotlin.fir.expressions.FirBlock +import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirFunctionCall import org.jetbrains.kotlin.fir.expressions.FirOperation import org.jetbrains.kotlin.fir.expressions.impl.FirAugmentedArraySetCallImpl @@ -28,19 +28,23 @@ import org.jetbrains.kotlin.fir.visitors.* class FirAugmentedArraySetCallBuilder : FirAnnotationContainerBuilder { override var source: KtSourceElement? = null override val annotations: MutableList = mutableListOf() - lateinit var assignCall: FirFunctionCall - lateinit var setGetBlock: FirBlock + lateinit var lhsGetCall: FirFunctionCall + lateinit var rhs: FirExpression + lateinit var rhs2: FirExpression lateinit var operation: FirOperation var calleeReference: FirReference = FirStubReference + var arrayAccessSource: KtSourceElement? = null override fun build(): FirAugmentedArraySetCall { return FirAugmentedArraySetCallImpl( source, annotations, - assignCall, - setGetBlock, + lhsGetCall, + rhs, + rhs2, operation, calleeReference, + arrayAccessSource, ) } diff --git a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirAugmentedArraySetCallImpl.kt b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirAugmentedArraySetCallImpl.kt index 1cdf54514b3..a6ee7711f07 100644 --- a/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirAugmentedArraySetCallImpl.kt +++ b/compiler/fir/tree/gen/org/jetbrains/kotlin/fir/expressions/impl/FirAugmentedArraySetCallImpl.kt @@ -8,7 +8,7 @@ package org.jetbrains.kotlin.fir.expressions.impl import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.fir.expressions.FirAnnotation import org.jetbrains.kotlin.fir.expressions.FirAugmentedArraySetCall -import org.jetbrains.kotlin.fir.expressions.FirBlock +import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirFunctionCall import org.jetbrains.kotlin.fir.expressions.FirOperation import org.jetbrains.kotlin.fir.references.FirReference @@ -22,22 +22,26 @@ import org.jetbrains.kotlin.fir.visitors.* internal class FirAugmentedArraySetCallImpl( override val source: KtSourceElement?, override val annotations: MutableList, - override var assignCall: FirFunctionCall, - override var setGetBlock: FirBlock, + override var lhsGetCall: FirFunctionCall, + override var rhs: FirExpression, + override var rhs2: FirExpression, override val operation: FirOperation, override var calleeReference: FirReference, + override val arrayAccessSource: KtSourceElement?, ) : FirAugmentedArraySetCall() { override fun acceptChildren(visitor: FirVisitor, data: D) { annotations.forEach { it.accept(visitor, data) } - assignCall.accept(visitor, data) - setGetBlock.accept(visitor, data) + lhsGetCall.accept(visitor, data) + rhs.accept(visitor, data) + rhs2.accept(visitor, data) calleeReference.accept(visitor, data) } override fun transformChildren(transformer: FirTransformer, data: D): FirAugmentedArraySetCallImpl { transformAnnotations(transformer, data) - assignCall = assignCall.transform(transformer, data) - setGetBlock = setGetBlock.transform(transformer, data) + lhsGetCall = lhsGetCall.transform(transformer, data) + rhs = rhs.transform(transformer, data) + rhs2 = rhs2.transform(transformer, data) calleeReference = calleeReference.transform(transformer, data) return this } diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirGeneration.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirGeneration.kt new file mode 100644 index 00000000000..1533781465b --- /dev/null +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirGeneration.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir + +import org.jetbrains.kotlin.KtFakeSourceElementKind +import org.jetbrains.kotlin.KtSourceElement +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.Visibilities +import org.jetbrains.kotlin.fakeElement +import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin +import org.jetbrains.kotlin.fir.declarations.FirVariable +import org.jetbrains.kotlin.fir.declarations.builder.buildProperty +import org.jetbrains.kotlin.fir.declarations.impl.FirDeclarationStatusImpl +import org.jetbrains.kotlin.fir.expressions.FirAnnotation +import org.jetbrains.kotlin.fir.expressions.FirExpression +import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression +import org.jetbrains.kotlin.fir.expressions.builder.buildPropertyAccessExpression +import org.jetbrains.kotlin.fir.references.builder.buildResolvedNamedReference +import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol +import org.jetbrains.kotlin.fir.types.FirTypeRef +import org.jetbrains.kotlin.fir.types.builder.buildImplicitTypeRef +import org.jetbrains.kotlin.name.Name + +fun FirVariable.toQualifiedAccess(): FirQualifiedAccessExpression = buildPropertyAccessExpression { + calleeReference = buildResolvedNamedReference { + source = this@toQualifiedAccess.source?.fakeElement(KtFakeSourceElementKind.ReferenceInAtomicQualifiedAccess) + name = this@toQualifiedAccess.name + resolvedSymbol = this@toQualifiedAccess.symbol + } +} + +fun generateTemporaryVariable( + moduleData: FirModuleData, + source: KtSourceElement?, + name: Name, + initializer: FirExpression, + typeRef: FirTypeRef? = null, + extractedAnnotations: Collection? = 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? = null, +): FirVariable = + generateTemporaryVariable( + moduleData, + source, + Name.special("<$specialName>"), + initializer, + null, + extractedAnnotations, + ) diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirRenderer.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirRenderer.kt index 886989e839b..962c37a6c95 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirRenderer.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/FirRenderer.kt @@ -20,7 +20,10 @@ import org.jetbrains.kotlin.fir.expressions.impl.* import org.jetbrains.kotlin.fir.references.* import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol -import org.jetbrains.kotlin.fir.symbols.impl.* +import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol import org.jetbrains.kotlin.fir.types.* import org.jetbrains.kotlin.fir.visitors.FirVisitorVoid import org.jetbrains.kotlin.name.Name @@ -1302,7 +1305,11 @@ open class FirRenderer(builder: StringBuilder, protected val mode: RenderMode = override fun visitAugmentedArraySetCall(augmentedArraySetCall: FirAugmentedArraySetCall) { augmentedArraySetCall.annotations.renderAnnotations() print("ArraySet:[") - augmentedArraySetCall.assignCall.accept(this) + augmentedArraySetCall.lhsGetCall.accept(this) + print(" ") + print(augmentedArraySetCall.operation.operator) + print(" ") + augmentedArraySetCall.rhs.accept(this) print("]") } diff --git a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/NodeConfigurator.kt b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/NodeConfigurator.kt index 9f7923ab8f2..83b5a5f806f 100644 --- a/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/NodeConfigurator.kt +++ b/compiler/fir/tree/tree-generator/src/org/jetbrains/kotlin/fir/tree/generator/NodeConfigurator.kt @@ -461,10 +461,13 @@ object NodeConfigurator : AbstractFieldConfigurator(FirTreeBuild } augmentedArraySetCall.configure { - +field("assignCall", functionCall) - +field("setGetBlock", block) + +field("lhsGetCall", functionCall) + +field("rhs", expression) + +field("rhs2", expression) +field("operation", operationType) + // Used for resolution errors reporting in case +field("calleeReference", reference, withReplace = true) + +field("arrayAccessSource", sourceElementType, nullable = true) } classReferenceExpression.configure { diff --git a/compiler/testData/diagnostics/tests/callableReference/compatibilityResolveWithVarargAndOperatorCall.fir.kt b/compiler/testData/diagnostics/tests/callableReference/compatibilityResolveWithVarargAndOperatorCall.fir.kt deleted file mode 100644 index 8f47952580e..00000000000 --- a/compiler/testData/diagnostics/tests/callableReference/compatibilityResolveWithVarargAndOperatorCall.fir.kt +++ /dev/null @@ -1,18 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_PARAMETER - -fun interface IFoo { - fun foo(i: Int) -} - -fun interface IFoo2 : IFoo - -object A - -operator fun A.get(i: IFoo) = 1 -operator fun A.set(i: IFoo, newValue: Int) {} - -fun withVararg(vararg xs: Int) = 42 - -fun test1() { - A[::withVararg] += 1 -} diff --git a/compiler/testData/diagnostics/tests/callableReference/compatibilityResolveWithVarargAndOperatorCall.kt b/compiler/testData/diagnostics/tests/callableReference/compatibilityResolveWithVarargAndOperatorCall.kt index c012456ffbe..821c47c5809 100644 --- a/compiler/testData/diagnostics/tests/callableReference/compatibilityResolveWithVarargAndOperatorCall.kt +++ b/compiler/testData/diagnostics/tests/callableReference/compatibilityResolveWithVarargAndOperatorCall.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_PARAMETER fun interface IFoo { diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInArrayAccess.fir.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInArrayAccess.fir.kt index 909c220f100..a6d1bce63f6 100644 --- a/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInArrayAccess.fir.kt +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/deadCode/deadCodeInArrayAccess.fir.kt @@ -34,7 +34,7 @@ fun testArrayAssignment4(n: Nothing) { fun testArrayPlusAssign(array: Array) { operator fun Any.plusAssign(a: Any) {} - array[1] += todo() + array[1] += todo() } fun todo(): Nothing = throw Exception() diff --git a/compiler/testData/diagnostics/tests/nullableTypes/safeCallOperators.fir.kt b/compiler/testData/diagnostics/tests/nullableTypes/safeCallOperators.fir.kt index 083caedbf96..6121d5990ff 100644 --- a/compiler/testData/diagnostics/tests/nullableTypes/safeCallOperators.fir.kt +++ b/compiler/testData/diagnostics/tests/nullableTypes/safeCallOperators.fir.kt @@ -64,7 +64,7 @@ fun foo(a: A?) { // 1. All kinds of green code with safe+call + invoke we identified fails with CCE if `a != null`, anyway // 2. In case of null value, the behavior is intended (no call performed) a?.q() - a?.w++ + a?.w++ (a?.l) += 1 (a?.l)[0] @@ -75,7 +75,7 @@ fun foo(a: A?) { (a?.ll)[0][0]++ (a?.ll)[0][0] = 1 (a?.q)() - (a?.w)++ + (a?.w)++ a?.l.plusAssign(1) a?.l.get(0)