From 29849b13308b5ea4d8969e733749ac85566caab9 Mon Sep 17 00:00:00 2001 From: Dmitriy Novozhilov Date: Mon, 6 Jul 2020 12:51:47 +0300 Subject: [PATCH] [FIR] Resolve rhs of += in dependent context --- .../inference/plusAssignWithLambdaInRhs.dot | 107 ++++++++++++++++++ .../inference/plusAssignWithLambdaInRhs.kt | 19 ++++ .../inference/plusAssignWithLambdaInRhs.txt | 16 +++ ...FirDiagnosticsWithStdlibTestGenerated.java | 5 + .../jetbrains/kotlin/fir/FirCallResolver.kt | 29 ++++- .../fir/resolve/dfa/FirDataFlowAnalyzer.kt | 20 ++++ .../dfa/cfg/ControlFlowGraphBuilder.kt | 31 ++++- .../resolve/inference/FirInferenceSession.kt | 42 +++---- .../FirExpressionsResolveTransformer.kt | 71 +++++++++--- .../genericSamProjectedOutWithNewInference.kt | 1 - .../regressions/lambdaPostponeConstruction.kt | 1 - .../genericAssignmentOperator.fir.kt | 2 +- .../tests/operatorsOverloading/kt1028.fir.kt | 2 +- .../inference/plusAssignWithLambda.fir.kt | 4 +- .../inference/plusAssignWithLambda2.fir.kt | 6 +- 15 files changed, 303 insertions(+), 53 deletions(-) create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/plusAssignWithLambdaInRhs.dot create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/plusAssignWithLambdaInRhs.kt create mode 100644 compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/plusAssignWithLambdaInRhs.txt diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/plusAssignWithLambdaInRhs.dot b/compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/plusAssignWithLambdaInRhs.dot new file mode 100644 index 00000000000..5ba45d4c9d6 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/plusAssignWithLambdaInRhs.dot @@ -0,0 +1,107 @@ +digraph plusAssignWithLambdaInRhs_kt { + graph [nodesep=3] + node [shape=box penwidth=2] + edge [penwidth=2] + + subgraph cluster_0 { + color=red + 0 [label="Enter function plusAssign" style="filled" fillcolor=red]; + subgraph cluster_1 { + color=blue + 1 [label="Enter block"]; + 2 [label="Exit block"]; + } + 3 [label="Exit function plusAssign" style="filled" fillcolor=red]; + } + 0 -> {1}; + 1 -> {2}; + 2 -> {3}; + + subgraph cluster_2 { + color=red + 4 [label="Enter function plus" style="filled" fillcolor=red]; + subgraph cluster_3 { + color=blue + 5 [label="Enter block"]; + 6 [label="Access variable this@R|/plus|"]; + 7 [label="Jump: ^plus this@R|/plus|"]; + 8 [label="Stub" style="filled" fillcolor=gray]; + 9 [label="Exit block" style="filled" fillcolor=gray]; + } + 10 [label="Exit function plus" style="filled" fillcolor=red]; + } + 4 -> {5}; + 5 -> {6}; + 6 -> {7}; + 7 -> {10}; + 7 -> {8} [style=dotted]; + 8 -> {9} [style=dotted]; + 9 -> {10} [style=dotted]; + + subgraph cluster_4 { + color=red + 11 [label="Enter function test" style="filled" fillcolor=red]; + subgraph cluster_5 { + color=blue + 12 [label="Enter block"]; + 13 [label="Const: Null(null)"]; + 14 [label="Check not null: Null(null)!!"]; + 15 [label="Variable declaration: lval list: R|kotlin/collections/MutableList>|"]; + 16 [label="Access variable R|/list|"]; + 17 [label="Postponed enter to lambda"]; + subgraph cluster_6 { + color=blue + 22 [label="Enter function anonymousFunction" style="filled" fillcolor=red]; + subgraph cluster_7 { + color=blue + 23 [label="Enter block"]; + 24 [label="Access variable R|/it|"]; + 25 [label="Exit block"]; + } + 26 [label="Exit function anonymousFunction" style="filled" fillcolor=red]; + } + 18 [label="Postponed exit from lambda"]; + 19 [label="Function call: R|/list|.R|/plusAssign| kotlin/String|>(...)"]; + 20 [label="Exit block"]; + } + 21 [label="Exit function test" style="filled" fillcolor=red]; + } + 11 -> {12}; + 12 -> {13}; + 13 -> {14}; + 14 -> {15}; + 15 -> {16}; + 16 -> {17}; + 17 -> {18 22}; + 18 -> {19}; + 19 -> {20}; + 20 -> {21}; + 22 -> {23}; + 23 -> {24}; + 24 -> {25}; + 25 -> {26}; + + subgraph cluster_8 { + color=red + 27 [label="Enter function listOf" style="filled" fillcolor=red]; + subgraph cluster_9 { + color=blue + 28 [label="Enter block"]; + 29 [label="Const: Null(null)"]; + 30 [label="Check not null: Null(null)!!"]; + 31 [label="Jump: ^listOf Null(null)!!"]; + 32 [label="Stub" style="filled" fillcolor=gray]; + 33 [label="Exit block" style="filled" fillcolor=gray]; + } + 34 [label="Exit function listOf" style="filled" fillcolor=red]; + } + 27 -> {28}; + 28 -> {29}; + 29 -> {30}; + 30 -> {31}; + 31 -> {34}; + 31 -> {32} [style=dotted]; + 32 -> {33} [style=dotted]; + 33 -> {34} [style=dotted]; + +} diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/plusAssignWithLambdaInRhs.kt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/plusAssignWithLambdaInRhs.kt new file mode 100644 index 00000000000..a0b34537ed3 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/plusAssignWithLambdaInRhs.kt @@ -0,0 +1,19 @@ +// ISSUE: KT-39005 +// !DUMP_CFG + +fun test() { + val list: MutableList<(String) -> String> = null!! + list += { it } +} + +//class A(private val executor: ((T) -> Unit) -> Unit) +// +//fun postpone(computation: () -> T): A { +// val queue = mutableListOf<() -> Unit>() +// +// return A { resolve -> +// queue += { +// resolve(computation()) +// } +// } +//} diff --git a/compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/plusAssignWithLambdaInRhs.txt b/compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/plusAssignWithLambdaInRhs.txt new file mode 100644 index 00000000000..0b91590e24f --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/plusAssignWithLambdaInRhs.txt @@ -0,0 +1,16 @@ +FILE: plusAssignWithLambdaInRhs.kt + public final operator fun R|kotlin/collections/MutableList|.plusAssign(x: R|T|): R|kotlin/Unit| { + } + public final operator fun R|kotlin/collections/List|.plus(x: R|T|): R|kotlin/collections/List| { + ^plus this@R|/plus| + } + public final fun test(): R|kotlin/Unit| { + lval list: R|kotlin/collections/MutableList>| = Null(null)!! + R|/list|.R|/plusAssign| kotlin/String|>(fun (it: R|kotlin/String|): R|kotlin/String| { + ^ R|/it| + } + ) + } + public final fun listOf(x: R|T|): R|kotlin/collections/List| { + ^listOf Null(null)!! + } diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java index bece5da1881..993b0ccbf1a 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithStdlibTestGenerated.java @@ -716,6 +716,11 @@ public class FirDiagnosticsWithStdlibTestGenerated extends AbstractFirDiagnostic public void testIfElvisReturn() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/ifElvisReturn.kt"); } + + @TestMetadata("plusAssignWithLambdaInRhs.kt") + public void testPlusAssignWithLambdaInRhs() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/plusAssignWithLambdaInRhs.kt"); + } } @TestMetadata("compiler/fir/analysis-tests/testData/resolveWithStdlib/j+k") diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt index 46c6e399b4a..98980e574f5 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/FirCallResolver.kt @@ -58,14 +58,22 @@ class FirCallResolver( inferenceComponents.session.callConflictResolverFactory .create(TypeSpecificityComparator.NONE, inferenceComponents) + @PrivateForInline + var needTransformArguments: Boolean = true + + @OptIn(PrivateForInline::class) fun resolveCallAndSelectCandidate(functionCall: FirFunctionCall): FirFunctionCall { qualifiedResolver.reset() @Suppress("NAME_SHADOWING") - val functionCall = functionCall.transformExplicitReceiver(transformer, ResolutionMode.ContextIndependent) - .also { - dataFlowAnalyzer.enterQualifiedAccessExpression() - functionCall.argumentList.transformArguments(transformer, ResolutionMode.ContextDependent) - } + val functionCall = if (needTransformArguments) { + functionCall.transformExplicitReceiver(transformer, ResolutionMode.ContextIndependent) + .also { + dataFlowAnalyzer.enterQualifiedAccessExpression() + functionCall.argumentList.transformArguments(transformer, ResolutionMode.ContextDependent) + } + } else { + functionCall + } val name = functionCall.calleeReference.name val result = collectCandidates(functionCall, name) @@ -289,6 +297,17 @@ class FirCallResolver( return callResolver.selectDelegatingConstructorCall(delegatedConstructorCall, name, result, callInfo) } + @OptIn(PrivateForInline::class) + inline fun withNoArgumentsTransform(block: () -> T): T { + val oldValue = needTransformArguments + needTransformArguments = false + return try { + block() + } finally { + needTransformArguments = oldValue + } + } + private fun selectDelegatingConstructorCall( call: FirDelegatedConstructorCall, name: Name, result: CandidateCollector, callInfo: CallInfo ): FirDelegatedConstructorCall { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt index a5dfbe27571..6a6e72bebb5 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/FirDataFlowAnalyzer.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.fir.resolve.dfa import org.jetbrains.kotlin.fir.FirSession +import org.jetbrains.kotlin.fir.PrivateForInline import org.jetbrains.kotlin.fir.contracts.FirResolvedContractDescription import org.jetbrains.kotlin.fir.contracts.description.ConeBooleanConstantReference import org.jetbrains.kotlin.fir.contracts.description.ConeConditionalEffectDeclaration @@ -106,6 +107,9 @@ abstract class FirDataFlowAnalyzer( protected val any = components.session.builtinTypes.anyType.type private val nullableNothing = components.session.builtinTypes.nullableNothingType.type + @PrivateForInline + var ignoreFunctionCalls: Boolean = false + // ----------------------------------- Requests ----------------------------------- fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): MutableList? { @@ -133,6 +137,17 @@ abstract class FirDataFlowAnalyzer( graphBuilder.dropSubgraphFromCall(call) } + @OptIn(PrivateForInline::class) + inline fun withIgnoreFunctionCalls(block: () -> T): T { + val oldValue = ignoreFunctionCalls + ignoreFunctionCalls = true + return try { + block() + } finally { + ignoreFunctionCalls = oldValue + } + } + // ----------------------------------- Named function ----------------------------------- fun enterFunction(function: FirFunction<*>) { @@ -683,7 +698,12 @@ abstract class FirDataFlowAnalyzer( graphBuilder.enterCall() } + @OptIn(PrivateForInline::class) fun exitFunctionCall(functionCall: FirFunctionCall, callCompleted: Boolean) { + if (ignoreFunctionCalls) { + graphBuilder.exitIgnoredCall(functionCall) + return + } val (functionCallNode, unionNode) = graphBuilder.exitFunctionCall(functionCall, callCompleted) unionNode?.let { unionFlowFromArguments(it) } functionCallNode.mergeIncomingFlow() diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt index 710c52db03c..63493bcd8d0 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/cfg/ControlFlowGraphBuilder.kt @@ -93,6 +93,25 @@ class ControlFlowGraphBuilder { private val exitSafeCallNodes: Stack = stackOf() private val exitElvisExpressionNodes: Stack = stackOf() + /* + * ignoredFunctionCalls is needed for resolve of += operator: + * we have two different calls for resolve, but we left only one of them, + * so we twice call `enterCall` and twice increase `levelCounter`, but + * `exitFunctionCall` we call only once. + * + * So workflow looks like that: + * Calls: + * - a.plus(b) // (1) + * - a.plusAssign(b) // (2) + * + * enterCall(a.plus(b)), increase counter + * exitIgnoredCall(a.plus(b)) // decrease counter + * enterCall(a.plusAssign(b)) // increase counter + * exitIgnoredCall(a.plusAssign(b)) // decrease counter + * exitFunctionCall(a.plus(b) | a.plusAssign(b)) // don't touch counter + */ + private val ignoredFunctionCalls: MutableSet = mutableSetOf() + // ----------------------------------- API for node builders ----------------------------------- private var idCounter: Int = Random.nextInt() @@ -818,8 +837,18 @@ class ControlFlowGraphBuilder { levelCounter++ } - fun exitFunctionCall(functionCall: FirFunctionCall, callCompleted: Boolean): Pair { + fun exitIgnoredCall(functionCall: FirFunctionCall) { levelCounter-- + ignoredFunctionCalls += functionCall + } + + fun exitFunctionCall(functionCall: FirFunctionCall, callCompleted: Boolean): Pair { + val callWasIgnored = ignoredFunctionCalls.remove(functionCall) + if (!callWasIgnored) { + levelCounter-- + } else { + ignoredFunctionCalls.clear() + } val returnsNothing = functionCall.resultType.isNothing val node = createFunctionCallNode(functionCall) val (kind, unionNode) = processUnionOfArguments(node, callCompleted) diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt index 70d163a2ad7..537c9210f81 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/inference/FirInferenceSession.kt @@ -14,25 +14,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage abstract class FirInferenceSession { companion object { - val DEFAULT: FirInferenceSession = object : FirInferenceSession() { - override fun shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = true - - override val currentConstraintSystem: ConstraintStorage - get() = ConstraintStorage.Empty - - override fun addPartiallyResolvedCall(call: T) where T : FirResolvable, T : FirStatement {} - override fun addErrorCall(call: T) where T : FirResolvable, T : FirStatement {} - override fun addCompetedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {} - - override fun inferPostponedVariables( - lambda: ResolvedLambdaAtom, - initialStorage: ConstraintStorage - ): Map? = null - - override fun writeOnlyStubs(call: T): Boolean where T : FirResolvable, T : FirStatement = false - override fun callCompleted(call: T): Boolean where T : FirResolvable, T : FirStatement = false - override fun shouldCompleteResolvedSubAtomsOf(call: T): Boolean where T : FirResolvable, T : FirStatement = true - } + val DEFAULT: FirInferenceSession = object : FirStubInferenceSession() {} } abstract fun shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement @@ -51,4 +33,24 @@ abstract class FirInferenceSession { abstract fun writeOnlyStubs(call: T): Boolean where T : FirResolvable, T : FirStatement abstract fun callCompleted(call: T): Boolean where T : FirResolvable, T : FirStatement abstract fun shouldCompleteResolvedSubAtomsOf(call: T): Boolean where T : FirResolvable, T : FirStatement -} \ No newline at end of file +} + +abstract class FirStubInferenceSession : FirInferenceSession() { + override fun shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = true + + override val currentConstraintSystem: ConstraintStorage + get() = ConstraintStorage.Empty + + override fun addPartiallyResolvedCall(call: T) where T : FirResolvable, T : FirStatement {} + override fun addErrorCall(call: T) where T : FirResolvable, T : FirStatement {} + override fun addCompetedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {} + + override fun inferPostponedVariables( + lambda: ResolvedLambdaAtom, + initialStorage: ConstraintStorage + ): Map? = null + + override fun writeOnlyStubs(call: T): Boolean where T : FirResolvable, T : FirStatement = false + override fun callCompleted(call: T): Boolean where T : FirResolvable, T : FirStatement = false + override fun shouldCompleteResolvedSubAtomsOf(call: T): Boolean where T : FirResolvable, T : FirStatement = true +} 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 270451ca6c5..f8f0a498e9d 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 @@ -22,10 +22,10 @@ import org.jetbrains.kotlin.fir.references.builder.buildExplicitSuperReference import org.jetbrains.kotlin.fir.references.builder.buildSimpleNamedReference import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference import org.jetbrains.kotlin.fir.resolve.* -import org.jetbrains.kotlin.fir.resolve.calls.ImplicitDispatchReceiverValue +import org.jetbrains.kotlin.fir.resolve.calls.* import org.jetbrains.kotlin.fir.resolve.calls.candidate -import org.jetbrains.kotlin.fir.resolve.calls.mapArguments import org.jetbrains.kotlin.fir.resolve.diagnostics.* +import org.jetbrains.kotlin.fir.resolve.inference.FirStubInferenceSession import org.jetbrains.kotlin.fir.resolve.transformers.InvocationKindTransformer import org.jetbrains.kotlin.fir.resolve.transformers.StoreReceiver import org.jetbrains.kotlin.fir.resolve.transformers.firClassLike @@ -199,6 +199,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform storeTypeFromCallee(functionCall) } if (functionCall.calleeReference !is FirSimpleNamedReference) return functionCall.compose() + if (functionCall.calleeReference is FirNamedReferenceWithCandidate) return functionCall.compose() dataFlowAnalyzer.enterCall() functionCall.annotations.forEach { it.accept(this, data) } functionCall.transform(InvocationKindTransformer, null) @@ -302,9 +303,10 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform require(operatorCall.operation != FirOperation.ASSIGN) operatorCall.annotations.forEach { it.accept(this, data) } + var (leftArgument, rightArgument) = operatorCall.arguments @Suppress("NAME_SHADOWING") - operatorCall.argumentList.transformArguments(this, ResolutionMode.ContextIndependent) - val (leftArgument, rightArgument) = operatorCall.arguments + leftArgument = leftArgument.transformSingle(transformer, ResolutionMode.ContextIndependent) + rightArgument = rightArgument.transformSingle(transformer, ResolutionMode.ContextDependent) fun createFunctionCall(name: Name) = buildFunctionCall { source = operatorCall.source?.withKind(FirFakeSourceElementKind.DesugaredCompoundAssignment) @@ -317,24 +319,35 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform } } - // TODO: disable DataFlowAnalyzer for resolving that two calls // x.plusAssign(y) val assignmentOperatorName = FirOperationNameConventions.ASSIGNMENTS.getValue(operatorCall.operation) val assignOperatorCall = createFunctionCall(assignmentOperatorName) - val resolvedAssignCall = assignOperatorCall.transformSingle(this, ResolutionMode.ContextIndependent) - val assignCallReference = resolvedAssignCall.toResolvedCallableReference() - val assignIsError = resolvedAssignCall.typeRef is FirErrorTypeRef + val resolvedAssignCall = resolveCandidateForAssignmentOperatorCall { + assignOperatorCall.transformSingle(this, ResolutionMode.ContextDependent) + } + val assignCallReference = resolvedAssignCall.calleeReference as? FirNamedReferenceWithCandidate + val assignIsError = assignCallReference?.isError ?: true // x = x + y val simpleOperatorName = FirOperationNameConventions.ASSIGNMENTS_TO_SIMPLE_OPERATOR.getValue(operatorCall.operation) val simpleOperatorCall = createFunctionCall(simpleOperatorName) - val resolvedOperatorCall = simpleOperatorCall.transformSingle(this, ResolutionMode.ContextIndependent) - val operatorCallReference = resolvedOperatorCall.toResolvedCallableReference() + val resolvedOperatorCall = resolveCandidateForAssignmentOperatorCall { + simpleOperatorCall.transformSingle(this, ResolutionMode.ContextDependent) + } + val operatorCallReference = resolvedOperatorCall.calleeReference as? FirNamedReferenceWithCandidate + val operatorIsError = operatorCallReference?.isError ?: true val lhsReference = leftArgument.toResolvedCallableReference() - val lhsIsVar = (lhsReference?.resolvedSymbol as? FirVariableSymbol<*>)?.fir?.isVar == true + val lhsVariable = (lhsReference?.resolvedSymbol as? FirVariableSymbol<*>)?.fir + val lhsIsVar = lhsVariable?.isVar == true return when { - operatorCallReference == null || (!lhsIsVar && !assignIsError) -> resolvedAssignCall.compose() - assignCallReference == null -> { + operatorIsError || (!lhsIsVar && !assignIsError) -> { + callCompleter.completeCall(resolvedAssignCall, noExpectedType) + dataFlowAnalyzer.exitFunctionCall(resolvedAssignCall, callCompleted = true) + resolvedAssignCall.compose() + } + assignIsError -> { + callCompleter.completeCall(resolvedOperatorCall, lhsVariable?.returnTypeRef ?: noExpectedType) + dataFlowAnalyzer.exitFunctionCall(resolvedOperatorCall, callCompleted = true) val assignment = buildVariableAssignment { source = operatorCall.source @@ -353,17 +366,39 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform } assignment.transform(transformer, ResolutionMode.ContextIndependent) } - else -> buildErrorExpression { - source = operatorCall.source - diagnostic = - ConeOperatorAmbiguityError(listOf(operatorCallReference.resolvedSymbol, assignCallReference.resolvedSymbol)) - }.compose() + else -> { + val operatorCallSymbol = operatorCallReference?.candidateSymbol + val assignmentCallSymbol = assignCallReference?.candidateSymbol + + requireNotNull(operatorCallSymbol) + requireNotNull(assignmentCallSymbol) + + buildErrorExpression { + source = operatorCall.source + diagnostic = + ConeOperatorAmbiguityError(listOf(operatorCallSymbol, assignmentCallSymbol)) + }.compose() + } } } throw IllegalArgumentException(operatorCall.render()) } + private inline fun resolveCandidateForAssignmentOperatorCall(block: () -> T): T { + return dataFlowAnalyzer.withIgnoreFunctionCalls { + callResolver.withNoArgumentsTransform { + inferenceComponents.withInferenceSession(InferenceSessionForAssignmentOperatorCall) { + block() + } + } + } + } + + private object InferenceSessionForAssignmentOperatorCall : FirStubInferenceSession() { + override fun shouldRunCompletion(call: T): Boolean where T : FirStatement, T : FirResolvable = false + } + private fun FirTypeRef.withTypeArgumentsForBareType(argument: FirExpression): FirTypeRef { // TODO: Everything should also work for case of checked-type itself is a type alias val baseTypeArguments = diff --git a/compiler/testData/codegen/box/javaInterop/genericSamProjectedOutWithNewInference.kt b/compiler/testData/codegen/box/javaInterop/genericSamProjectedOutWithNewInference.kt index 1675074a8c6..bb275c2a0b7 100644 --- a/compiler/testData/codegen/box/javaInterop/genericSamProjectedOutWithNewInference.kt +++ b/compiler/testData/codegen/box/javaInterop/genericSamProjectedOutWithNewInference.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR // TARGET_BACKEND: JVM // IGNORE_BACKEND: JVM_IR diff --git a/compiler/testData/codegen/box/regressions/lambdaPostponeConstruction.kt b/compiler/testData/codegen/box/regressions/lambdaPostponeConstruction.kt index 3a61a2b1836..8da1e337a0c 100644 --- a/compiler/testData/codegen/box/regressions/lambdaPostponeConstruction.kt +++ b/compiler/testData/codegen/box/regressions/lambdaPostponeConstruction.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND_FIR: JVM_IR class MyList operator fun MyList.plusAssign(element: T) {} diff --git a/compiler/testData/diagnostics/tests/inference/genericAssignmentOperator.fir.kt b/compiler/testData/diagnostics/tests/inference/genericAssignmentOperator.fir.kt index c35eb35f310..724d1bebfef 100644 --- a/compiler/testData/diagnostics/tests/inference/genericAssignmentOperator.fir.kt +++ b/compiler/testData/diagnostics/tests/inference/genericAssignmentOperator.fir.kt @@ -5,5 +5,5 @@ fun f(): R = R() operator fun Int.plusAssign(y: R) {} fun box() { - 1 += f() + 1 += f() } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/operatorsOverloading/kt1028.fir.kt b/compiler/testData/diagnostics/tests/operatorsOverloading/kt1028.fir.kt index b4e43ca6326..9455690baf2 100644 --- a/compiler/testData/diagnostics/tests/operatorsOverloading/kt1028.fir.kt +++ b/compiler/testData/diagnostics/tests/operatorsOverloading/kt1028.fir.kt @@ -29,7 +29,7 @@ class Test() fun test() { val control = Control() - control.MouseMoved += { it.X } // here + control.MouseMoved += { it.X } // here control.MouseMoved.plusAssign( { it.X } ) // ok } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda.fir.kt index 26372d350a2..89e50eaa83a 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda.fir.kt @@ -37,6 +37,6 @@ class B { } fun foo3(x: B) = { - x += { "" } - x += id { "" } + x += { "" } + x += id { "" } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda2.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda2.fir.kt index 72884822191..29dc106bc45 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda2.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/plusAssignWithLambda2.fir.kt @@ -13,8 +13,8 @@ fun id(x: T) = x fun main() { var newValue = A() - newValue += id { total -> A() } - newValue += id(fun(total) = A()) - newValue += id(fun(total): A { return A() }) + newValue += id { total -> A() } + newValue += id(fun(total) = A()) + newValue += id(fun(total): A { return A() }) newValue += id(::foo) }