[FIR] Replace all necessary usages of operator call with new ones

#KT-38333 Fixed
This commit is contained in:
Ivan Kylchik
2020-07-15 21:53:05 +03:00
parent def47bdd9d
commit 9a119a6757
8 changed files with 153 additions and 121 deletions
@@ -869,6 +869,10 @@ class Fir2IrVisitor(
}
}
override fun visitEqualityOperatorCall(equalityOperatorCall: FirEqualityOperatorCall, data: Any?): IrElement {
return operatorGenerator.convertEqualityOperatorCall(equalityOperatorCall)
}
override fun visitCheckNotNullCall(checkNotNullCall: FirCheckNotNullCall, data: Any?): IrElement {
return checkNotNullCall.convertWithOffsets { startOffset, endOffset ->
IrCallImpl(
@@ -35,8 +35,15 @@ internal class OperatorExpressionGenerator(
}
fun convertOperatorCall(operatorCall: FirOperatorCall): IrExpression {
return operatorCall.convertWithOffsets { startOffset, endOffset ->
generateOperatorCall(startOffset, endOffset, operatorCall.operation, operatorCall.arguments)
return when (val operation = operatorCall.operation) {
FirOperation.EXCL -> visitor.convertToIrExpression(operatorCall.arguments[0]).negate(IrStatementOrigin.EXCL)
else -> error("Unexpected operation: $operation")
}
}
fun convertEqualityOperatorCall(equalityOperatorCall: FirEqualityOperatorCall): IrExpression {
return equalityOperatorCall.convertWithOffsets { startOffset, endOffset ->
generateEqualityOperatorCall(startOffset, endOffset, equalityOperatorCall.operation, equalityOperatorCall.arguments)
}
}
@@ -84,16 +91,15 @@ internal class OperatorExpressionGenerator(
}
}
private fun generateOperatorCall(
private fun generateEqualityOperatorCall(
startOffset: Int, endOffset: Int, operation: FirOperation, arguments: List<FirExpression>
): IrExpression = when (operation) {
FirOperation.EQ, FirOperation.NOT_EQ -> generateEqualityOperatorCall(startOffset, endOffset, operation, arguments)
FirOperation.IDENTITY, FirOperation.NOT_IDENTITY -> generateIdentityOperatorCall(startOffset, endOffset, operation, arguments)
FirOperation.EXCL -> visitor.convertToIrExpression(arguments[0]).negate(IrStatementOrigin.EXCL)
FirOperation.EQ, FirOperation.NOT_EQ -> transformEqualityOperatorCall(startOffset, endOffset, operation, arguments)
FirOperation.IDENTITY, FirOperation.NOT_IDENTITY -> transformIdentityOperatorCall(startOffset, endOffset, operation, arguments)
else -> error("Unexpected operation: $operation")
}
private fun generateEqualityOperatorCall(
private fun transformEqualityOperatorCall(
startOffset: Int, endOffset: Int, operation: FirOperation, arguments: List<FirExpression>
): IrExpression {
val origin = when (operation) {
@@ -117,7 +123,7 @@ internal class OperatorExpressionGenerator(
}
}
private fun generateIdentityOperatorCall(
private fun transformIdentityOperatorCall(
startOffset: Int, endOffset: Int, operation: FirOperation, arguments: List<FirExpression>
): IrExpression {
val origin = when (operation) {
@@ -64,7 +64,7 @@ private object ConeConditionalEffectToFirVisitor : ConeContractDescriptionVisito
override fun visitIsNullPredicate(isNullPredicate: ConeIsNullPredicate, data: Map<Int, FirExpression>): FirExpression? {
val argument = isNullPredicate.arg.accept(this, data) ?: return null
return buildOperatorCall {
return buildEqualityOperatorCall {
operation = if (isNullPredicate.isNegated) {
FirOperation.NOT_EQ
} else {
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.FirElseIfTrueCondition
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
import org.jetbrains.kotlin.fir.resolve.firSymbolProvider
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
@@ -88,9 +87,9 @@ class FirWhenExhaustivenessTransformer(private val bodyResolveComponents: BodyRe
private object EnumExhaustivenessVisitor : FirVisitor<Unit, EnumExhaustivenessData>() {
override fun visitElement(element: FirElement, data: EnumExhaustivenessData) {}
override fun visitOperatorCall(operatorCall: FirOperatorCall, data: EnumExhaustivenessData) {
if (operatorCall.operation == FirOperation.EQ) {
when (val argument = operatorCall.arguments[1]) {
override fun visitEqualityOperatorCall(equalityOperatorCall: FirEqualityOperatorCall, data: EnumExhaustivenessData) {
if (equalityOperatorCall.operation.let { it == FirOperation.EQ || it == FirOperation.IDENTITY }) {
when (val argument = equalityOperatorCall.arguments[1]) {
is FirConstExpression<*> -> {
if (argument.value == null) {
data.containsNull = true
@@ -173,9 +172,9 @@ class FirWhenExhaustivenessTransformer(private val bodyResolveComponents: BodyRe
}
}
override fun visitOperatorCall(operatorCall: FirOperatorCall, data: SealedExhaustivenessData) {
if (operatorCall.operation == FirOperation.EQ) {
when (val argument = operatorCall.arguments[1]) {
override fun visitEqualityOperatorCall(equalityOperatorCall: FirEqualityOperatorCall, data: SealedExhaustivenessData) {
if (equalityOperatorCall.operation.let { it == FirOperation.EQ || it == FirOperation.IDENTITY }) {
when (val argument = equalityOperatorCall.arguments[1]) {
is FirConstExpression<*> -> {
if (argument.value == null) {
data.containsNull = true
@@ -220,9 +219,9 @@ class FirWhenExhaustivenessTransformer(private val bodyResolveComponents: BodyRe
private object BooleanExhaustivenessVisitor : FirVisitor<Unit, BooleanExhaustivenessFlags>() {
override fun visitElement(element: FirElement, data: BooleanExhaustivenessFlags) {}
override fun visitOperatorCall(operatorCall: FirOperatorCall, data: BooleanExhaustivenessFlags) {
if (operatorCall.operation == FirOperation.EQ) {
val argument = operatorCall.arguments[1]
override fun visitEqualityOperatorCall(equalityOperatorCall: FirEqualityOperatorCall, data: BooleanExhaustivenessFlags) {
if (equalityOperatorCall.operation.let { it == FirOperation.EQ || it == FirOperation.IDENTITY }) {
val argument = equalityOperatorCall.arguments[1]
if (argument is FirConstExpression<*>) {
when (argument.value) {
true -> data.containsTrue = true
@@ -120,22 +120,6 @@ class IntegerLiteralTypeApproximationTransformer(
}
override fun transformOperatorCall(operatorCall: FirOperatorCall, data: ConeKotlinType?): CompositeTransformResult<FirStatement> {
if (operatorCall.operation !in FirOperation.BOOLEANS) return operatorCall.compose()
val leftArgument = operatorCall.arguments[0]
val rightArgument = operatorCall.arguments[1]
val leftIsIlt = leftArgument.typeRef.coneTypeSafe<ConeIntegerLiteralType>() != null
val rightIsIlt = rightArgument.typeRef.coneTypeSafe<ConeIntegerLiteralType>() != null
val expectedType: ConeKotlinType? = when {
!leftIsIlt && !rightIsIlt -> return operatorCall.compose()
leftIsIlt && rightIsIlt -> null
leftIsIlt -> rightArgument.typeRef.coneType
rightIsIlt -> leftArgument.typeRef.coneType
else -> throw IllegalStateException()
}
operatorCall.argumentList.transformArguments(this, expectedType)
return operatorCall.compose()
}
@@ -148,6 +132,28 @@ class IntegerLiteralTypeApproximationTransformer(
return typeOperatorCall.compose()
}
override fun transformEqualityOperatorCall(
equalityOperatorCall: FirEqualityOperatorCall,
data: ConeKotlinType?
): CompositeTransformResult<FirStatement> {
val leftArgument = equalityOperatorCall.arguments[0]
val rightArgument = equalityOperatorCall.arguments[1]
val leftIsIlt = leftArgument.typeRef.coneTypeSafe<ConeIntegerLiteralType>() != null
val rightIsIlt = rightArgument.typeRef.coneTypeSafe<ConeIntegerLiteralType>() != null
val expectedType: ConeKotlinType? = when {
!leftIsIlt && !rightIsIlt -> return equalityOperatorCall.compose()
leftIsIlt && rightIsIlt -> null
leftIsIlt -> rightArgument.typeRef.coneType
rightIsIlt -> leftArgument.typeRef.coneType
else -> throw IllegalStateException()
}
equalityOperatorCall.argumentList.transformArguments(this, expectedType)
return equalityOperatorCall.compose()
}
override fun transformCheckedSafeCallSubject(
checkedSafeCallSubject: FirCheckedSafeCallSubject,
data: ConeKotlinType?
@@ -140,6 +140,20 @@ open class FirBodyResolveTransformer(
return expressionsTransformer.transformTypeOperatorCall(typeOperatorCall, data)
}
override fun transformAssignmentOperatorStatement(
assignmentOperatorStatement: FirAssignmentOperatorStatement,
data: ResolutionMode
): CompositeTransformResult<FirStatement> {
return expressionsTransformer.transformAssignmentOperatorStatement(assignmentOperatorStatement, data)
}
override fun transformEqualityOperatorCall(
equalityOperatorCall: FirEqualityOperatorCall,
data: ResolutionMode
): CompositeTransformResult<FirStatement> {
return expressionsTransformer.transformEqualityOperatorCall(equalityOperatorCall, data)
}
override fun transformCheckNotNullCall(
checkNotNullCall: FirCheckNotNullCall,
data: ResolutionMode
@@ -334,99 +334,102 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
}.transformSingle(integerLiteralTypeApproximator, null).also(dataFlowAnalyzer::exitComparisonExpressionCall).compose()
}
override fun transformOperatorCall(operatorCall: FirOperatorCall, data: ResolutionMode): CompositeTransformResult<FirStatement> {
if (operatorCall.operation in FirOperation.BOOLEANS) {
// TODO: add approximation of integer literals
val result = (operatorCall.transformChildren(transformer, ResolutionMode.ContextIndependent) as FirOperatorCall).also {
it.resultType = operatorCall.typeRef.resolvedTypeFromPrototype(builtinTypes.booleanType.type)
}.transformSingle(integerLiteralTypeApproximator, null)
dataFlowAnalyzer.exitOperatorCall(result)
return result.compose()
override fun transformAssignmentOperatorStatement(
assignmentOperatorStatement: FirAssignmentOperatorStatement,
data: ResolutionMode
): CompositeTransformResult<FirStatement> {
require(assignmentOperatorStatement.operation != FirOperation.ASSIGN)
assignmentOperatorStatement.annotations.forEach { it.accept(this, data) }
val leftArgument = assignmentOperatorStatement.leftArgument.transformSingle(transformer, ResolutionMode.ContextIndependent)
val rightArgument = assignmentOperatorStatement.rightArgument.transformSingle(transformer, ResolutionMode.ContextDependent)
fun createFunctionCall(name: Name) = buildFunctionCall {
source = assignmentOperatorStatement.source?.fakeElement(FirFakeSourceElementKind.DesugaredCompoundAssignment)
explicitReceiver = leftArgument
argumentList = buildUnaryArgumentList(rightArgument)
calleeReference = buildSimpleNamedReference {
source = assignmentOperatorStatement.source
this.name = name
candidateSymbol = null
}
}
if (operatorCall.operation in FirOperation.ASSIGNMENTS) {
require(operatorCall.operation != FirOperation.ASSIGN)
// x.plusAssign(y)
val assignmentOperatorName = FirOperationNameConventions.ASSIGNMENTS.getValue(assignmentOperatorStatement.operation)
val assignOperatorCall = createFunctionCall(assignmentOperatorName)
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(assignmentOperatorStatement.operation)
val simpleOperatorCall = createFunctionCall(simpleOperatorName)
val resolvedOperatorCall = resolveCandidateForAssignmentOperatorCall {
simpleOperatorCall.transformSingle(this, ResolutionMode.ContextDependent)
}
val operatorCallReference = resolvedOperatorCall.calleeReference as? FirNamedReferenceWithCandidate
val operatorIsError = operatorCallReference?.isError ?: true
operatorCall.annotations.forEach { it.accept(this, data) }
var (leftArgument, rightArgument) = operatorCall.arguments
@Suppress("NAME_SHADOWING")
leftArgument = leftArgument.transformSingle(transformer, ResolutionMode.ContextIndependent)
rightArgument = rightArgument.transformSingle(transformer, ResolutionMode.ContextDependent)
fun createFunctionCall(name: Name) = buildFunctionCall {
source = operatorCall.source?.fakeElement(FirFakeSourceElementKind.DesugaredCompoundAssignment)
explicitReceiver = leftArgument
argumentList = buildUnaryArgumentList(rightArgument)
calleeReference = buildSimpleNamedReference {
source = operatorCall.source
this.name = name
candidateSymbol = null
}
val lhsReference = leftArgument.toResolvedCallableReference()
val lhsVariable = (lhsReference?.resolvedSymbol as? FirVariableSymbol<*>)?.fir
val lhsIsVar = lhsVariable?.isVar == true
return when {
operatorIsError || (!lhsIsVar && !assignIsError) -> {
callCompleter.completeCall(resolvedAssignCall, noExpectedType)
dataFlowAnalyzer.exitFunctionCall(resolvedAssignCall, callCompleted = true)
resolvedAssignCall.compose()
}
// x.plusAssign(y)
val assignmentOperatorName = FirOperationNameConventions.ASSIGNMENTS.getValue(operatorCall.operation)
val assignOperatorCall = createFunctionCall(assignmentOperatorName)
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 = resolveCandidateForAssignmentOperatorCall {
simpleOperatorCall.transformSingle(this, ResolutionMode.ContextDependent)
}
val operatorCallReference = resolvedOperatorCall.calleeReference as? FirNamedReferenceWithCandidate
val operatorIsError = operatorCallReference?.isError ?: true
val lhsReference = leftArgument.toResolvedCallableReference()
val lhsVariable = (lhsReference?.resolvedSymbol as? FirVariableSymbol<*>)?.fir
val lhsIsVar = lhsVariable?.isVar == true
return when {
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
rValue = resolvedOperatorCall
calleeReference = if (lhsIsVar)
lhsReference!!
else
buildErrorNamedReference {
source = operatorCall.argument.source
diagnostic = ConeVariableExpectedError()
}
(leftArgument as? FirQualifiedAccess)?.let {
dispatchReceiver = it.dispatchReceiver
extensionReceiver = it.extensionReceiver
assignIsError -> {
callCompleter.completeCall(resolvedOperatorCall, lhsVariable?.returnTypeRef ?: noExpectedType)
dataFlowAnalyzer.exitFunctionCall(resolvedOperatorCall, callCompleted = true)
val assignment =
buildVariableAssignment {
source = assignmentOperatorStatement.source
rValue = resolvedOperatorCall
calleeReference = when {
lhsIsVar -> lhsReference!!
else -> buildErrorNamedReference {
source = assignmentOperatorStatement.leftArgument.source
diagnostic = ConeVariableExpectedError()
}
}
assignment.transform(transformer, ResolutionMode.ContextIndependent)
}
else -> {
val operatorCallSymbol = operatorCallReference?.candidateSymbol
val assignmentCallSymbol = assignCallReference?.candidateSymbol
(leftArgument as? FirQualifiedAccess)?.let {
dispatchReceiver = it.dispatchReceiver
extensionReceiver = it.extensionReceiver
}
}
assignment.transform(transformer, ResolutionMode.ContextIndependent)
}
else -> {
val operatorCallSymbol = operatorCallReference?.candidateSymbol
val assignmentCallSymbol = assignCallReference?.candidateSymbol
requireNotNull(operatorCallSymbol)
requireNotNull(assignmentCallSymbol)
requireNotNull(operatorCallSymbol)
requireNotNull(assignmentCallSymbol)
buildErrorExpression {
source = operatorCall.source
diagnostic =
ConeOperatorAmbiguityError(listOf(operatorCallSymbol, assignmentCallSymbol))
}.compose()
}
buildErrorExpression {
source = assignmentOperatorStatement.source
diagnostic = ConeOperatorAmbiguityError(listOf(operatorCallSymbol, assignmentCallSymbol))
}.compose()
}
}
}
override fun transformEqualityOperatorCall(
equalityOperatorCall: FirEqualityOperatorCall,
data: ResolutionMode
): CompositeTransformResult<FirStatement> {
// TODO: add approximation of integer literals
val result = (equalityOperatorCall.transformChildren(transformer, ResolutionMode.ContextIndependent) as FirEqualityOperatorCall)
.also { it.resultType = equalityOperatorCall.typeRef.resolvedTypeFromPrototype(builtinTypes.booleanType.type) }
.transformSingle(integerLiteralTypeApproximator, null)
dataFlowAnalyzer.exitEqualityOperatorCall(result)
return result.compose()
}
override fun transformOperatorCall(operatorCall: FirOperatorCall, data: ResolutionMode): CompositeTransformResult<FirStatement> {
throw IllegalArgumentException(operatorCall.render())
}
@@ -96,15 +96,15 @@ class ConeEffectExtractor(
return ConeBinaryLogicExpression(left, right, binaryLogicExpression.kind)
}
override fun visitOperatorCall(operatorCall: FirOperatorCall, data: Nothing?): ConeContractDescriptionElement? {
val isNegated = when (operatorCall.operation) {
override fun visitEqualityOperatorCall(equalityOperatorCall: FirEqualityOperatorCall, data: Nothing?): ConeContractDescriptionElement? {
val isNegated = when (equalityOperatorCall.operation) {
FirOperation.EQ -> false
FirOperation.NOT_EQ -> true
else -> return null
}
val const = operatorCall.arguments[1] as? FirConstExpression<*> ?: return null
val const = equalityOperatorCall.arguments[1] as? FirConstExpression<*> ?: return null
if (const.kind != FirConstKind.Null) return null
val arg = operatorCall.arguments[0].accept(this, null) as? ConeValueParameterReference ?: return null
val arg = equalityOperatorCall.arguments[0].accept(this, null) as? ConeValueParameterReference ?: return null
return ConeIsNullPredicate(arg, isNegated)
}