[FIR] Resolve rhs of += in dependent context
This commit is contained in:
Vendored
+107
@@ -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<kotlin/Function1<kotlin/String, kotlin/String>>|"];
|
||||||
|
16 [label="Access variable R|<local>/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|<local>/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|<local>/list|.R|/plusAssign|<R|(kotlin/String) -> 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];
|
||||||
|
|
||||||
|
}
|
||||||
Vendored
+19
@@ -0,0 +1,19 @@
|
|||||||
|
// ISSUE: KT-39005
|
||||||
|
// !DUMP_CFG
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val list: MutableList<(String) -> String> = null!!
|
||||||
|
list += { it }
|
||||||
|
}
|
||||||
|
|
||||||
|
//class A<T>(private val executor: ((T) -> Unit) -> Unit)
|
||||||
|
//
|
||||||
|
//fun <T> postpone(computation: () -> T): A<T> {
|
||||||
|
// val queue = mutableListOf<() -> Unit>()
|
||||||
|
//
|
||||||
|
// return A { resolve ->
|
||||||
|
// queue += {
|
||||||
|
// resolve(computation())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
Vendored
+16
@@ -0,0 +1,16 @@
|
|||||||
|
FILE: plusAssignWithLambdaInRhs.kt
|
||||||
|
public final operator fun <T> R|kotlin/collections/MutableList<T>|.plusAssign(x: R|T|): R|kotlin/Unit| {
|
||||||
|
}
|
||||||
|
public final operator fun <T> R|kotlin/collections/List<T>|.plus(x: R|T|): R|kotlin/collections/List<T>| {
|
||||||
|
^plus this@R|/plus|
|
||||||
|
}
|
||||||
|
public final fun test(): R|kotlin/Unit| {
|
||||||
|
lval list: R|kotlin/collections/MutableList<kotlin/Function1<kotlin/String, kotlin/String>>| = Null(null)!!
|
||||||
|
R|<local>/list|.R|/plusAssign|<R|(kotlin/String) -> kotlin/String|>(fun <anonymous>(it: R|kotlin/String|): R|kotlin/String| {
|
||||||
|
^ R|<local>/it|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
public final fun <T> listOf(x: R|T|): R|kotlin/collections/List<T>| {
|
||||||
|
^listOf Null(null)!!
|
||||||
|
}
|
||||||
+5
@@ -716,6 +716,11 @@ public class FirDiagnosticsWithStdlibTestGenerated extends AbstractFirDiagnostic
|
|||||||
public void testIfElvisReturn() throws Exception {
|
public void testIfElvisReturn() throws Exception {
|
||||||
runTest("compiler/fir/analysis-tests/testData/resolveWithStdlib/inference/ifElvisReturn.kt");
|
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")
|
@TestMetadata("compiler/fir/analysis-tests/testData/resolveWithStdlib/j+k")
|
||||||
|
|||||||
@@ -58,14 +58,22 @@ class FirCallResolver(
|
|||||||
inferenceComponents.session.callConflictResolverFactory
|
inferenceComponents.session.callConflictResolverFactory
|
||||||
.create(TypeSpecificityComparator.NONE, inferenceComponents)
|
.create(TypeSpecificityComparator.NONE, inferenceComponents)
|
||||||
|
|
||||||
|
@PrivateForInline
|
||||||
|
var needTransformArguments: Boolean = true
|
||||||
|
|
||||||
|
@OptIn(PrivateForInline::class)
|
||||||
fun resolveCallAndSelectCandidate(functionCall: FirFunctionCall): FirFunctionCall {
|
fun resolveCallAndSelectCandidate(functionCall: FirFunctionCall): FirFunctionCall {
|
||||||
qualifiedResolver.reset()
|
qualifiedResolver.reset()
|
||||||
@Suppress("NAME_SHADOWING")
|
@Suppress("NAME_SHADOWING")
|
||||||
val functionCall = functionCall.transformExplicitReceiver(transformer, ResolutionMode.ContextIndependent)
|
val functionCall = if (needTransformArguments) {
|
||||||
.also {
|
functionCall.transformExplicitReceiver(transformer, ResolutionMode.ContextIndependent)
|
||||||
dataFlowAnalyzer.enterQualifiedAccessExpression()
|
.also {
|
||||||
functionCall.argumentList.transformArguments(transformer, ResolutionMode.ContextDependent)
|
dataFlowAnalyzer.enterQualifiedAccessExpression()
|
||||||
}
|
functionCall.argumentList.transformArguments(transformer, ResolutionMode.ContextDependent)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
functionCall
|
||||||
|
}
|
||||||
|
|
||||||
val name = functionCall.calleeReference.name
|
val name = functionCall.calleeReference.name
|
||||||
val result = collectCandidates(functionCall, name)
|
val result = collectCandidates(functionCall, name)
|
||||||
@@ -289,6 +297,17 @@ class FirCallResolver(
|
|||||||
return callResolver.selectDelegatingConstructorCall(delegatedConstructorCall, name, result, callInfo)
|
return callResolver.selectDelegatingConstructorCall(delegatedConstructorCall, name, result, callInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(PrivateForInline::class)
|
||||||
|
inline fun <T> withNoArgumentsTransform(block: () -> T): T {
|
||||||
|
val oldValue = needTransformArguments
|
||||||
|
needTransformArguments = false
|
||||||
|
return try {
|
||||||
|
block()
|
||||||
|
} finally {
|
||||||
|
needTransformArguments = oldValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun selectDelegatingConstructorCall(
|
private fun selectDelegatingConstructorCall(
|
||||||
call: FirDelegatedConstructorCall, name: Name, result: CandidateCollector, callInfo: CallInfo
|
call: FirDelegatedConstructorCall, name: Name, result: CandidateCollector, callInfo: CallInfo
|
||||||
): FirDelegatedConstructorCall {
|
): FirDelegatedConstructorCall {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
package org.jetbrains.kotlin.fir.resolve.dfa
|
package org.jetbrains.kotlin.fir.resolve.dfa
|
||||||
|
|
||||||
import org.jetbrains.kotlin.fir.FirSession
|
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.FirResolvedContractDescription
|
||||||
import org.jetbrains.kotlin.fir.contracts.description.ConeBooleanConstantReference
|
import org.jetbrains.kotlin.fir.contracts.description.ConeBooleanConstantReference
|
||||||
import org.jetbrains.kotlin.fir.contracts.description.ConeConditionalEffectDeclaration
|
import org.jetbrains.kotlin.fir.contracts.description.ConeConditionalEffectDeclaration
|
||||||
@@ -106,6 +107,9 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
|||||||
protected val any = components.session.builtinTypes.anyType.type
|
protected val any = components.session.builtinTypes.anyType.type
|
||||||
private val nullableNothing = components.session.builtinTypes.nullableNothingType.type
|
private val nullableNothing = components.session.builtinTypes.nullableNothingType.type
|
||||||
|
|
||||||
|
@PrivateForInline
|
||||||
|
var ignoreFunctionCalls: Boolean = false
|
||||||
|
|
||||||
// ----------------------------------- Requests -----------------------------------
|
// ----------------------------------- Requests -----------------------------------
|
||||||
|
|
||||||
fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): MutableList<ConeKotlinType>? {
|
fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): MutableList<ConeKotlinType>? {
|
||||||
@@ -133,6 +137,17 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
|||||||
graphBuilder.dropSubgraphFromCall(call)
|
graphBuilder.dropSubgraphFromCall(call)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(PrivateForInline::class)
|
||||||
|
inline fun <T> withIgnoreFunctionCalls(block: () -> T): T {
|
||||||
|
val oldValue = ignoreFunctionCalls
|
||||||
|
ignoreFunctionCalls = true
|
||||||
|
return try {
|
||||||
|
block()
|
||||||
|
} finally {
|
||||||
|
ignoreFunctionCalls = oldValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------- Named function -----------------------------------
|
// ----------------------------------- Named function -----------------------------------
|
||||||
|
|
||||||
fun enterFunction(function: FirFunction<*>) {
|
fun enterFunction(function: FirFunction<*>) {
|
||||||
@@ -683,7 +698,12 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
|||||||
graphBuilder.enterCall()
|
graphBuilder.enterCall()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(PrivateForInline::class)
|
||||||
fun exitFunctionCall(functionCall: FirFunctionCall, callCompleted: Boolean) {
|
fun exitFunctionCall(functionCall: FirFunctionCall, callCompleted: Boolean) {
|
||||||
|
if (ignoreFunctionCalls) {
|
||||||
|
graphBuilder.exitIgnoredCall(functionCall)
|
||||||
|
return
|
||||||
|
}
|
||||||
val (functionCallNode, unionNode) = graphBuilder.exitFunctionCall(functionCall, callCompleted)
|
val (functionCallNode, unionNode) = graphBuilder.exitFunctionCall(functionCall, callCompleted)
|
||||||
unionNode?.let { unionFlowFromArguments(it) }
|
unionNode?.let { unionFlowFromArguments(it) }
|
||||||
functionCallNode.mergeIncomingFlow()
|
functionCallNode.mergeIncomingFlow()
|
||||||
|
|||||||
+30
-1
@@ -93,6 +93,25 @@ class ControlFlowGraphBuilder {
|
|||||||
private val exitSafeCallNodes: Stack<ExitSafeCallNode> = stackOf()
|
private val exitSafeCallNodes: Stack<ExitSafeCallNode> = stackOf()
|
||||||
private val exitElvisExpressionNodes: Stack<ElvisExitNode> = stackOf()
|
private val exitElvisExpressionNodes: Stack<ElvisExitNode> = 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<FirFunctionCall> = mutableSetOf()
|
||||||
|
|
||||||
// ----------------------------------- API for node builders -----------------------------------
|
// ----------------------------------- API for node builders -----------------------------------
|
||||||
|
|
||||||
private var idCounter: Int = Random.nextInt()
|
private var idCounter: Int = Random.nextInt()
|
||||||
@@ -818,8 +837,18 @@ class ControlFlowGraphBuilder {
|
|||||||
levelCounter++
|
levelCounter++
|
||||||
}
|
}
|
||||||
|
|
||||||
fun exitFunctionCall(functionCall: FirFunctionCall, callCompleted: Boolean): Pair<FunctionCallNode, UnionFunctionCallArgumentsNode?> {
|
fun exitIgnoredCall(functionCall: FirFunctionCall) {
|
||||||
levelCounter--
|
levelCounter--
|
||||||
|
ignoredFunctionCalls += functionCall
|
||||||
|
}
|
||||||
|
|
||||||
|
fun exitFunctionCall(functionCall: FirFunctionCall, callCompleted: Boolean): Pair<FunctionCallNode, UnionFunctionCallArgumentsNode?> {
|
||||||
|
val callWasIgnored = ignoredFunctionCalls.remove(functionCall)
|
||||||
|
if (!callWasIgnored) {
|
||||||
|
levelCounter--
|
||||||
|
} else {
|
||||||
|
ignoredFunctionCalls.clear()
|
||||||
|
}
|
||||||
val returnsNothing = functionCall.resultType.isNothing
|
val returnsNothing = functionCall.resultType.isNothing
|
||||||
val node = createFunctionCallNode(functionCall)
|
val node = createFunctionCallNode(functionCall)
|
||||||
val (kind, unionNode) = processUnionOfArguments(node, callCompleted)
|
val (kind, unionNode) = processUnionOfArguments(node, callCompleted)
|
||||||
|
|||||||
+22
-20
@@ -14,25 +14,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
|
|||||||
|
|
||||||
abstract class FirInferenceSession {
|
abstract class FirInferenceSession {
|
||||||
companion object {
|
companion object {
|
||||||
val DEFAULT: FirInferenceSession = object : FirInferenceSession() {
|
val DEFAULT: FirInferenceSession = object : FirStubInferenceSession() {}
|
||||||
override fun <T> shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = true
|
|
||||||
|
|
||||||
override val currentConstraintSystem: ConstraintStorage
|
|
||||||
get() = ConstraintStorage.Empty
|
|
||||||
|
|
||||||
override fun <T> addPartiallyResolvedCall(call: T) where T : FirResolvable, T : FirStatement {}
|
|
||||||
override fun <T> addErrorCall(call: T) where T : FirResolvable, T : FirStatement {}
|
|
||||||
override fun <T> addCompetedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {}
|
|
||||||
|
|
||||||
override fun inferPostponedVariables(
|
|
||||||
lambda: ResolvedLambdaAtom,
|
|
||||||
initialStorage: ConstraintStorage
|
|
||||||
): Map<ConeTypeVariableTypeConstructor, ConeKotlinType>? = null
|
|
||||||
|
|
||||||
override fun <T> writeOnlyStubs(call: T): Boolean where T : FirResolvable, T : FirStatement = false
|
|
||||||
override fun <T> callCompleted(call: T): Boolean where T : FirResolvable, T : FirStatement = false
|
|
||||||
override fun <T> shouldCompleteResolvedSubAtomsOf(call: T): Boolean where T : FirResolvable, T : FirStatement = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract fun <T> shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement
|
abstract fun <T> shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement
|
||||||
@@ -51,4 +33,24 @@ abstract class FirInferenceSession {
|
|||||||
abstract fun <T> writeOnlyStubs(call: T): Boolean where T : FirResolvable, T : FirStatement
|
abstract fun <T> writeOnlyStubs(call: T): Boolean where T : FirResolvable, T : FirStatement
|
||||||
abstract fun <T> callCompleted(call: T): Boolean where T : FirResolvable, T : FirStatement
|
abstract fun <T> callCompleted(call: T): Boolean where T : FirResolvable, T : FirStatement
|
||||||
abstract fun <T> shouldCompleteResolvedSubAtomsOf(call: T): Boolean where T : FirResolvable, T : FirStatement
|
abstract fun <T> shouldCompleteResolvedSubAtomsOf(call: T): Boolean where T : FirResolvable, T : FirStatement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class FirStubInferenceSession : FirInferenceSession() {
|
||||||
|
override fun <T> shouldRunCompletion(call: T): Boolean where T : FirResolvable, T : FirStatement = true
|
||||||
|
|
||||||
|
override val currentConstraintSystem: ConstraintStorage
|
||||||
|
get() = ConstraintStorage.Empty
|
||||||
|
|
||||||
|
override fun <T> addPartiallyResolvedCall(call: T) where T : FirResolvable, T : FirStatement {}
|
||||||
|
override fun <T> addErrorCall(call: T) where T : FirResolvable, T : FirStatement {}
|
||||||
|
override fun <T> addCompetedCall(call: T, candidate: Candidate) where T : FirResolvable, T : FirStatement {}
|
||||||
|
|
||||||
|
override fun inferPostponedVariables(
|
||||||
|
lambda: ResolvedLambdaAtom,
|
||||||
|
initialStorage: ConstraintStorage
|
||||||
|
): Map<ConeTypeVariableTypeConstructor, ConeKotlinType>? = null
|
||||||
|
|
||||||
|
override fun <T> writeOnlyStubs(call: T): Boolean where T : FirResolvable, T : FirStatement = false
|
||||||
|
override fun <T> callCompleted(call: T): Boolean where T : FirResolvable, T : FirStatement = false
|
||||||
|
override fun <T> shouldCompleteResolvedSubAtomsOf(call: T): Boolean where T : FirResolvable, T : FirStatement = true
|
||||||
|
}
|
||||||
|
|||||||
+53
-18
@@ -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.builder.buildSimpleNamedReference
|
||||||
import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference
|
import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference
|
||||||
import org.jetbrains.kotlin.fir.resolve.*
|
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.candidate
|
||||||
import org.jetbrains.kotlin.fir.resolve.calls.mapArguments
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.diagnostics.*
|
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.InvocationKindTransformer
|
||||||
import org.jetbrains.kotlin.fir.resolve.transformers.StoreReceiver
|
import org.jetbrains.kotlin.fir.resolve.transformers.StoreReceiver
|
||||||
import org.jetbrains.kotlin.fir.resolve.transformers.firClassLike
|
import org.jetbrains.kotlin.fir.resolve.transformers.firClassLike
|
||||||
@@ -199,6 +199,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
|
|||||||
storeTypeFromCallee(functionCall)
|
storeTypeFromCallee(functionCall)
|
||||||
}
|
}
|
||||||
if (functionCall.calleeReference !is FirSimpleNamedReference) return functionCall.compose()
|
if (functionCall.calleeReference !is FirSimpleNamedReference) return functionCall.compose()
|
||||||
|
if (functionCall.calleeReference is FirNamedReferenceWithCandidate) return functionCall.compose()
|
||||||
dataFlowAnalyzer.enterCall()
|
dataFlowAnalyzer.enterCall()
|
||||||
functionCall.annotations.forEach { it.accept(this, data) }
|
functionCall.annotations.forEach { it.accept(this, data) }
|
||||||
functionCall.transform<FirFunctionCall, Nothing?>(InvocationKindTransformer, null)
|
functionCall.transform<FirFunctionCall, Nothing?>(InvocationKindTransformer, null)
|
||||||
@@ -302,9 +303,10 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
|
|||||||
require(operatorCall.operation != FirOperation.ASSIGN)
|
require(operatorCall.operation != FirOperation.ASSIGN)
|
||||||
|
|
||||||
operatorCall.annotations.forEach { it.accept(this, data) }
|
operatorCall.annotations.forEach { it.accept(this, data) }
|
||||||
|
var (leftArgument, rightArgument) = operatorCall.arguments
|
||||||
@Suppress("NAME_SHADOWING")
|
@Suppress("NAME_SHADOWING")
|
||||||
operatorCall.argumentList.transformArguments(this, ResolutionMode.ContextIndependent)
|
leftArgument = leftArgument.transformSingle(transformer, ResolutionMode.ContextIndependent)
|
||||||
val (leftArgument, rightArgument) = operatorCall.arguments
|
rightArgument = rightArgument.transformSingle(transformer, ResolutionMode.ContextDependent)
|
||||||
|
|
||||||
fun createFunctionCall(name: Name) = buildFunctionCall {
|
fun createFunctionCall(name: Name) = buildFunctionCall {
|
||||||
source = operatorCall.source?.withKind(FirFakeSourceElementKind.DesugaredCompoundAssignment)
|
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)
|
// x.plusAssign(y)
|
||||||
val assignmentOperatorName = FirOperationNameConventions.ASSIGNMENTS.getValue(operatorCall.operation)
|
val assignmentOperatorName = FirOperationNameConventions.ASSIGNMENTS.getValue(operatorCall.operation)
|
||||||
val assignOperatorCall = createFunctionCall(assignmentOperatorName)
|
val assignOperatorCall = createFunctionCall(assignmentOperatorName)
|
||||||
val resolvedAssignCall = assignOperatorCall.transformSingle(this, ResolutionMode.ContextIndependent)
|
val resolvedAssignCall = resolveCandidateForAssignmentOperatorCall {
|
||||||
val assignCallReference = resolvedAssignCall.toResolvedCallableReference()
|
assignOperatorCall.transformSingle(this, ResolutionMode.ContextDependent)
|
||||||
val assignIsError = resolvedAssignCall.typeRef is FirErrorTypeRef
|
}
|
||||||
|
val assignCallReference = resolvedAssignCall.calleeReference as? FirNamedReferenceWithCandidate
|
||||||
|
val assignIsError = assignCallReference?.isError ?: true
|
||||||
// x = x + y
|
// x = x + y
|
||||||
val simpleOperatorName = FirOperationNameConventions.ASSIGNMENTS_TO_SIMPLE_OPERATOR.getValue(operatorCall.operation)
|
val simpleOperatorName = FirOperationNameConventions.ASSIGNMENTS_TO_SIMPLE_OPERATOR.getValue(operatorCall.operation)
|
||||||
val simpleOperatorCall = createFunctionCall(simpleOperatorName)
|
val simpleOperatorCall = createFunctionCall(simpleOperatorName)
|
||||||
val resolvedOperatorCall = simpleOperatorCall.transformSingle(this, ResolutionMode.ContextIndependent)
|
val resolvedOperatorCall = resolveCandidateForAssignmentOperatorCall {
|
||||||
val operatorCallReference = resolvedOperatorCall.toResolvedCallableReference()
|
simpleOperatorCall.transformSingle(this, ResolutionMode.ContextDependent)
|
||||||
|
}
|
||||||
|
val operatorCallReference = resolvedOperatorCall.calleeReference as? FirNamedReferenceWithCandidate
|
||||||
|
val operatorIsError = operatorCallReference?.isError ?: true
|
||||||
|
|
||||||
val lhsReference = leftArgument.toResolvedCallableReference()
|
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 {
|
return when {
|
||||||
operatorCallReference == null || (!lhsIsVar && !assignIsError) -> resolvedAssignCall.compose()
|
operatorIsError || (!lhsIsVar && !assignIsError) -> {
|
||||||
assignCallReference == null -> {
|
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 =
|
val assignment =
|
||||||
buildVariableAssignment {
|
buildVariableAssignment {
|
||||||
source = operatorCall.source
|
source = operatorCall.source
|
||||||
@@ -353,17 +366,39 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
|
|||||||
}
|
}
|
||||||
assignment.transform(transformer, ResolutionMode.ContextIndependent)
|
assignment.transform(transformer, ResolutionMode.ContextIndependent)
|
||||||
}
|
}
|
||||||
else -> buildErrorExpression {
|
else -> {
|
||||||
source = operatorCall.source
|
val operatorCallSymbol = operatorCallReference?.candidateSymbol
|
||||||
diagnostic =
|
val assignmentCallSymbol = assignCallReference?.candidateSymbol
|
||||||
ConeOperatorAmbiguityError(listOf(operatorCallReference.resolvedSymbol, assignCallReference.resolvedSymbol))
|
|
||||||
}.compose()
|
requireNotNull(operatorCallSymbol)
|
||||||
|
requireNotNull(assignmentCallSymbol)
|
||||||
|
|
||||||
|
buildErrorExpression {
|
||||||
|
source = operatorCall.source
|
||||||
|
diagnostic =
|
||||||
|
ConeOperatorAmbiguityError(listOf(operatorCallSymbol, assignmentCallSymbol))
|
||||||
|
}.compose()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw IllegalArgumentException(operatorCall.render())
|
throw IllegalArgumentException(operatorCall.render())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inline fun <T> resolveCandidateForAssignmentOperatorCall(block: () -> T): T {
|
||||||
|
return dataFlowAnalyzer.withIgnoreFunctionCalls {
|
||||||
|
callResolver.withNoArgumentsTransform {
|
||||||
|
inferenceComponents.withInferenceSession(InferenceSessionForAssignmentOperatorCall) {
|
||||||
|
block()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private object InferenceSessionForAssignmentOperatorCall : FirStubInferenceSession() {
|
||||||
|
override fun <T> shouldRunCompletion(call: T): Boolean where T : FirStatement, T : FirResolvable = false
|
||||||
|
}
|
||||||
|
|
||||||
private fun FirTypeRef.withTypeArgumentsForBareType(argument: FirExpression): FirTypeRef {
|
private fun FirTypeRef.withTypeArgumentsForBareType(argument: FirExpression): FirTypeRef {
|
||||||
// TODO: Everything should also work for case of checked-type itself is a type alias
|
// TODO: Everything should also work for case of checked-type itself is a type alias
|
||||||
val baseTypeArguments =
|
val baseTypeArguments =
|
||||||
|
|||||||
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
|
||||||
// TARGET_BACKEND: JVM
|
// TARGET_BACKEND: JVM
|
||||||
// IGNORE_BACKEND: JVM_IR
|
// IGNORE_BACKEND: JVM_IR
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND_FIR: JVM_IR
|
|
||||||
class MyList<T>
|
class MyList<T>
|
||||||
|
|
||||||
operator fun <T> MyList<T>.plusAssign(element: T) {}
|
operator fun <T> MyList<T>.plusAssign(element: T) {}
|
||||||
|
|||||||
+1
-1
@@ -5,5 +5,5 @@ fun <T> f(): R<T> = R<T>()
|
|||||||
operator fun Int.plusAssign(y: R<Int>) {}
|
operator fun Int.plusAssign(y: R<Int>) {}
|
||||||
|
|
||||||
fun box() {
|
fun box() {
|
||||||
<!INAPPLICABLE_CANDIDATE!>1 += f()<!>
|
1 += f()
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,7 @@ class Test()
|
|||||||
fun test()
|
fun test()
|
||||||
{
|
{
|
||||||
val control = Control()
|
val control = Control()
|
||||||
control.MouseMoved += { <!UNRESOLVED_REFERENCE!>it<!>.<!UNRESOLVED_REFERENCE!>X<!> } // here
|
control.MouseMoved += { it.X } // here
|
||||||
control.MouseMoved.plusAssign( { it.X } ) // ok
|
control.MouseMoved.plusAssign( { it.X } ) // ok
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Vendored
+2
-2
@@ -37,6 +37,6 @@ class B {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun foo3(x: B) = {
|
fun foo3(x: B) = {
|
||||||
<!VARIABLE_EXPECTED!>x<!> += { "" }
|
x += { "" }
|
||||||
<!VARIABLE_EXPECTED!>x<!> += id { "" }
|
x += id { "" }
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+3
-3
@@ -13,8 +13,8 @@ fun <T> id(x: T) = x
|
|||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
var newValue = A()
|
var newValue = A()
|
||||||
<!INAPPLICABLE_CANDIDATE!>newValue += id { total -> A() }<!>
|
<!ASSIGN_OPERATOR_AMBIGUITY!>newValue += id { total -> A() }<!>
|
||||||
<!INAPPLICABLE_CANDIDATE!>newValue += id(fun(total) = A())<!>
|
<!ASSIGN_OPERATOR_AMBIGUITY!>newValue += id(fun(total) = A())<!>
|
||||||
<!INAPPLICABLE_CANDIDATE!>newValue += id(fun(total): A { return A() })<!>
|
<!ASSIGN_OPERATOR_AMBIGUITY!>newValue += id(fun(total): A { return A() })<!>
|
||||||
<!ASSIGN_OPERATOR_AMBIGUITY!>newValue += id(::foo)<!>
|
<!ASSIGN_OPERATOR_AMBIGUITY!>newValue += id(::foo)<!>
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user