[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 {
|
||||
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")
|
||||
|
||||
@@ -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 <T> 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 {
|
||||
|
||||
@@ -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<FLOW : Flow>(
|
||||
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<ConeKotlinType>? {
|
||||
@@ -133,6 +137,17 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
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 -----------------------------------
|
||||
|
||||
fun enterFunction(function: FirFunction<*>) {
|
||||
@@ -683,7 +698,12 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
|
||||
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()
|
||||
|
||||
+30
-1
@@ -93,6 +93,25 @@ class ControlFlowGraphBuilder {
|
||||
private val exitSafeCallNodes: Stack<ExitSafeCallNode> = 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 -----------------------------------
|
||||
|
||||
private var idCounter: Int = Random.nextInt()
|
||||
@@ -818,8 +837,18 @@ class ControlFlowGraphBuilder {
|
||||
levelCounter++
|
||||
}
|
||||
|
||||
fun exitFunctionCall(functionCall: FirFunctionCall, callCompleted: Boolean): Pair<FunctionCallNode, UnionFunctionCallArgumentsNode?> {
|
||||
fun exitIgnoredCall(functionCall: FirFunctionCall) {
|
||||
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 node = createFunctionCallNode(functionCall)
|
||||
val (kind, unionNode) = processUnionOfArguments(node, callCompleted)
|
||||
|
||||
+22
-20
@@ -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 <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
|
||||
}
|
||||
val DEFAULT: FirInferenceSession = object : FirStubInferenceSession() {}
|
||||
}
|
||||
|
||||
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> callCompleted(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.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<FirFunctionCall, Nothing?>(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 <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 {
|
||||
// TODO: Everything should also work for case of checked-type itself is a type alias
|
||||
val baseTypeArguments =
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
// TARGET_BACKEND: JVM
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND_FIR: JVM_IR
|
||||
class MyList<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>) {}
|
||||
|
||||
fun box() {
|
||||
<!INAPPLICABLE_CANDIDATE!>1 += f()<!>
|
||||
1 += f()
|
||||
}
|
||||
@@ -29,7 +29,7 @@ class Test()
|
||||
fun test()
|
||||
{
|
||||
val control = Control()
|
||||
control.MouseMoved += { <!UNRESOLVED_REFERENCE!>it<!>.<!UNRESOLVED_REFERENCE!>X<!> } // here
|
||||
control.MouseMoved += { it.X } // here
|
||||
control.MouseMoved.plusAssign( { it.X } ) // ok
|
||||
}
|
||||
}
|
||||
Vendored
+2
-2
@@ -37,6 +37,6 @@ class B {
|
||||
}
|
||||
|
||||
fun foo3(x: B) = {
|
||||
<!VARIABLE_EXPECTED!>x<!> += { "" }
|
||||
<!VARIABLE_EXPECTED!>x<!> += id { "" }
|
||||
x += { "" }
|
||||
x += id { "" }
|
||||
}
|
||||
|
||||
Vendored
+3
-3
@@ -13,8 +13,8 @@ fun <T> id(x: T) = x
|
||||
|
||||
fun main() {
|
||||
var newValue = A()
|
||||
<!INAPPLICABLE_CANDIDATE!>newValue += id { total -> A() }<!>
|
||||
<!INAPPLICABLE_CANDIDATE!>newValue += id(fun(total) = A())<!>
|
||||
<!INAPPLICABLE_CANDIDATE!>newValue += id(fun(total): A { return A() })<!>
|
||||
<!ASSIGN_OPERATOR_AMBIGUITY!>newValue += id { total -> A() }<!>
|
||||
<!ASSIGN_OPERATOR_AMBIGUITY!>newValue += id(fun(total) = A())<!>
|
||||
<!ASSIGN_OPERATOR_AMBIGUITY!>newValue += id(fun(total): A { return A() })<!>
|
||||
<!ASSIGN_OPERATOR_AMBIGUITY!>newValue += id(::foo)<!>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user