FIR: Choose a resolved candidate for augmented assignment when both

assign and operator candidates are unsuccessful.
This commit is contained in:
Mark Punzalan
2021-05-25 23:10:15 +00:00
committed by teamcityserver
parent 32bb64a225
commit d1531f9cdd
9 changed files with 34 additions and 44 deletions
@@ -39,7 +39,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.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransformer) : FirPartialBodyResolveTransformer(transformer) {
private inline val builtinTypes: BuiltinTypes get() = session.builtinTypes
@@ -386,7 +386,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
assignOperatorCall.transformSingle(this, ResolutionMode.ContextDependent)
}
val assignCallReference = resolvedAssignCall.calleeReference as? FirNamedReferenceWithCandidate
val assignIsResolvable = assignCallReference?.isError == false
val assignIsSuccessful = assignCallReference?.isError == false
// x = x + y
val simpleOperatorName = FirOperationNameConventions.ASSIGNMENTS_TO_SIMPLE_OPERATOR.getValue(assignmentOperatorStatement.operation)
@@ -395,7 +395,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
simpleOperatorCall.transformSingle(this, ResolutionMode.ContextDependent)
}
val operatorCallReference = resolvedOperatorCall.calleeReference as? FirNamedReferenceWithCandidate
val operatorIsResolvable = operatorCallReference?.isError == false
val operatorIsSuccessful = 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.
@@ -408,8 +408,8 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
leftArgument.typeRef.coneType
)
}
// following `!!` is safe since `operatorIsResolvable = true` implies `operatorCallReference != null`
val operatorReturnTypeMatches = operatorIsResolvable && operatorReturnTypeMatches(operatorCallReference!!.candidate)
// following `!!` is safe since `operatorIsSuccessful = true` implies `operatorCallReference != null`
val operatorReturnTypeMatches = operatorIsSuccessful && operatorReturnTypeMatches(operatorCallReference!!.candidate)
val lhsReference = leftArgument.toResolvedCallableReference()
val lhsSymbol = lhsReference?.resolvedSymbol as? FirVariableSymbol<*>
@@ -467,11 +467,21 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
}
return when {
assignIsResolvable && !lhsIsVar -> chooseAssign()
!assignIsResolvable && !operatorIsResolvable -> chooseAssign()
!assignIsResolvable && operatorIsResolvable -> chooseOperator()
assignIsResolvable && !operatorIsResolvable -> chooseAssign()
assignIsResolvable && operatorIsResolvable && !operatorReturnTypeMatches -> chooseAssign()
assignIsSuccessful && !lhsIsVar -> chooseAssign()
!assignIsSuccessful && !operatorIsSuccessful -> {
// If neither candidate is successful, choose whichever is resolved, prioritizing assign
val isAssignResolved = assignCallReference.safeAs<FirErrorReferenceWithCandidate>()?.diagnostic !is ConeUnresolvedNameError
val isOperatorResolved =
operatorCallReference.safeAs<FirErrorReferenceWithCandidate>()?.diagnostic !is ConeUnresolvedNameError
when {
isAssignResolved -> chooseAssign()
isOperatorResolved -> chooseOperator()
else -> chooseAssign()
}
}
!assignIsSuccessful && operatorIsSuccessful -> chooseOperator()
assignIsSuccessful && !operatorIsSuccessful -> chooseAssign()
assignIsSuccessful && operatorIsSuccessful && !operatorReturnTypeMatches -> chooseAssign()
else -> reportAmbiguity()
}
}
@@ -8,7 +8,7 @@ fun f(): Unit {
x <!UNSAFE_OPERATOR_CALL!>+<!> 1
x <!UNSAFE_INFIX_CALL!>plus<!> 1
x <!UNSAFE_OPERATOR_CALL!><<!> 1
x <!UNRESOLVED_REFERENCE!>+=<!> 1
x <!UNSAFE_OPERATOR_CALL!>+=<!> 1
x == 1
x != 1
@@ -1,17 +0,0 @@
class A {
operator fun plusAssign(s: String) {}
}
fun test() {
var a: A? = A()
a <!UNSAFE_OPERATOR_CALL!>+=<!> ""
}
class B {
operator fun plus(other: B) = this
}
fun test2() {
var b: B? = B()
b <!UNRESOLVED_REFERENCE!>+=<!> B()
}
@@ -1,3 +1,4 @@
// FIR_IDENTICAL
class A {
operator fun plusAssign(s: String) {}
}
@@ -1,14 +1,14 @@
fun intBinEq() {
var x = 0
x <!UNRESOLVED_REFERENCE!>+=<!> 'a'
x <!NONE_APPLICABLE!>+=<!> 'a'
x += 1.toByte()
x += 1.toShort()
<!ASSIGNMENT_TYPE_MISMATCH!>x += 1L<!>
<!ASSIGNMENT_TYPE_MISMATCH!>x += 1f<!>
<!ASSIGNMENT_TYPE_MISMATCH!>x += 1.0<!>
x <!UNRESOLVED_REFERENCE!>*=<!> 'a'
<!ASSIGNMENT_TYPE_MISMATCH!>x *= 1.toByte()<!>
<!ASSIGNMENT_TYPE_MISMATCH!>x *= 1.toShort()<!>
x <!NONE_APPLICABLE!>*=<!> 'a'
x *= 1.toByte()
x *= 1.toShort()
<!ASSIGNMENT_TYPE_MISMATCH!>x *= 1L<!>
<!ASSIGNMENT_TYPE_MISMATCH!>x *= 1f<!>
<!ASSIGNMENT_TYPE_MISMATCH!>x *= 1.0<!>
@@ -16,16 +16,16 @@ fun intBinEq() {
fun shortBinEq() {
var x = 0.toShort()
x <!UNRESOLVED_REFERENCE!>+=<!> 'a'
x <!NONE_APPLICABLE!>+=<!> 'a'
x += 1.toByte()
x += 1.toShort()
<!ASSIGNMENT_TYPE_MISMATCH!>x += 1L<!>
<!ASSIGNMENT_TYPE_MISMATCH!>x += 1f<!>
<!ASSIGNMENT_TYPE_MISMATCH!>x += 1.0<!>
x <!UNRESOLVED_REFERENCE!>*=<!> 'a'
<!ASSIGNMENT_TYPE_MISMATCH!>x *= 1.toByte()<!>
<!ASSIGNMENT_TYPE_MISMATCH!>x *= 1.toShort()<!>
x <!NONE_APPLICABLE!>*=<!> 'a'
x *= 1.toByte()
x *= 1.toShort()
<!ASSIGNMENT_TYPE_MISMATCH!>x *= 1L<!>
<!ASSIGNMENT_TYPE_MISMATCH!>x *= 1f<!>
<!ASSIGNMENT_TYPE_MISMATCH!>x *= 1.0<!>
@@ -7,7 +7,7 @@ fun foo(): String {
var t: String? = "y"
if (t == null) t = "x"
var x: Int? = null
if (x == null) x <!UNRESOLVED_REFERENCE!>+=<!> null
if (x == null) x <!UNSAFE_OPERATOR_CALL!>+=<!> null
return t + s
}
@@ -6,7 +6,7 @@
fun case_1() {
var x: Int? = 11
x!!
try {x = null;} finally { <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!> <!UNRESOLVED_REFERENCE!>+=<!> 10; }
try {x = null;} finally { <!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>x<!> <!UNSAFE_OPERATOR_CALL!>+=<!> 10; }
}
// TESTCASE NUMBER: 2
@@ -6,6 +6,4 @@ class A {
fun foo(b: A) {
var a: A? = A()
a <caret>+= b
}
/* IGNORE_FIR */
}
@@ -6,6 +6,4 @@ class A {
fun foo(b: A) {
var a: A? = A()
a = a?.plus(b)
}
/* IGNORE_FIR */
}