FIR: Fix resolving += to ignore + if it doesn't return correct type

This is the FIR fix of the FE change tracked by
https://youtrack.jetbrains.com/issue/KT-45503

Consider the following code:

```
fun foo() {
  var l = mutableListOf("")
  l += ""
}
```

The above code used to be considered invalid due to ambiguous `+=`. But
after KT-45503, it's now considered valid code because `plus` is not
really a valid option for this code. This is because the return type
of `plus` for `List` is `List`, which does not match the type of
variable `l`.

As for now, FIR rejects the above code due to ambiguous `+=`. This
change fixes that by also consider the return type when resolving `+=`.
This commit is contained in:
Tianyu Geng
2021-04-09 14:01:19 -07:00
committed by TeamCityServer
parent c43faa2ada
commit 4d2e3a2379
3 changed files with 16 additions and 31 deletions
@@ -388,6 +388,20 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
val operatorCallReference = resolvedOperatorCall.calleeReference as? FirNamedReferenceWithCandidate
val operatorIsResolvable = operatorCallReference?.isError == false
fun operatorReturnTypeMatches(candidate: Candidate): Boolean {
// After KT-45503, non-assign flavor of operator is checked more strictly: the return type must be assignable to the variable.
val operatorCallReturnType = resolvedOperatorCall.typeRef.coneType
val substitutor = candidate.system.currentStorage()
.buildAbstractResultingSubstitutor(candidate.system.typeSystemContext) as ConeSubstitutor
return AbstractTypeChecker.isSubtypeOf(
session.typeContext,
substitutor.substituteOrSelf(operatorCallReturnType),
leftArgument.typeRef.coneType
)
}
// following `!!` is safe since `operatorIsResolvable = true` implies `operatorCallReference != null`
val operatorReturnTypeMatches = operatorIsResolvable && operatorReturnTypeMatches(operatorCallReference!!.candidate)
val lhsReference = leftArgument.toResolvedCallableReference()
val lhsSymbol = lhsReference?.resolvedSymbol as? FirVariableSymbol<*>
val lhsVariable = lhsSymbol?.fir
@@ -444,6 +458,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
!assignIsResolvable && !operatorIsResolvable -> chooseAssign()
!assignIsResolvable && operatorIsResolvable -> chooseOperator()
assignIsResolvable && !operatorIsResolvable -> chooseAssign()
assignIsResolvable && operatorIsResolvable && !operatorReturnTypeMatches -> chooseAssign()
else -> reportAmbiguity()
}
}
@@ -1,31 +0,0 @@
// WITH_RUNTIME
fun test1() {
var list = ArrayList<Int>()
<!ASSIGN_OPERATOR_AMBIGUITY!>list -= 2<!>
}
fun test2() {
var set = HashMap<Int, Int>()
<!ASSIGN_OPERATOR_AMBIGUITY!>set += 2 to 2<!>
}
fun test3() {
var set = HashSet<Int>()
<!ASSIGN_OPERATOR_AMBIGUITY!>set -= 2<!>
}
fun test4() {
var list = mutableListOf(1)
<!ASSIGN_OPERATOR_AMBIGUITY!>list += 2<!>
}
fun test5() {
var map = mutableMapOf(1 to 1)
<!ASSIGN_OPERATOR_AMBIGUITY!>map += 2 to 2<!>
}
fun test6() {
var set = mutableSetOf(1)
<!ASSIGN_OPERATOR_AMBIGUITY!>set += 2<!>
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
// WITH_RUNTIME
fun test1() {