Handle equality checks for 'when' and data classes

This commit is contained in:
Dmitry Petrov
2018-02-06 17:51:12 +03:00
parent 299eb24ca9
commit 00325ae539
19 changed files with 631 additions and 89 deletions
@@ -40,6 +40,7 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue;
import org.jetbrains.kotlin.resolve.calls.smartcasts.ExplicitSmartCasts;
import org.jetbrains.kotlin.resolve.calls.smartcasts.ImplicitSmartCasts;
import org.jetbrains.kotlin.resolve.calls.tower.KotlinResolutionCallbacksImpl;
import org.jetbrains.kotlin.resolve.checkers.PrimitiveNumericComparisonInfo;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics;
import org.jetbrains.kotlin.resolve.scopes.LexicalScope;
@@ -267,8 +268,7 @@ public interface BindingContext {
WritableSlice<KtFunction, KotlinResolutionCallbacksImpl.LambdaInfo> NEW_INFERENCE_LAMBDA_INFO = new BasicWritableSlice<>(DO_NOTHING);
WritableSlice<KtBinaryExpression, KotlinType> PRIMITIVE_NUMERIC_COMPARISON_TYPE = Slices.createSimpleSlice();
WritableSlice<KtExpression, KotlinType> PRIMITIVE_NUMERIC_COMPARISON_OPERAND_TYPE = Slices.createSimpleSlice();
WritableSlice<KtExpression, PrimitiveNumericComparisonInfo> PRIMITIVE_NUMERIC_COMPARISON_INFO = Slices.createSimpleSlice();
@SuppressWarnings("UnusedDeclaration")
@Deprecated // This field is needed only for the side effects of its initializer
@@ -10,6 +10,7 @@ import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.KtBinaryExpression
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
@@ -17,6 +18,12 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.*
class PrimitiveNumericComparisonInfo(
val comparisonType: KotlinType,
val leftType: KotlinType,
val rightType: KotlinType
)
object PrimitiveNumericComparisonCallChecker : CallChecker {
private val comparisonOperatorTokens = setOf(KtTokens.EQEQ, KtTokens.EXCLEQ, KtTokens.LT, KtTokens.LTEQ, KtTokens.GT, KtTokens.GTEQ)
@@ -29,15 +36,27 @@ object PrimitiveNumericComparisonCallChecker : CallChecker {
val leftExpr = binaryExpression.left ?: return
val rightExpr = binaryExpression.right ?: return
val leftType = context.getInferredPrimitiveNumericType(leftExpr) ?: return
val rightType = context.getInferredPrimitiveNumericType(rightExpr) ?: return
val leftTypes = context.getStableTypesForExpression(leftExpr)
val rightTypes = context.getStableTypesForExpression(rightExpr)
context.trace.record(BindingContext.PRIMITIVE_NUMERIC_COMPARISON_OPERAND_TYPE, leftExpr, leftType)
context.trace.record(BindingContext.PRIMITIVE_NUMERIC_COMPARISON_OPERAND_TYPE, rightExpr, rightType)
inferPrimitiveNumericComparisonType(context.trace, leftTypes, rightTypes, binaryExpression)
}
val leastCommonType = leastCommonPrimitiveNumericType(leftType, rightType)
fun inferPrimitiveNumericComparisonType(
trace: BindingTrace,
leftTypes: List<KotlinType>,
rightTypes: List<KotlinType>,
comparison: KtExpression
) {
val leftPrimitiveType = leftTypes.findPrimitiveType() ?: return
val rightPrimitiveType = rightTypes.findPrimitiveType() ?: return
val leastCommonType = leastCommonPrimitiveNumericType(leftPrimitiveType, rightPrimitiveType)
context.trace.record(BindingContext.PRIMITIVE_NUMERIC_COMPARISON_TYPE, binaryExpression, leastCommonType)
trace.record(
BindingContext.PRIMITIVE_NUMERIC_COMPARISON_INFO,
comparison,
PrimitiveNumericComparisonInfo(leastCommonType, leftPrimitiveType, rightPrimitiveType)
)
}
private fun leastCommonPrimitiveNumericType(t1: KotlinType, t2: KotlinType): KotlinType {
@@ -60,14 +79,14 @@ object PrimitiveNumericComparisonCallChecker : CallChecker {
else -> this
}
private fun CallCheckerContext.getInferredPrimitiveNumericType(expression: KtExpression): KotlinType? {
val type = trace.bindingContext.getType(expression) ?: return null
private fun CallCheckerContext.getStableTypesForExpression(expression: KtExpression): List<KotlinType> {
val type = trace.bindingContext.getType(expression) ?: return emptyList()
val dataFlowValue = DataFlowValueFactory.createDataFlowValue(
expression, type, trace.bindingContext, resolutionContext.scope.ownerDescriptor
)
val dataFlowInfo = trace.get(BindingContext.EXPRESSION_TYPE_INFO, expression)?.dataFlowInfo ?: return null
val dataFlowInfo = trace.get(BindingContext.EXPRESSION_TYPE_INFO, expression)?.dataFlowInfo ?: return emptyList()
val stableTypes = dataFlowInfo.getStableTypes(dataFlowValue, languageVersionSettings)
return (listOf(type) + stableTypes).findPrimitiveType()
return listOf(type) + stableTypes
}
private fun List<KotlinType>.findPrimitiveType() =
@@ -34,6 +34,7 @@ import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
import org.jetbrains.kotlin.resolve.calls.util.CallMaker
import org.jetbrains.kotlin.resolve.checkers.PrimitiveNumericComparisonCallChecker
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
@@ -259,7 +260,8 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
if (checkSmartCastToExpectedTypeInSubject(
contextBeforeSubject, subjectExpression, subjectType,
possibleCastType
)) {
)
) {
return
}
}
@@ -271,7 +273,8 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
if (checkSmartCastToExpectedTypeInSubject(
contextBeforeSubject, subjectExpression, subjectType,
notNullableType
)) {
)
) {
return
}
}
@@ -384,7 +387,7 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
val expression = condition.expression
if (expression != null) {
val basicDataFlowInfo = checkTypeForExpressionCondition(
context, expression, subjectType, subjectExpression == null, subjectDataFlowValue
context, expression, subjectType, subjectExpression, subjectDataFlowValue
)
val moduleDescriptor = DescriptorUtils.getContainingModule(context.scope.ownerDescriptor)
val dataFlowInfoFromES =
@@ -404,14 +407,16 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
context: ExpressionTypingContext,
expression: KtExpression,
subjectType: KotlinType,
conditionExpected: Boolean,
subjectExpression: KtExpression?,
subjectDataFlowValue: DataFlowValue
): ConditionalDataFlowInfo {
var newContext = context
val typeInfo = facade.getTypeInfo(expression, newContext)
val type = typeInfo.type ?: return noChange(newContext)
newContext = newContext.replaceDataFlowInfo(typeInfo.dataFlowInfo)
if (conditionExpected) {
if (subjectExpression == null) { // condition expected
val booleanType = components.builtIns.booleanType
val checkedTypeInfo = components.dataFlowAnalyzer.checkType(typeInfo, expression, newContext.replaceExpectedType(booleanType))
if (KotlinTypeChecker.DEFAULT.equalTypes(booleanType, checkedTypeInfo.type ?: type)) {
@@ -421,8 +426,21 @@ class PatternMatchingTypingVisitor internal constructor(facade: ExpressionTyping
}
return noChange(newContext)
}
checkTypeCompatibility(newContext, type, subjectType, expression)
val expressionDataFlowValue = DataFlowValueFactory.createDataFlowValue(expression, type, newContext)
val subjectStableTypes =
listOf(subjectType) + context.dataFlowInfo.getStableTypes(subjectDataFlowValue, components.languageVersionSettings)
val expressionStableTypes =
listOf(type) + newContext.dataFlowInfo.getStableTypes(expressionDataFlowValue, components.languageVersionSettings)
PrimitiveNumericComparisonCallChecker.inferPrimitiveNumericComparisonType(
context.trace,
subjectStableTypes,
expressionStableTypes,
expression
)
val result = noChange(newContext)
return ConditionalDataFlowInfo(
result.thenInfo.equate(
@@ -37,7 +37,7 @@ import org.jetbrains.kotlin.types.KotlinType
class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGeneratorExtension(statementGenerator) {
fun generateAssignment(expression: KtBinaryExpression): IrExpression {
val ktLeft = expression.left!!
val irRhs = statementGenerator.generateExpression(expression.right!!)
val irRhs = expression.right!!.genExpr()
val irAssignmentReceiver = generateAssignmentReceiver(ktLeft, IrStatementOrigin.EQ)
return irAssignmentReceiver.assign(irRhs)
}
@@ -52,7 +52,7 @@ class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGen
return irAssignmentReceiver.assign { irLValue ->
val opCall = statementGenerator.pregenerateCallReceivers(opResolvedCall)
opCall.setExplicitReceiverValue(irLValue)
opCall.irValueArgumentsByIndex[0] = statementGenerator.generateExpression(ktRight)
opCall.irValueArgumentsByIndex[0] = ktRight.genExpr()
val irOpCall = CallGenerator(statementGenerator).generateCall(expression, opCall, origin)
if (isSimpleAssignment) {
@@ -137,7 +137,7 @@ class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGen
origin
)
else ->
OnceExpressionValue(statementGenerator.generateExpression(ktLeft))
OnceExpressionValue(ktLeft.genExpr())
}
}
@@ -237,8 +237,8 @@ class AssignmentGenerator(statementGenerator: StatementGenerator) : StatementGen
ktLeft: KtArrayAccessExpression,
origin: IrStatementOrigin
): ArrayAccessAssignmentReceiver {
val irArray = statementGenerator.generateExpression(ktLeft.arrayExpression!!)
val irIndexExpressions = ktLeft.indexExpressions.map { statementGenerator.generateExpression(it) }
val irArray = ktLeft.arrayExpression!!.genExpr()
val irIndexExpressions = ktLeft.indexExpressions.map { it.genExpr() }
val indexedGetResolvedCall = get(BindingContext.INDEXED_LVALUE_GET, ktLeft)
val indexedGetCall = indexedGetResolvedCall?.let { statementGenerator.pregenerateCallReceivers(it) }
@@ -40,8 +40,8 @@ class BranchingExpressionGenerator(statementGenerator: StatementGenerator) : Sta
var irElseBranch: IrExpression? = null
whenBranches@ while (true) {
val irCondition = statementGenerator.generateExpression(ktLastIf.condition!!)
val irThenBranch = statementGenerator.generateExpression(ktLastIf.then!!)
val irCondition = ktLastIf.condition!!.genExpr()
val irThenBranch = ktLastIf.then!!.genExpr()
irBranches.add(IrBranchImpl(irCondition, irThenBranch))
val ktElse = ktLastIf.`else`?.deparenthesize()
@@ -49,7 +49,7 @@ class BranchingExpressionGenerator(statementGenerator: StatementGenerator) : Sta
null -> break@whenBranches
is KtIfExpression -> ktLastIf = ktElse
is KtExpression -> {
irElseBranch = statementGenerator.generateExpression(ktElse)
irElseBranch = ktElse.genExpr()
break@whenBranches
}
else -> throw AssertionError("Unexpected else expression: ${ktElse.text}")
@@ -85,7 +85,7 @@ class BranchingExpressionGenerator(statementGenerator: StatementGenerator) : Sta
fun generateWhenExpression(expression: KtWhenExpression): IrExpression {
val irSubject = expression.subjectExpression?.let {
scope.createTemporaryVariable(statementGenerator.generateExpression(it), "subject")
scope.createTemporaryVariable(it.genExpr(), "subject")
}
@@ -104,7 +104,7 @@ class BranchingExpressionGenerator(statementGenerator: StatementGenerator) : Sta
for (ktEntry in expression.entries) {
if (ktEntry.isElse) {
val irElseResult = statementGenerator.generateExpression(ktEntry.expression!!)
val irElseResult = ktEntry.expression!!.genExpr()
irWhen.branches.add(IrBranchImpl.elseBranch(irElseResult))
break
}
@@ -117,10 +117,9 @@ class BranchingExpressionGenerator(statementGenerator: StatementGenerator) : Sta
else
generateWhenConditionNoSubject(ktCondition)
irBranchCondition = irBranchCondition?.let { context.whenComma(it, irCondition) } ?: irCondition
}
val irBranchResult = statementGenerator.generateExpression(ktEntry.expression!!)
val irBranchResult = ktEntry.expression!!.genExpr()
irWhen.branches.add(IrBranchImpl(irBranchCondition!!, irBranchResult))
}
addElseBranchForExhaustiveWhenIfNeeded(irWhen, expression)
@@ -163,7 +162,7 @@ class BranchingExpressionGenerator(statementGenerator: StatementGenerator) : Sta
}
private fun generateWhenConditionNoSubject(ktCondition: KtWhenCondition): IrExpression =
statementGenerator.generateExpression((ktCondition as KtWhenConditionWithExpression).expression!!)
(ktCondition as KtWhenConditionWithExpression).expression!!.genExpr()
private fun generateWhenConditionWithSubject(ktCondition: KtWhenCondition, irSubject: IrVariable): IrExpression {
return when (ktCondition) {
@@ -204,10 +203,13 @@ class BranchingExpressionGenerator(statementGenerator: StatementGenerator) : Sta
}
}
private fun generateEqualsCondition(irSubject: IrVariable, ktCondition: KtWhenConditionWithExpression): IrBinaryPrimitiveImpl =
IrBinaryPrimitiveImpl(
ktCondition.startOffset, ktCondition.endOffset,
IrStatementOrigin.EQEQ, context.irBuiltIns.eqeqSymbol,
irSubject.defaultLoad(), statementGenerator.generateExpression(ktCondition.expression!!)
private fun generateEqualsCondition(irSubject: IrVariable, ktCondition: KtWhenConditionWithExpression): IrExpression {
val ktExpression = ktCondition.expression
val irExpression = ktExpression!!.genExpr()
return OperatorExpressionGenerator(statementGenerator).generateEquality(
ktCondition.startOffset, ktCondition.endOffset, IrStatementOrigin.EQEQ,
irSubject.defaultLoad(), irExpression,
context.bindingContext[BindingContext.PRIMITIVE_NUMERIC_COMPARISON_INFO, ktExpression]
)
}
}
@@ -137,12 +137,9 @@ class DataClassMembersGenerator(declarationGenerator: DeclarationGenerator) : De
+irIfThenReturnFalse(irNotIs(irOther(), classDescriptor.defaultType))
val otherWithCast = irTemporary(irAs(irOther(), classDescriptor.defaultType), "other_with_cast")
for (property in properties) {
+irIfThenReturnFalse(
irNotEquals(
irGet(irThis(), getPropertyGetterSymbol(property)),
irGet(irGet(otherWithCast.symbol), getPropertyGetterSymbol(property))
)
)
val arg1 = irGet(irThis(), getPropertyGetterSymbol(property))
val arg2 = irGet(irGet(otherWithCast.symbol), getPropertyGetterSymbol(property))
+irIfThenReturnFalse(irNotEquals(arg1, arg2))
}
+irReturnTrue()
}
@@ -229,7 +226,8 @@ class DataClassMembersGenerator(declarationGenerator: DeclarationGenerator) : De
val typeConstructorDescriptor = property.type.constructor.declarationDescriptor
val irPropertyStringValue =
if (typeConstructorDescriptor is ClassDescriptor &&
KotlinBuiltIns.isArrayOrPrimitiveArray(typeConstructorDescriptor))
KotlinBuiltIns.isArrayOrPrimitiveArray(typeConstructorDescriptor)
)
irCall(context.irBuiltIns.dataClassArrayMemberToStringSymbol).apply {
putValueArgument(0, irPropertyValue)
}
@@ -46,19 +46,19 @@ class ErrorExpressionGenerator(statementGenerator: StatementGenerator) : Stateme
val type = getErrorExpressionType(ktCall)
val irErrorCall = IrErrorCallExpressionImpl(ktCall.startOffset, ktCall.endOffset, type, "") // TODO problem description?
irErrorCall.explicitReceiver = (ktCall.parent as? KtDotQualifiedExpression)?.let {
statementGenerator.generateExpression(it.receiverExpression)
irErrorCall.explicitReceiver = (ktCall.parent as? KtDotQualifiedExpression)?.run {
receiverExpression.genExpr()
}
ktCall.valueArguments.forEach {
val ktArgument = it.getArgumentExpression()
if (ktArgument != null) {
irErrorCall.addArgument(statementGenerator.generateExpression(ktArgument))
irErrorCall.addArgument(ktArgument.genExpr())
}
}
ktCall.lambdaArguments.forEach {
irErrorCall.addArgument(statementGenerator.generateExpression(it.getArgumentExpression()))
irErrorCall.addArgument(it.getArgumentExpression().genExpr())
}
irErrorCall
@@ -73,7 +73,7 @@ class ErrorExpressionGenerator(statementGenerator: StatementGenerator) : Stateme
val irErrorCall = IrErrorCallExpressionImpl(ktName.startOffset, ktName.endOffset, type, "") // TODO problem description?
irErrorCall.explicitReceiver = (ktName.parent as? KtDotQualifiedExpression)?.let { ktParent ->
if (ktParent.receiverExpression == ktName) null
else statementGenerator.generateExpression(ktParent.receiverExpression)
else ktParent.receiverExpression.genExpr()
}
irErrorCall
@@ -36,7 +36,7 @@ class LoopExpressionGenerator(statementGenerator: StatementGenerator) : Statemen
context.builtIns.unitType, IrStatementOrigin.WHILE_LOOP
)
irLoop.condition = statementGenerator.generateExpression(ktWhile.condition!!)
irLoop.condition = ktWhile.condition!!.genExpr()
statementGenerator.bodyGenerator.putLoop(ktWhile, irLoop)
@@ -44,7 +44,7 @@ class LoopExpressionGenerator(statementGenerator: StatementGenerator) : Statemen
if (ktLoopBody is KtBlockExpression)
generateWhileLoopBody(ktLoopBody)
else
statementGenerator.generateExpression(ktLoopBody)
ktLoopBody.genExpr()
}
irLoop.label = getLoopLabel(ktWhile)
@@ -64,10 +64,10 @@ class LoopExpressionGenerator(statementGenerator: StatementGenerator) : Statemen
if (ktLoopBody is KtBlockExpression)
generateDoWhileLoopBody(ktLoopBody)
else
statementGenerator.generateExpression(ktLoopBody)
ktLoopBody.genExpr()
}
irLoop.condition = statementGenerator.generateExpression(ktDoWhile.condition!!)
irLoop.condition = ktDoWhile.condition!!.genExpr()
irLoop.label = getLoopLabel(ktDoWhile)
@@ -79,14 +79,14 @@ class LoopExpressionGenerator(statementGenerator: StatementGenerator) : Statemen
private fun generateWhileLoopBody(ktLoopBody: KtBlockExpression): IrExpression =
IrBlockImpl(
ktLoopBody.startOffset, ktLoopBody.endOffset, context.builtIns.unitType, null,
ktLoopBody.statements.map { statementGenerator.generateStatement(it) }
ktLoopBody.statements.map { it.genStmt() }
)
private fun generateDoWhileLoopBody(ktLoopBody: KtBlockExpression): IrExpression =
IrCompositeImpl(
ktLoopBody.startOffset, ktLoopBody.endOffset, context.builtIns.unitType, null,
ktLoopBody.statements.map { statementGenerator.generateStatement(it) }
ktLoopBody.statements.map { it.genStmt() }
)
fun generateBreak(ktBreak: KtBreakExpression): IrExpression {
@@ -199,7 +199,7 @@ class LoopExpressionGenerator(statementGenerator: StatementGenerator) : Statemen
}
if (ktForBody != null) {
irInnerBody.statements.add(statementGenerator.generateExpression(ktForBody))
irInnerBody.statements.add(ktForBody.genExpr())
}
return irForBlock
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.psi2ir.findSingleFunction
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.checkers.PrimitiveNumericComparisonInfo
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberType
@@ -89,7 +90,7 @@ class OperatorExpressionGenerator(statementGenerator: StatementGenerator) : Stat
return IrTypeOperatorCallImpl(
expression.startOffset, expression.endOffset, resultType, irOperator, rhsType,
statementGenerator.generateExpression(expression.left)
expression.left.genExpr()
)
}
@@ -100,7 +101,7 @@ class OperatorExpressionGenerator(statementGenerator: StatementGenerator) : Stat
return IrTypeOperatorCallImpl(
expression.startOffset, expression.endOffset, context.builtIns.booleanType, irOperator,
againstType, statementGenerator.generateExpression(expression.leftHandSide)
againstType, expression.leftHandSide.genExpr()
)
}
@@ -130,8 +131,8 @@ class OperatorExpressionGenerator(statementGenerator: StatementGenerator) : Stat
private fun generateElvis(expression: KtBinaryExpression): IrExpression {
val specialCallForElvis = getResolvedCall(expression)!!
val resultType = specialCallForElvis.resultingDescriptor.returnType!!
val irArgument0 = statementGenerator.generateExpression(expression.left!!)
val irArgument1 = statementGenerator.generateExpression(expression.right!!)
val irArgument0 = expression.left!!.genExpr()
val irArgument1 = expression.right!!.genExpr()
return irBlock(expression, IrStatementOrigin.ELVIS, resultType) {
val temporary = irTemporary(irArgument0, "elvis_lhs")
@@ -140,8 +141,8 @@ class OperatorExpressionGenerator(statementGenerator: StatementGenerator) : Stat
}
private fun generateBinaryBooleanOperator(expression: KtBinaryExpression, irOperator: IrStatementOrigin): IrExpression {
val irArgument0 = statementGenerator.generateExpression(expression.left!!)
val irArgument1 = statementGenerator.generateExpression(expression.right!!)
val irArgument0 = expression.left!!.genExpr()
val irArgument1 = expression.right!!.genExpr()
return when (irOperator) {
IrStatementOrigin.OROR ->
context.oror(expression.startOffset, expression.endOffset, irArgument0, irArgument1)
@@ -173,8 +174,8 @@ class OperatorExpressionGenerator(statementGenerator: StatementGenerator) : Stat
}
private fun generateIdentityOperator(expression: KtBinaryExpression, irOperator: IrStatementOrigin): IrExpression {
val irArgument0 = statementGenerator.generateExpression(expression.left!!)
val irArgument1 = statementGenerator.generateExpression(expression.right!!)
val irArgument0 = expression.left!!.genExpr()
val irArgument1 = expression.right!!.genExpr()
val irIdentityEquals = IrBinaryPrimitiveImpl(
expression.startOffset, expression.endOffset, irOperator,
@@ -196,31 +197,27 @@ class OperatorExpressionGenerator(statementGenerator: StatementGenerator) : Stat
}
}
private fun KtExpression.generateAsPrimitiveNumericComparisonOperand(primitiveNumericComparisonType: KotlinType?) =
statementGenerator.generateExpression(this)
.promoteToPrimitiveNumericType(
getPrimitiveNumericComparisonOperandType(this),
primitiveNumericComparisonType
)
private fun KtExpression.generateAsPrimitiveNumericComparisonOperand(
expressionType: KotlinType?,
comparisonType: KotlinType?
) = genExpr().promoteToPrimitiveNumericType(expressionType, comparisonType)
private fun getPrimitiveNumericComparisonType(ktExpression: KtBinaryExpression) =
context.bindingContext[BindingContext.PRIMITIVE_NUMERIC_COMPARISON_TYPE, ktExpression]
private fun getPrimitiveNumericComparisonOperandType(ktExpression: KtExpression) =
context.bindingContext[BindingContext.PRIMITIVE_NUMERIC_COMPARISON_OPERAND_TYPE, ktExpression]
private fun getPrimitiveNumericComparisonInfo(ktExpression: KtBinaryExpression) =
context.bindingContext[BindingContext.PRIMITIVE_NUMERIC_COMPARISON_INFO, ktExpression]
private fun generateEqualityOperator(expression: KtBinaryExpression, irOperator: IrStatementOrigin): IrExpression {
val primitiveNumericComparisonType = getPrimitiveNumericComparisonType(expression)
val comparisonInfo = getPrimitiveNumericComparisonInfo(expression)
val comparisonType = comparisonInfo?.comparisonType
val eqeqSymbol = context.irBuiltIns.ieee754equalsFunByOperandType[primitiveNumericComparisonType]?.symbol
val eqeqSymbol = context.irBuiltIns.ieee754equalsFunByOperandType[comparisonType]?.symbol
?: context.irBuiltIns.eqeqSymbol
val irEquals = IrBinaryPrimitiveImpl(
expression.startOffset, expression.endOffset,
irOperator,
eqeqSymbol,
expression.left!!.generateAsPrimitiveNumericComparisonOperand(primitiveNumericComparisonType),
expression.right!!.generateAsPrimitiveNumericComparisonOperand(primitiveNumericComparisonType)
expression.left!!.generateAsPrimitiveNumericComparisonOperand(comparisonInfo?.leftType, comparisonType),
expression.right!!.generateAsPrimitiveNumericComparisonOperand(comparisonInfo?.rightType, comparisonType)
)
return when (irOperator) {
@@ -238,6 +235,33 @@ class OperatorExpressionGenerator(statementGenerator: StatementGenerator) : Stat
}
}
fun generateEquality(
startOffset: Int,
endOffset: Int,
irOperator: IrStatementOrigin,
arg1: IrExpression,
arg2: IrExpression,
comparisonInfo: PrimitiveNumericComparisonInfo?
): IrExpression =
if (comparisonInfo != null) {
val comparisonType = comparisonInfo.comparisonType
val eqeqSymbol =
context.irBuiltIns.ieee754equalsFunByOperandType[comparisonType]?.symbol
?: context.irBuiltIns.eqeqSymbol
IrBinaryPrimitiveImpl(
startOffset, endOffset, irOperator,
eqeqSymbol,
arg1.promoteToPrimitiveNumericType(comparisonInfo.leftType, comparisonType),
arg2.promoteToPrimitiveNumericType(comparisonInfo.rightType, comparisonType)
)
} else {
IrBinaryPrimitiveImpl(
startOffset, endOffset, irOperator,
context.irBuiltIns.eqeqSymbol,
arg1, arg2
)
}
private fun IrExpression.promoteToPrimitiveNumericType(operandType: KotlinType?, targetType: KotlinType?): IrExpression {
if (targetType == null) return this
if (operandType == null) throw AssertionError("operandType should be non-null")
@@ -290,14 +314,14 @@ class OperatorExpressionGenerator(statementGenerator: StatementGenerator) : Stat
val startOffset = expression.startOffset
val endOffset = expression.endOffset
val primitiveNumberComparisonType = getPrimitiveNumericComparisonType(expression)
val comparisonInfo = getPrimitiveNumericComparisonInfo(expression)
return if (primitiveNumberComparisonType != null) {
return if (comparisonInfo != null) {
IrBinaryPrimitiveImpl(
startOffset, endOffset, origin,
getComparisonOperatorSymbol(origin, primitiveNumberComparisonType),
expression.left!!.generateAsPrimitiveNumericComparisonOperand(primitiveNumberComparisonType),
expression.right!!.generateAsPrimitiveNumericComparisonOperand(primitiveNumberComparisonType)
getComparisonOperatorSymbol(origin, comparisonInfo.comparisonType),
expression.left!!.generateAsPrimitiveNumericComparisonOperand(comparisonInfo.leftType, comparisonInfo.comparisonType),
expression.right!!.generateAsPrimitiveNumericComparisonOperand(comparisonInfo.rightType, comparisonInfo.comparisonType)
)
} else {
IrBinaryPrimitiveImpl(
@@ -327,7 +351,7 @@ class OperatorExpressionGenerator(statementGenerator: StatementGenerator) : Stat
private fun generateExclExclOperator(expression: KtPostfixExpression, origin: IrStatementOrigin): IrExpression {
val ktArgument = expression.baseExpression!!
val irArgument = statementGenerator.generateExpression(ktArgument)
val irArgument = ktArgument.genExpr()
val ktOperator = expression.operationReference
val resultType = irArgument.type.makeNotNullable()
@@ -39,7 +39,7 @@ class ReflectionReferencesGenerator(statementGenerator: StatementGenerator) : St
return if (lhs is DoubleColonLHS.Expression && !lhs.isObjectQualifier) {
IrGetClassImpl(
ktClassLiteral.startOffset, ktClassLiteral.endOffset, resultType,
statementGenerator.generateExpression(ktArgument)
ktArgument.genExpr()
)
} else {
val typeConstructorDeclaration = lhs.type.constructor.declarationDescriptor
@@ -407,4 +407,7 @@ class StatementGenerator(
abstract class StatementGeneratorExtension(val statementGenerator: StatementGenerator) : GeneratorWithScope {
override val scope: Scope get() = statementGenerator.scope
override val context: GeneratorContext get() = statementGenerator.context
fun KtExpression.genExpr() = statementGenerator.generateExpression(this)
fun KtExpression.genStmt() = statementGenerator.generateStatement(this)
}
@@ -30,7 +30,7 @@ class TryCatchExpressionGenerator(statementGenerator: StatementGenerator) : Stat
val resultType = getInferredTypeWithImplicitCastsOrFail(ktTry)
val irTryCatch = IrTryImpl(ktTry.startOffset, ktTry.endOffset, resultType)
irTryCatch.tryResult = statementGenerator.generateExpression(ktTry.tryBlock)
irTryCatch.tryResult = ktTry.tryBlock.genExpr()
for (ktCatchClause in ktTry.catchClauses) {
val ktCatchParameter = ktCatchClause.catchParameter!!
@@ -45,13 +45,13 @@ class TryCatchExpressionGenerator(statementGenerator: StatementGenerator) : Stat
catchParameterDescriptor
)
).apply {
result = statementGenerator.generateExpression(ktCatchBody)
result = ktCatchBody.genExpr()
}
irTryCatch.catches.add(irCatch)
}
irTryCatch.finallyExpression = ktTry.finallyBlock?.let { statementGenerator.generateExpression(it.finalExpression) }
irTryCatch.finallyExpression = ktTry.finallyBlock?.run { finalExpression.genExpr() }
return irTryCatch
}
+3 -1
View File
@@ -1,3 +1,5 @@
data class Test1(val x: Int, val y: String, val z: Any)
data class Test2(val x: Any?)
data class Test2(val x: Any?)
data class Test3(val d: Double, val dn: Double?, val f: Float, val df: Float?)
+240 -1
View File
@@ -270,4 +270,243 @@ FILE fqName:<root> fileName:/dataClasses.kt
CONST Boolean type=kotlin.Boolean value=false
RETURN type=kotlin.Nothing from='equals(Any?): Boolean'
CONST Boolean type=kotlin.Boolean value=true
CLASS CLASS name:Test3 modality:FINAL visibility:public flags:data
$this: VALUE_PARAMETER INSTANCE_RECEIVER name:<this> type:Test3 flags:
CONSTRUCTOR visibility:public <> (d:kotlin.Double, dn:kotlin.Double?, f:kotlin.Float, df:kotlin.Float?) returnType:Test3 flags:
VALUE_PARAMETER name:d index:0 type:kotlin.Double flags:
VALUE_PARAMETER name:dn index:1 type:kotlin.Double? flags:
VALUE_PARAMETER name:f index:2 type:kotlin.Float flags:
VALUE_PARAMETER name:df index:3 type:kotlin.Float? flags:
BLOCK_BODY
DELEGATING_CONSTRUCTOR_CALL 'constructor Any()'
INSTANCE_INITIALIZER_CALL classDescriptor='Test3'
PROPERTY name:d type:kotlin.Double visibility:public modality:FINAL flags:val
FIELD PROPERTY_BACKING_FIELD name:d type:kotlin.Double visibility:public
EXPRESSION_BODY
GET_VAR 'value-parameter d: Double' type=kotlin.Double origin=INITIALIZE_PROPERTY_FROM_PARAMETER
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-d> visibility:public modality:FINAL <> ($this:Test3) returnType:Double flags:
$this: VALUE_PARAMETER name:<this> type:Test3 flags:
BLOCK_BODY
RETURN type=kotlin.Nothing from='<get-d>(): Double'
GET_FIELD 'd: Double' type=kotlin.Double origin=null
receiver: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
PROPERTY name:dn type:kotlin.Double? visibility:public modality:FINAL flags:val
FIELD PROPERTY_BACKING_FIELD name:dn type:kotlin.Double? visibility:public
EXPRESSION_BODY
GET_VAR 'value-parameter dn: Double?' type=kotlin.Double? origin=INITIALIZE_PROPERTY_FROM_PARAMETER
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-dn> visibility:public modality:FINAL <> ($this:Test3) returnType:Double? flags:
$this: VALUE_PARAMETER name:<this> type:Test3 flags:
BLOCK_BODY
RETURN type=kotlin.Nothing from='<get-dn>(): Double?'
GET_FIELD 'dn: Double?' type=kotlin.Double? origin=null
receiver: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
PROPERTY name:f type:kotlin.Float visibility:public modality:FINAL flags:val
FIELD PROPERTY_BACKING_FIELD name:f type:kotlin.Float visibility:public
EXPRESSION_BODY
GET_VAR 'value-parameter f: Float' type=kotlin.Float origin=INITIALIZE_PROPERTY_FROM_PARAMETER
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-f> visibility:public modality:FINAL <> ($this:Test3) returnType:Float flags:
$this: VALUE_PARAMETER name:<this> type:Test3 flags:
BLOCK_BODY
RETURN type=kotlin.Nothing from='<get-f>(): Float'
GET_FIELD 'f: Float' type=kotlin.Float origin=null
receiver: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
PROPERTY name:df type:kotlin.Float? visibility:public modality:FINAL flags:val
FIELD PROPERTY_BACKING_FIELD name:df type:kotlin.Float? visibility:public
EXPRESSION_BODY
GET_VAR 'value-parameter df: Float?' type=kotlin.Float? origin=INITIALIZE_PROPERTY_FROM_PARAMETER
FUN DEFAULT_PROPERTY_ACCESSOR name:<get-df> visibility:public modality:FINAL <> ($this:Test3) returnType:Float? flags:
$this: VALUE_PARAMETER name:<this> type:Test3 flags:
BLOCK_BODY
RETURN type=kotlin.Nothing from='<get-df>(): Float?'
GET_FIELD 'df: Float?' type=kotlin.Float? origin=null
receiver: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
FUN GENERATED_DATA_CLASS_MEMBER name:component1 visibility:public modality:FINAL <> ($this:Test3) returnType:Double flags:
$this: VALUE_PARAMETER name:<this> type:Test3 flags:
BLOCK_BODY
RETURN type=kotlin.Nothing from='component1(): Double'
CALL '<get-d>(): Double' type=kotlin.Double origin=GET_PROPERTY
$this: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
FUN GENERATED_DATA_CLASS_MEMBER name:component2 visibility:public modality:FINAL <> ($this:Test3) returnType:Double? flags:
$this: VALUE_PARAMETER name:<this> type:Test3 flags:
BLOCK_BODY
RETURN type=kotlin.Nothing from='component2(): Double?'
CALL '<get-dn>(): Double?' type=kotlin.Double? origin=GET_PROPERTY
$this: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
FUN GENERATED_DATA_CLASS_MEMBER name:component3 visibility:public modality:FINAL <> ($this:Test3) returnType:Float flags:
$this: VALUE_PARAMETER name:<this> type:Test3 flags:
BLOCK_BODY
RETURN type=kotlin.Nothing from='component3(): Float'
CALL '<get-f>(): Float' type=kotlin.Float origin=GET_PROPERTY
$this: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
FUN GENERATED_DATA_CLASS_MEMBER name:component4 visibility:public modality:FINAL <> ($this:Test3) returnType:Float? flags:
$this: VALUE_PARAMETER name:<this> type:Test3 flags:
BLOCK_BODY
RETURN type=kotlin.Nothing from='component4(): Float?'
CALL '<get-df>(): Float?' type=kotlin.Float? origin=GET_PROPERTY
$this: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
FUN GENERATED_DATA_CLASS_MEMBER name:copy visibility:public modality:FINAL <> ($this:Test3, d:kotlin.Double, dn:kotlin.Double?, f:kotlin.Float, df:kotlin.Float?) returnType:Test3 flags:
$this: VALUE_PARAMETER name:<this> type:Test3 flags:
VALUE_PARAMETER name:d index:0 type:kotlin.Double flags:
EXPRESSION_BODY
CALL '<get-d>(): Double' type=kotlin.Double origin=GET_PROPERTY
$this: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
VALUE_PARAMETER name:dn index:1 type:kotlin.Double? flags:
EXPRESSION_BODY
CALL '<get-dn>(): Double?' type=kotlin.Double? origin=GET_PROPERTY
$this: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
VALUE_PARAMETER name:f index:2 type:kotlin.Float flags:
EXPRESSION_BODY
CALL '<get-f>(): Float' type=kotlin.Float origin=GET_PROPERTY
$this: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
VALUE_PARAMETER name:df index:3 type:kotlin.Float? flags:
EXPRESSION_BODY
CALL '<get-df>(): Float?' type=kotlin.Float? origin=GET_PROPERTY
$this: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
BLOCK_BODY
RETURN type=kotlin.Nothing from='copy(Double = ..., Double? = ..., Float = ..., Float? = ...): Test3'
CALL 'constructor Test3(Double, Double?, Float, Float?)' type=Test3 origin=null
d: GET_VAR 'value-parameter d: Double = ...' type=kotlin.Double origin=null
dn: GET_VAR 'value-parameter dn: Double? = ...' type=kotlin.Double? origin=null
f: GET_VAR 'value-parameter f: Float = ...' type=kotlin.Float origin=null
df: GET_VAR 'value-parameter df: Float? = ...' type=kotlin.Float? origin=null
FUN GENERATED_DATA_CLASS_MEMBER name:toString visibility:public modality:OPEN <> ($this:Test3) returnType:String flags:
$this: VALUE_PARAMETER name:<this> type:Test3 flags:
BLOCK_BODY
RETURN type=kotlin.Nothing from='toString(): String'
STRING_CONCATENATION type=kotlin.String
CONST String type=kotlin.String value=Test3(
CONST String type=kotlin.String value=d=
CALL '<get-d>(): Double' type=kotlin.Double origin=GET_PROPERTY
$this: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
CONST String type=kotlin.String value=,
CONST String type=kotlin.String value=dn=
CALL '<get-dn>(): Double?' type=kotlin.Double? origin=GET_PROPERTY
$this: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
CONST String type=kotlin.String value=,
CONST String type=kotlin.String value=f=
CALL '<get-f>(): Float' type=kotlin.Float origin=GET_PROPERTY
$this: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
CONST String type=kotlin.String value=,
CONST String type=kotlin.String value=df=
CALL '<get-df>(): Float?' type=kotlin.Float? origin=GET_PROPERTY
$this: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
CONST String type=kotlin.String value=)
FUN GENERATED_DATA_CLASS_MEMBER name:hashCode visibility:public modality:OPEN <> ($this:Test3) returnType:Int flags:
$this: VALUE_PARAMETER name:<this> type:Test3 flags:
BLOCK_BODY
VAR IR_TEMPORARY_VARIABLE name:tmp0_result type:kotlin.Int flags:var
CONST Int type=kotlin.Int value=0
SET_VAR 'tmp0_result: Int' type=kotlin.Unit origin=EQ
CALL 'hashCode(): Int' type=kotlin.Int origin=null
$this: CALL '<get-d>(): Double' type=kotlin.Double origin=GET_PROPERTY
$this: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
SET_VAR 'tmp0_result: Int' type=kotlin.Unit origin=EQ
CALL 'plus(Int): Int' type=kotlin.Int origin=null
$this: CALL 'times(Int): Int' type=kotlin.Int origin=null
$this: GET_VAR 'tmp0_result: Int' type=kotlin.Int origin=null
other: CONST Int type=kotlin.Int value=31
other: BLOCK type=kotlin.Int origin=null
VAR IR_TEMPORARY_VARIABLE name:tmp1 type:kotlin.Double? flags:val
CALL '<get-dn>(): Double?' type=kotlin.Double? origin=GET_PROPERTY
$this: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
WHEN type=kotlin.Int origin=null
BRANCH
if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: GET_VAR 'tmp1: Double?' type=kotlin.Double? origin=null
arg1: CONST Null type=kotlin.Nothing? value=null
then: CONST Int type=kotlin.Int value=0
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: CALL 'hashCode(): Int' type=kotlin.Int origin=null
$this: GET_VAR 'tmp1: Double?' type=kotlin.Double? origin=null
SET_VAR 'tmp0_result: Int' type=kotlin.Unit origin=EQ
CALL 'plus(Int): Int' type=kotlin.Int origin=null
$this: CALL 'times(Int): Int' type=kotlin.Int origin=null
$this: GET_VAR 'tmp0_result: Int' type=kotlin.Int origin=null
other: CONST Int type=kotlin.Int value=31
other: CALL 'hashCode(): Int' type=kotlin.Int origin=null
$this: CALL '<get-f>(): Float' type=kotlin.Float origin=GET_PROPERTY
$this: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
SET_VAR 'tmp0_result: Int' type=kotlin.Unit origin=EQ
CALL 'plus(Int): Int' type=kotlin.Int origin=null
$this: CALL 'times(Int): Int' type=kotlin.Int origin=null
$this: GET_VAR 'tmp0_result: Int' type=kotlin.Int origin=null
other: CONST Int type=kotlin.Int value=31
other: BLOCK type=kotlin.Int origin=null
VAR IR_TEMPORARY_VARIABLE name:tmp2 type:kotlin.Float? flags:val
CALL '<get-df>(): Float?' type=kotlin.Float? origin=GET_PROPERTY
$this: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
WHEN type=kotlin.Int origin=null
BRANCH
if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: GET_VAR 'tmp2: Float?' type=kotlin.Float? origin=null
arg1: CONST Null type=kotlin.Nothing? value=null
then: CONST Int type=kotlin.Int value=0
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: CALL 'hashCode(): Int' type=kotlin.Int origin=null
$this: GET_VAR 'tmp2: Float?' type=kotlin.Float? origin=null
RETURN type=kotlin.Nothing from='hashCode(): Int'
GET_VAR 'tmp0_result: Int' type=kotlin.Int origin=null
FUN GENERATED_DATA_CLASS_MEMBER name:equals visibility:public modality:OPEN <> ($this:Test3, other:kotlin.Any?) returnType:Boolean flags:
$this: VALUE_PARAMETER name:<this> type:Test3 flags:
VALUE_PARAMETER name:other index:0 type:kotlin.Any? flags:
BLOCK_BODY
WHEN type=kotlin.Unit origin=null
BRANCH
if: CALL 'EQEQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQEQ
arg0: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
arg1: GET_VAR 'value-parameter other: Any?' type=kotlin.Any? origin=null
then: RETURN type=kotlin.Nothing from='equals(Any?): Boolean'
CONST Boolean type=kotlin.Boolean value=true
WHEN type=kotlin.Unit origin=null
BRANCH
if: TYPE_OP type=kotlin.Boolean origin=NOT_INSTANCEOF typeOperand=Test3
GET_VAR 'value-parameter other: Any?' type=kotlin.Any? origin=null
then: RETURN type=kotlin.Nothing from='equals(Any?): Boolean'
CONST Boolean type=kotlin.Boolean value=false
VAR IR_TEMPORARY_VARIABLE name:tmp0_other_with_cast type:Test3 flags:val
TYPE_OP type=Test3 origin=CAST typeOperand=Test3
GET_VAR 'value-parameter other: Any?' type=kotlin.Any? origin=null
WHEN type=kotlin.Unit origin=null
BRANCH
if: CALL 'NOT(Boolean): Boolean' type=kotlin.Boolean origin=EXCLEQ
arg0: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EXCLEQ
arg0: CALL '<get-d>(): Double' type=kotlin.Double origin=GET_PROPERTY
$this: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
arg1: CALL '<get-d>(): Double' type=kotlin.Double origin=GET_PROPERTY
$this: GET_VAR 'tmp0_other_with_cast: Test3' type=Test3 origin=null
then: RETURN type=kotlin.Nothing from='equals(Any?): Boolean'
CONST Boolean type=kotlin.Boolean value=false
WHEN type=kotlin.Unit origin=null
BRANCH
if: CALL 'NOT(Boolean): Boolean' type=kotlin.Boolean origin=EXCLEQ
arg0: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EXCLEQ
arg0: CALL '<get-dn>(): Double?' type=kotlin.Double? origin=GET_PROPERTY
$this: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
arg1: CALL '<get-dn>(): Double?' type=kotlin.Double? origin=GET_PROPERTY
$this: GET_VAR 'tmp0_other_with_cast: Test3' type=Test3 origin=null
then: RETURN type=kotlin.Nothing from='equals(Any?): Boolean'
CONST Boolean type=kotlin.Boolean value=false
WHEN type=kotlin.Unit origin=null
BRANCH
if: CALL 'NOT(Boolean): Boolean' type=kotlin.Boolean origin=EXCLEQ
arg0: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EXCLEQ
arg0: CALL '<get-f>(): Float' type=kotlin.Float origin=GET_PROPERTY
$this: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
arg1: CALL '<get-f>(): Float' type=kotlin.Float origin=GET_PROPERTY
$this: GET_VAR 'tmp0_other_with_cast: Test3' type=Test3 origin=null
then: RETURN type=kotlin.Nothing from='equals(Any?): Boolean'
CONST Boolean type=kotlin.Boolean value=false
WHEN type=kotlin.Unit origin=null
BRANCH
if: CALL 'NOT(Boolean): Boolean' type=kotlin.Boolean origin=EXCLEQ
arg0: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EXCLEQ
arg0: CALL '<get-df>(): Float?' type=kotlin.Float? origin=GET_PROPERTY
$this: GET_VAR 'this@Test3: Test3' type=Test3 origin=null
arg1: CALL '<get-df>(): Float?' type=kotlin.Float? origin=GET_PROPERTY
$this: GET_VAR 'tmp0_other_with_cast: Test3' type=Test3 origin=null
then: RETURN type=kotlin.Nothing from='equals(Any?): Boolean'
CONST Boolean type=kotlin.Boolean value=false
RETURN type=kotlin.Nothing from='equals(Any?): Boolean'
CONST Boolean type=kotlin.Boolean value=true
@@ -0,0 +1,2 @@
fun test(x: Any) =
x == (if (x !is Double) null!! else x)
@@ -0,0 +1,27 @@
FILE fqName:<root> fileName:/eqeqRhsConditionPossiblyAffectingLhs.kt
FUN name:test visibility:public modality:FINAL <> (x:kotlin.Any) returnType:Boolean flags:
VALUE_PARAMETER name:x index:0 type:kotlin.Any flags:
BLOCK_BODY
RETURN type=kotlin.Nothing from='test(Any): Boolean'
CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: GET_VAR 'value-parameter x: Any' type=kotlin.Any origin=null
arg1: WHEN type=kotlin.Double origin=null
BRANCH
if: TYPE_OP type=kotlin.Boolean origin=NOT_INSTANCEOF typeOperand=kotlin.Double
GET_VAR 'value-parameter x: Any' type=kotlin.Any origin=null
then: BLOCK type=kotlin.Nothing origin=EXCLEXCL
VAR IR_TEMPORARY_VARIABLE name:tmp0_notnull type:kotlin.Nothing? flags:val
CONST Null type=kotlin.Nothing? value=null
WHEN type=kotlin.Nothing origin=null
BRANCH
if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: GET_VAR 'tmp0_notnull: Nothing?' type=kotlin.Nothing? origin=null
arg1: CONST Null type=kotlin.Nothing? value=null
then: CALL 'THROW_NPE(): Nothing' type=kotlin.Nothing origin=EXCLEXCL
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: GET_VAR 'tmp0_notnull: Nothing?' type=kotlin.Nothing? origin=null
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: TYPE_OP type=kotlin.Double origin=IMPLICIT_CAST typeOperand=kotlin.Double
GET_VAR 'value-parameter x: Any' type=kotlin.Any origin=null
@@ -0,0 +1,48 @@
fun testSimple(x: Double) =
when (x) {
0.0 -> 0
else -> 1
}
fun testSmartCastInWhenSubject(x: Any): Int {
if (x !is Double) return -1
return when (x) {
0.0 -> 0
else -> 1
}
}
fun testSmartCastInWhenCondition(x: Double, y: Any): Int {
if (y !is Double) return -1
return when (x) {
y -> 0
else -> 1
}
}
fun testSmartCastInWhenConditionInBranch(x: Any) =
when (x) {
!is Double -> -1
0.0 -> 0
else -> 1
}
fun testSmartCastToDifferentTypes(x: Any, y: Any): Int {
if (x !is Double) return -1
if (y !is Float) return -1
return when (x) {
y -> 0
else -> 1
}
}
fun foo(x: Double) = x
fun testWithPrematureExitInConditionSubexpression(x: Any): Int {
return when (x) {
foo(
if (x !is Double) return 42 else x
) -> 0
else -> 1
}
}
@@ -0,0 +1,148 @@
FILE fqName:<root> fileName:/whenByFloatingPoint.kt
FUN name:testSimple visibility:public modality:FINAL <> (x:kotlin.Double) returnType:Int flags:
VALUE_PARAMETER name:x index:0 type:kotlin.Double flags:
BLOCK_BODY
RETURN type=kotlin.Nothing from='testSimple(Double): Int'
BLOCK type=kotlin.Int origin=WHEN
VAR IR_TEMPORARY_VARIABLE name:tmp0_subject type:kotlin.Double flags:val
GET_VAR 'value-parameter x: Double' type=kotlin.Double origin=null
WHEN type=kotlin.Int origin=WHEN
BRANCH
if: CALL 'ieee754equals(Double?, Double?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: GET_VAR 'tmp0_subject: Double' type=kotlin.Double origin=null
arg1: CONST Double type=kotlin.Double value=0.0
then: CONST Int type=kotlin.Int value=0
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: CONST Int type=kotlin.Int value=1
FUN name:testSmartCastInWhenSubject visibility:public modality:FINAL <> (x:kotlin.Any) returnType:Int flags:
VALUE_PARAMETER name:x index:0 type:kotlin.Any flags:
BLOCK_BODY
WHEN type=kotlin.Unit origin=null
BRANCH
if: TYPE_OP type=kotlin.Boolean origin=NOT_INSTANCEOF typeOperand=kotlin.Double
GET_VAR 'value-parameter x: Any' type=kotlin.Any origin=null
then: RETURN type=kotlin.Nothing from='testSmartCastInWhenSubject(Any): Int'
CONST Int type=kotlin.Int value=-1
RETURN type=kotlin.Nothing from='testSmartCastInWhenSubject(Any): Int'
BLOCK type=kotlin.Int origin=WHEN
VAR IR_TEMPORARY_VARIABLE name:tmp0_subject type:kotlin.Any flags:val
GET_VAR 'value-parameter x: Any' type=kotlin.Any origin=null
WHEN type=kotlin.Int origin=WHEN
BRANCH
if: CALL 'ieee754equals(Double?, Double?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: TYPE_OP type=kotlin.Double origin=IMPLICIT_CAST typeOperand=kotlin.Double
GET_VAR 'tmp0_subject: Any' type=kotlin.Any origin=null
arg1: CONST Double type=kotlin.Double value=0.0
then: CONST Int type=kotlin.Int value=0
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: CONST Int type=kotlin.Int value=1
FUN name:testSmartCastInWhenCondition visibility:public modality:FINAL <> (x:kotlin.Double, y:kotlin.Any) returnType:Int flags:
VALUE_PARAMETER name:x index:0 type:kotlin.Double flags:
VALUE_PARAMETER name:y index:1 type:kotlin.Any flags:
BLOCK_BODY
WHEN type=kotlin.Unit origin=null
BRANCH
if: TYPE_OP type=kotlin.Boolean origin=NOT_INSTANCEOF typeOperand=kotlin.Double
GET_VAR 'value-parameter y: Any' type=kotlin.Any origin=null
then: RETURN type=kotlin.Nothing from='testSmartCastInWhenCondition(Double, Any): Int'
CONST Int type=kotlin.Int value=-1
RETURN type=kotlin.Nothing from='testSmartCastInWhenCondition(Double, Any): Int'
BLOCK type=kotlin.Int origin=WHEN
VAR IR_TEMPORARY_VARIABLE name:tmp0_subject type:kotlin.Double flags:val
GET_VAR 'value-parameter x: Double' type=kotlin.Double origin=null
WHEN type=kotlin.Int origin=WHEN
BRANCH
if: CALL 'ieee754equals(Double?, Double?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: GET_VAR 'tmp0_subject: Double' type=kotlin.Double origin=null
arg1: TYPE_OP type=kotlin.Double origin=IMPLICIT_CAST typeOperand=kotlin.Double
GET_VAR 'value-parameter y: Any' type=kotlin.Any origin=null
then: CONST Int type=kotlin.Int value=0
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: CONST Int type=kotlin.Int value=1
FUN name:testSmartCastInWhenConditionInBranch visibility:public modality:FINAL <> (x:kotlin.Any) returnType:Int flags:
VALUE_PARAMETER name:x index:0 type:kotlin.Any flags:
BLOCK_BODY
RETURN type=kotlin.Nothing from='testSmartCastInWhenConditionInBranch(Any): Int'
BLOCK type=kotlin.Int origin=WHEN
VAR IR_TEMPORARY_VARIABLE name:tmp0_subject type:kotlin.Any flags:val
GET_VAR 'value-parameter x: Any' type=kotlin.Any origin=null
WHEN type=kotlin.Int origin=WHEN
BRANCH
if: TYPE_OP type=kotlin.Boolean origin=INSTANCEOF typeOperand=kotlin.Double
GET_VAR 'tmp0_subject: Any' type=kotlin.Any origin=null
then: CONST Int type=kotlin.Int value=-1
BRANCH
if: CALL 'ieee754equals(Double?, Double?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: TYPE_OP type=kotlin.Double origin=IMPLICIT_CAST typeOperand=kotlin.Double
GET_VAR 'tmp0_subject: Any' type=kotlin.Any origin=null
arg1: CONST Double type=kotlin.Double value=0.0
then: CONST Int type=kotlin.Int value=0
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: CONST Int type=kotlin.Int value=1
FUN name:testSmartCastToDifferentTypes visibility:public modality:FINAL <> (x:kotlin.Any, y:kotlin.Any) returnType:Int flags:
VALUE_PARAMETER name:x index:0 type:kotlin.Any flags:
VALUE_PARAMETER name:y index:1 type:kotlin.Any flags:
BLOCK_BODY
WHEN type=kotlin.Unit origin=null
BRANCH
if: TYPE_OP type=kotlin.Boolean origin=NOT_INSTANCEOF typeOperand=kotlin.Double
GET_VAR 'value-parameter x: Any' type=kotlin.Any origin=null
then: RETURN type=kotlin.Nothing from='testSmartCastToDifferentTypes(Any, Any): Int'
CONST Int type=kotlin.Int value=-1
WHEN type=kotlin.Unit origin=null
BRANCH
if: TYPE_OP type=kotlin.Boolean origin=NOT_INSTANCEOF typeOperand=kotlin.Float
GET_VAR 'value-parameter y: Any' type=kotlin.Any origin=null
then: RETURN type=kotlin.Nothing from='testSmartCastToDifferentTypes(Any, Any): Int'
CONST Int type=kotlin.Int value=-1
RETURN type=kotlin.Nothing from='testSmartCastToDifferentTypes(Any, Any): Int'
BLOCK type=kotlin.Int origin=WHEN
VAR IR_TEMPORARY_VARIABLE name:tmp0_subject type:kotlin.Any flags:val
GET_VAR 'value-parameter x: Any' type=kotlin.Any origin=null
WHEN type=kotlin.Int origin=WHEN
BRANCH
if: CALL 'ieee754equals(Double?, Double?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: TYPE_OP type=kotlin.Double origin=IMPLICIT_CAST typeOperand=kotlin.Double
GET_VAR 'tmp0_subject: Any' type=kotlin.Any origin=null
arg1: CALL 'toDouble(): Double' type=kotlin.Double origin=null
$this: TYPE_OP type=kotlin.Float origin=IMPLICIT_CAST typeOperand=kotlin.Float
GET_VAR 'value-parameter y: Any' type=kotlin.Any origin=null
then: CONST Int type=kotlin.Int value=0
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: CONST Int type=kotlin.Int value=1
FUN name:foo visibility:public modality:FINAL <> (x:kotlin.Double) returnType:Double flags:
VALUE_PARAMETER name:x index:0 type:kotlin.Double flags:
BLOCK_BODY
RETURN type=kotlin.Nothing from='foo(Double): Double'
GET_VAR 'value-parameter x: Double' type=kotlin.Double origin=null
FUN name:testWithPrematureExitInConditionSubexpression visibility:public modality:FINAL <> (x:kotlin.Any) returnType:Int flags:
VALUE_PARAMETER name:x index:0 type:kotlin.Any flags:
BLOCK_BODY
RETURN type=kotlin.Nothing from='testWithPrematureExitInConditionSubexpression(Any): Int'
BLOCK type=kotlin.Int origin=WHEN
VAR IR_TEMPORARY_VARIABLE name:tmp0_subject type:kotlin.Any flags:val
GET_VAR 'value-parameter x: Any' type=kotlin.Any origin=null
WHEN type=kotlin.Int origin=WHEN
BRANCH
if: CALL 'EQEQ(Any?, Any?): Boolean' type=kotlin.Boolean origin=EQEQ
arg0: GET_VAR 'tmp0_subject: Any' type=kotlin.Any origin=null
arg1: CALL 'foo(Double): Double' type=kotlin.Double origin=null
x: WHEN type=kotlin.Double origin=null
BRANCH
if: TYPE_OP type=kotlin.Boolean origin=NOT_INSTANCEOF typeOperand=kotlin.Double
GET_VAR 'value-parameter x: Any' type=kotlin.Any origin=null
then: RETURN type=kotlin.Nothing from='testWithPrematureExitInConditionSubexpression(Any): Int'
CONST Int type=kotlin.Int value=42
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: TYPE_OP type=kotlin.Double origin=IMPLICIT_CAST typeOperand=kotlin.Double
GET_VAR 'value-parameter x: Any' type=kotlin.Any origin=null
then: CONST Int type=kotlin.Int value=0
BRANCH
if: CONST Boolean type=kotlin.Boolean value=true
then: CONST Int type=kotlin.Int value=1
@@ -1010,6 +1010,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
doTest(fileName);
}
@TestMetadata("eqeqRhsConditionPossiblyAffectingLhs.kt")
public void testEqeqRhsConditionPossiblyAffectingLhs() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/floatingPointComparisons/eqeqRhsConditionPossiblyAffectingLhs.kt");
doTest(fileName);
}
@TestMetadata("floatingPointCompareTo.kt")
public void testFloatingPointCompareTo() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/floatingPointComparisons/floatingPointCompareTo.kt");
@@ -1051,6 +1057,12 @@ public class IrTextTestCaseGenerated extends AbstractIrTextTestCase {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/floatingPointComparisons/nullableFloatingPointEqeq.kt");
doTest(fileName);
}
@TestMetadata("whenByFloatingPoint.kt")
public void testWhenByFloatingPoint() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/ir/irText/expressions/floatingPointComparisons/whenByFloatingPoint.kt");
doTest(fileName);
}
}
}