diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt index ad74fe4ea69..f9aa1c1960a 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/Fir2IrVisitor.kt @@ -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( diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/OperatorExpressionGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/OperatorExpressionGenerator.kt index 92ed1481687..d86a9ba4aa5 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/OperatorExpressionGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/OperatorExpressionGenerator.kt @@ -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 ): 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 ): 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 ): IrExpression { val origin = when (operation) { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/contracts/ConeConditionalEffectToFirVisitor.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/contracts/ConeConditionalEffectToFirVisitor.kt index 4d6e2b5468c..cbfd115b60a 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/contracts/ConeConditionalEffectToFirVisitor.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/dfa/contracts/ConeConditionalEffectToFirVisitor.kt @@ -64,7 +64,7 @@ private object ConeConditionalEffectToFirVisitor : ConeContractDescriptionVisito override fun visitIsNullPredicate(isNullPredicate: ConeIsNullPredicate, data: Map): FirExpression? { val argument = isNullPredicate.arg.accept(this, data) ?: return null - return buildOperatorCall { + return buildEqualityOperatorCall { operation = if (isNullPredicate.isNegated) { FirOperation.NOT_EQ } else { diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirWhenExhaustivenessTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirWhenExhaustivenessTransformer.kt index 2dd23a6d86a..52b42c856f9 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirWhenExhaustivenessTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/FirWhenExhaustivenessTransformer.kt @@ -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() { 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() { 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 diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/IntegerLiteralTypeApproximationTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/IntegerLiteralTypeApproximationTransformer.kt index 2fed3a6ffed..1338338f501 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/IntegerLiteralTypeApproximationTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/IntegerLiteralTypeApproximationTransformer.kt @@ -120,22 +120,6 @@ class IntegerLiteralTypeApproximationTransformer( } override fun transformOperatorCall(operatorCall: FirOperatorCall, data: ConeKotlinType?): CompositeTransformResult { - if (operatorCall.operation !in FirOperation.BOOLEANS) return operatorCall.compose() - val leftArgument = operatorCall.arguments[0] - val rightArgument = operatorCall.arguments[1] - - val leftIsIlt = leftArgument.typeRef.coneTypeSafe() != null - val rightIsIlt = rightArgument.typeRef.coneTypeSafe() != 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 { + val leftArgument = equalityOperatorCall.arguments[0] + val rightArgument = equalityOperatorCall.arguments[1] + + val leftIsIlt = leftArgument.typeRef.coneTypeSafe() != null + val rightIsIlt = rightArgument.typeRef.coneTypeSafe() != 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? diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirBodyResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirBodyResolveTransformer.kt index 10b269aeef1..c8f27a2a6d4 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirBodyResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirBodyResolveTransformer.kt @@ -140,6 +140,20 @@ open class FirBodyResolveTransformer( return expressionsTransformer.transformTypeOperatorCall(typeOperatorCall, data) } + override fun transformAssignmentOperatorStatement( + assignmentOperatorStatement: FirAssignmentOperatorStatement, + data: ResolutionMode + ): CompositeTransformResult { + return expressionsTransformer.transformAssignmentOperatorStatement(assignmentOperatorStatement, data) + } + + override fun transformEqualityOperatorCall( + equalityOperatorCall: FirEqualityOperatorCall, + data: ResolutionMode + ): CompositeTransformResult { + return expressionsTransformer.transformEqualityOperatorCall(equalityOperatorCall, data) + } + override fun transformCheckNotNullCall( checkNotNullCall: FirCheckNotNullCall, data: ResolutionMode diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt index 33dc75a28ad..88892df9e78 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/body/resolve/FirExpressionsResolveTransformer.kt @@ -334,99 +334,102 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform }.transformSingle(integerLiteralTypeApproximator, null).also(dataFlowAnalyzer::exitComparisonExpressionCall).compose() } - override fun transformOperatorCall(operatorCall: FirOperatorCall, data: ResolutionMode): CompositeTransformResult { - 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 { + 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 { + // 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 { throw IllegalArgumentException(operatorCall.render()) } diff --git a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/contracts/ConeEffectExtractor.kt b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/contracts/ConeEffectExtractor.kt index b8f2beb88f5..d043423a718 100644 --- a/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/contracts/ConeEffectExtractor.kt +++ b/compiler/fir/resolve/src/org/jetbrains/kotlin/fir/resolve/transformers/contracts/ConeEffectExtractor.kt @@ -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) }