From a790195808cb1e14ee24dfbcb4f179bdd5a211aa Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Thu, 8 Feb 2018 09:50:22 +0300 Subject: [PATCH] Implement "proper numeric comparisons" support in JVM BE --- .../org/jetbrains/kotlin/codegen/AsmUtil.java | 6 + .../codegen/BoxedVsPrimitiveBranchedValues.kt | 80 ++++++++++ .../jetbrains/kotlin/codegen/BranchedValue.kt | 2 +- .../kotlin/codegen/ExpressionCodegen.java | 135 ++++++++++++---- .../jetbrains/kotlin/codegen/codegenUtil.kt | 53 +++--- .../org/jetbrains/kotlin/codegen/ieee754.kt | 83 ++++++++++ ...ingPointRangeLiteralExpressionGenerator.kt | 1 + .../kotlin/codegen/state/GenerationState.kt | 151 ++++++++++-------- .../box/ieee754/comparableToTWithTLV13.kt | 11 ++ .../box/ieee754/nullableDoubleEqualsLV13.kt | 27 ++++ ...CastOnWhenSubjectAfterCheckInBranchLV13.kt | 25 +++ .../ieee754/smartCastToDifferentTypesLV13.kt | 13 ++ ...oDifferentTypesWithNumericPromotionLV13.kt | 76 +++++++++ .../ir/IrBlackBoxCodegenTestGenerated.java | 30 ++++ .../codegen/BlackBoxCodegenTestGenerated.java | 30 ++++ .../LightAnalysisModeTestGenerated.java | 30 ++++ .../kotlin/config/LanguageVersionSettings.kt | 1 + .../semantics/JsCodegenBoxTestGenerated.java | 36 +++++ 18 files changed, 652 insertions(+), 138 deletions(-) create mode 100644 compiler/backend/src/org/jetbrains/kotlin/codegen/ieee754.kt create mode 100644 compiler/testData/codegen/box/ieee754/comparableToTWithTLV13.kt create mode 100644 compiler/testData/codegen/box/ieee754/nullableDoubleEqualsLV13.kt create mode 100644 compiler/testData/codegen/box/ieee754/smartCastOnWhenSubjectAfterCheckInBranchLV13.kt create mode 100644 compiler/testData/codegen/box/ieee754/smartCastToDifferentTypesLV13.kt create mode 100644 compiler/testData/codegen/box/ieee754/smartCastToDifferentTypesWithNumericPromotionLV13.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java index 3b304dab05f..ed83da551e4 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java @@ -127,6 +127,12 @@ public class AsmUtil { return primitiveTypeByBoxedType.get(boxedType); } + @NotNull + public static Type unboxUnlessPrimitive(@NotNull Type boxedOrPrimitiveType) { + if (isPrimitive(boxedOrPrimitiveType)) return boxedOrPrimitiveType; + return unboxType(boxedOrPrimitiveType); + } + public static boolean isBoxedTypeOf(@NotNull Type boxedType, @NotNull Type unboxedType) { return unboxPrimitiveTypeOrNull(boxedType) == unboxedType; } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/BoxedVsPrimitiveBranchedValues.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/BoxedVsPrimitiveBranchedValues.kt index 05a9cd23974..2dae8b0dacb 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/BoxedVsPrimitiveBranchedValues.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/BoxedVsPrimitiveBranchedValues.kt @@ -294,4 +294,84 @@ class PrimitiveToObjectEquality private constructor( AsmUtil.isIntOrLongPrimitive(leftType) && rightType.sort == Type.OBJECT } +} + + +class Ieee754Equality private constructor( + private val frameMap: FrameMap, + left: StackValue, + right: StackValue, + operandType: Type +) : NumberLikeCompare(left, right, operandType, KtTokens.EQEQ) { + + init { + assert(operandType == Type.FLOAT_TYPE || operandType == Type.DOUBLE_TYPE) { + "Unexpected operandType for IEEE 754 equality (should be F or D): $operandType" + } + } + + private val leftType = left.type + private val rightType = right.type + + override fun condJump(jumpLabel: Label, v: InstructionAdapter, jumpIfFalse: Boolean) { + val leftIsBoxed = !AsmUtil.isPrimitive(leftType) + val rightIsBoxed = !AsmUtil.isPrimitive(rightType) + + frameMap.evaluateOnce(arg1, leftType, v) { left -> + frameMap.evaluateOnce(arg2!!, rightType, v) { right -> + val endLabel = Label() + val bothNonNullLabel = Label() + + when { + leftIsBoxed && rightIsBoxed -> { + val leftNonNullLabel = Label() + left.put(leftType, v) + v.ifnonnull(leftNonNullLabel) + // left == null + right.put(rightType, v) + v.ifnonnull(if (jumpIfFalse) jumpLabel else endLabel) + // left == null && right == null + if (jumpIfFalse) v.goTo(endLabel) else v.goTo(jumpLabel) + + v.mark(leftNonNullLabel) + // left != null + right.put(rightType, v) + v.ifnull(if (jumpIfFalse) jumpLabel else endLabel) + } + leftIsBoxed -> { + left.put(leftType, v) + v.ifnull(if (jumpIfFalse) jumpLabel else endLabel) + } + rightIsBoxed -> { + right.put(rightType, v) + v.ifnull(if (jumpIfFalse) jumpLabel else endLabel) + } + } + + v.mark(bothNonNullLabel) + left.put(operandType, v) + right.put(operandType, v) + v.cmpg(operandType) + if (jumpIfFalse) { + v.ifne(jumpLabel) + } else { + v.ifeq(jumpLabel) + } + + v.mark(endLabel) + } + } + } + + companion object { + @JvmStatic + fun create(frameMap: FrameMap, left: StackValue, right: StackValue, comparisonType: Type, opToken: IElementType) = + Ieee754Equality(frameMap, left, right, comparisonType).let { + when (opToken) { + KtTokens.EQEQ -> it + KtTokens.EXCLEQ -> Invert(it) + else -> throw AssertionError("Unexpected operator: $opToken") + } + } + } } \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/BranchedValue.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/BranchedValue.kt index e78531967ce..1d0954c4132 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/BranchedValue.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/BranchedValue.kt @@ -124,7 +124,7 @@ open class BranchedValue( IFEQ ) - fun cmp(opToken: IElementType, operandType: Type, left: StackValue, right: StackValue): StackValue = + fun cmp(opToken: IElementType, operandType: Type, left: StackValue, right: StackValue): BranchedValue = if (operandType.sort == Type.OBJECT) ObjectCompare(opToken, operandType, left, right) else diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 6e2881185a8..75a5751d17f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -62,6 +62,7 @@ import org.jetbrains.kotlin.resolve.calls.model.*; import org.jetbrains.kotlin.resolve.calls.util.CallMaker; import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject; import org.jetbrains.kotlin.resolve.calls.util.UnderscoreUtilKt; +import org.jetbrains.kotlin.resolve.checkers.PrimitiveNumericComparisonInfo; import org.jetbrains.kotlin.resolve.constants.*; import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluatorKt; import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; @@ -100,9 +101,7 @@ import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtilsKt.*; import static org.jetbrains.kotlin.resolve.BindingContext.*; import static org.jetbrains.kotlin.resolve.BindingContextUtils.getDelegationConstructorCall; import static org.jetbrains.kotlin.resolve.BindingContextUtils.isVarCapturedInClosure; -import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumClass; -import static org.jetbrains.kotlin.resolve.DescriptorUtils.isEnumEntry; -import static org.jetbrains.kotlin.resolve.DescriptorUtils.isObject; +import static org.jetbrains.kotlin.resolve.DescriptorUtils.*; import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.*; import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionExpression; import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFunctionLiteral; @@ -2991,7 +2990,10 @@ public class ExpressionCodegen extends KtVisitor impleme } else if (opToken == KtTokens.EQEQ || opToken == KtTokens.EXCLEQ || opToken == KtTokens.EQEQEQ || opToken == KtTokens.EXCLEQEQEQ) { - return generateEquals(expression.getLeft(), expression.getRight(), opToken, null); + return generateEquals( + expression.getLeft(), expression.getRight(), opToken, null, + bindingContext.get(BindingContext.PRIMITIVE_NUMERIC_COMPARISON_INFO, expression) + ); } else if (opToken == KtTokens.LT || opToken == KtTokens.LTEQ || opToken == KtTokens.GT || opToken == KtTokens.GTEQ) { @@ -3054,7 +3056,8 @@ public class ExpressionCodegen extends KtVisitor impleme @Nullable KtExpression left, @Nullable KtExpression right, @NotNull IElementType opToken, - @Nullable StackValue pregeneratedLeft + @Nullable StackValue pregeneratedLeft, + @Nullable PrimitiveNumericComparisonInfo primitiveNumericComparisonInfo ) { Type leftType = expressionType(left); Type rightType = expressionType(right); @@ -3139,7 +3142,9 @@ public class ExpressionCodegen extends KtVisitor impleme ); } - return genEqualsForExpressionsPreferIEEE754Arithmetic(left, right, opToken, leftType, rightType, pregeneratedLeft); + return genEqualsForExpressionsPreferIeee754Arithmetic( + left, right, opToken, leftType, rightType, pregeneratedLeft, primitiveNumericComparisonInfo + ); } private boolean isSelectorPureNonNullType(@NotNull KtSafeQualifiedExpression safeExpression) { @@ -3186,38 +3191,65 @@ public class ExpressionCodegen extends KtVisitor impleme ); } + @Nullable + private static KotlinType getLeftOperandType(@Nullable PrimitiveNumericComparisonInfo numericComparisonInfo) { + if (numericComparisonInfo == null) return null; + return numericComparisonInfo.getLeftType(); + } + + @Nullable + private static KotlinType getRightOperandType(@Nullable PrimitiveNumericComparisonInfo numericComparisonInfo) { + if (numericComparisonInfo == null) return null; + return numericComparisonInfo.getRightType(); + } + /*tries to use IEEE 754 arithmetic*/ - private StackValue genEqualsForExpressionsPreferIEEE754Arithmetic( + private StackValue genEqualsForExpressionsPreferIeee754Arithmetic( @Nullable KtExpression left, @Nullable KtExpression right, @NotNull IElementType opToken, @NotNull Type leftType, @NotNull Type rightType, - @Nullable StackValue pregeneratedLeft + @Nullable StackValue pregeneratedLeft, + @Nullable PrimitiveNumericComparisonInfo primitiveNumericComparisonInfo ) { assert (opToken == KtTokens.EQEQ || opToken == KtTokens.EXCLEQ) : "Optoken should be '==' or '!=', but: " + opToken; - TypeAndNullability left754Type = calcTypeForIEEE754ArithmeticIfNeeded(left); - TypeAndNullability right754Type = calcTypeForIEEE754ArithmeticIfNeeded(right); - if (left754Type != null && right754Type != null && left754Type.type.equals(right754Type.type)) { - //check nullability cause there is some optimizations in codegen for non-nullable case - if (left754Type.isNullable || right754Type.isNullable) { - if (state.getLanguageVersionSettings().getApiVersion().compareTo(ApiVersion.KOTLIN_1_1) >= 0) { - return StackValue.operation(Type.BOOLEAN_TYPE, v -> { - generate754EqualsForNullableTypesViaIntrinsic(v, opToken, pregeneratedLeft, left, left754Type, right, right754Type); - return Unit.INSTANCE; - }); + TypeAndNullability left754Type = calcTypeForIeee754ArithmeticIfNeeded(left, getLeftOperandType(primitiveNumericComparisonInfo)); + TypeAndNullability right754Type = calcTypeForIeee754ArithmeticIfNeeded(right, getRightOperandType(primitiveNumericComparisonInfo)); + if (left754Type != null && right754Type != null) { + if (left754Type.type.equals(right754Type.type)) { + //check nullability cause there is some optimizations in codegen for non-nullable case + if (left754Type.isNullable || right754Type.isNullable) { + if (state.getLanguageVersionSettings().getApiVersion().compareTo(ApiVersion.KOTLIN_1_1) >= 0) { + return StackValue.operation(Type.BOOLEAN_TYPE, v -> { + generate754EqualsForNullableTypesViaIntrinsic(v, opToken, pregeneratedLeft, left, left754Type, right, right754Type); + return Unit.INSTANCE; + }); + } + else { + return StackValue.operation(Type.BOOLEAN_TYPE, v -> { + generate754EqualsForNullableTypes(v, opToken, pregeneratedLeft, left, left754Type, right, right754Type); + return Unit.INSTANCE; + }); + } } else { - return StackValue.operation(Type.BOOLEAN_TYPE, v -> { - generate754EqualsForNullableTypes(v, opToken, pregeneratedLeft, left, left754Type, right, right754Type); - return Unit.INSTANCE; - }); + leftType = left754Type.type; + rightType = right754Type.type; } } - else { - leftType = left754Type.type; - rightType = right754Type.type; + else if (shouldUseProperNumberComparisons()) { + Type comparisonType = comparisonOperandType(left754Type.type, right754Type.type); + if (comparisonType == Type.FLOAT_TYPE || comparisonType == Type.DOUBLE_TYPE) { + return Ieee754Equality.create( + myFrameMap, + genLazy(left, boxIfNullable(left754Type)), + genLazy(right, boxIfNullable(right754Type)), + comparisonType, + opToken + ); + } } } @@ -3228,6 +3260,12 @@ public class ExpressionCodegen extends KtVisitor impleme ); } + @NotNull + private static Type boxIfNullable(@NotNull TypeAndNullability ieee754Type) { + if (ieee754Type.isNullable) return AsmUtil.boxType(ieee754Type.type); + return ieee754Type.type; + } + private void generate754EqualsForNullableTypesViaIntrinsic( @NotNull InstructionAdapter v, @NotNull IElementType opToken, @@ -3400,6 +3438,8 @@ public class ExpressionCodegen extends KtVisitor impleme private StackValue generateComparison(KtBinaryExpression expression, StackValue receiver) { ResolvedCall resolvedCall = CallUtilKt.getResolvedCallWithAssert(expression, bindingContext); + PrimitiveNumericComparisonInfo primitiveNumericComparisonInfo = + bindingContext.get(BindingContext.PRIMITIVE_NUMERIC_COMPARISON_INFO, expression); KtExpression left = expression.getLeft(); KtExpression right = expression.getRight(); @@ -3409,12 +3449,20 @@ public class ExpressionCodegen extends KtVisitor impleme StackValue rightValue; Type leftType = expressionType(left); Type rightType = expressionType(right); - TypeAndNullability left754Type = calcTypeForIEEE754ArithmeticIfNeeded(left); - TypeAndNullability right754Type = calcTypeForIEEE754ArithmeticIfNeeded(right); + TypeAndNullability left754Type = calcTypeForIeee754ArithmeticIfNeeded(left, getLeftOperandType(primitiveNumericComparisonInfo)); + TypeAndNullability right754Type = calcTypeForIeee754ArithmeticIfNeeded(right, getRightOperandType(primitiveNumericComparisonInfo)); Callable callable = resolveToCallable((FunctionDescriptor) resolvedCall.getResultingDescriptor(), false, resolvedCall); - boolean is754Arithmetic = left754Type != null && right754Type != null && left754Type.type.equals(right754Type.type); - if (callable instanceof IntrinsicCallable && ((isPrimitive(leftType) && isPrimitive(rightType)) || is754Arithmetic)) { - type = is754Arithmetic ? left754Type.type : comparisonOperandType(leftType, rightType); + boolean isSame754ArithmeticTypes = left754Type != null && right754Type != null && left754Type.type.equals(right754Type.type); + boolean properNumberComparisons = shouldUseProperNumberComparisons(); + + if (properNumberComparisons && left754Type != null && right754Type != null) { + type = comparisonOperandType(leftType, rightType); + leftValue = gen(left); + rightValue = gen(right); + } + else if (!properNumberComparisons && + callable instanceof IntrinsicCallable && ((isPrimitive(leftType) && isPrimitive(rightType)) || isSame754ArithmeticTypes)) { + type = isSame754ArithmeticTypes ? left754Type.type : comparisonOperandType(leftType, rightType); leftValue = gen(left); rightValue = gen(right); } @@ -3426,9 +3474,25 @@ public class ExpressionCodegen extends KtVisitor impleme return StackValue.cmp(expression.getOperationToken(), type, leftValue, rightValue); } - private TypeAndNullability calcTypeForIEEE754ArithmeticIfNeeded(@Nullable KtExpression expression) { - return CodegenUtilKt.calcTypeForIEEE754ArithmeticIfNeeded( - expression, bindingContext, context.getFunctionDescriptor(), state.getLanguageVersionSettings()); + @Nullable + private TypeAndNullability calcTypeForIeee754ArithmeticIfNeeded( + @Nullable KtExpression expression, + @Nullable KotlinType inferredPrimitiveType + ) { + if (expression == null) { + return null; + } + else if (shouldUseProperNumberComparisons()) { + return Ieee754Kt.calcProperTypeForIeee754ArithmeticIfNeeded(expression, bindingContext, inferredPrimitiveType, typeMapper); + } + else { + return Ieee754Kt.legacyCalcTypeForIeee754ArithmeticIfNeeded( + expression, bindingContext, context.getFunctionDescriptor(), state.getLanguageVersionSettings()); + } + } + + private boolean shouldUseProperNumberComparisons() { + return state.getLanguageVersionSettings().supportsFeature(LanguageFeature.ProperNumberComparisons); } private StackValue generateAssignmentExpression(KtBinaryExpression expression) { @@ -4251,7 +4315,10 @@ The "returned" value of try expression with no finally is either the last expres private StackValue generateExpressionMatch(StackValue expressionToMatch, KtExpression subjectExpression, KtExpression patternExpression) { if (expressionToMatch != null) { - return generateEquals(subjectExpression, patternExpression, KtTokens.EQEQ, expressionToMatch); + return generateEquals( + subjectExpression, patternExpression, KtTokens.EQEQ, expressionToMatch, + bindingContext.get(BindingContext.PRIMITIVE_NUMERIC_COMPARISON_INFO, patternExpression) + ); } else { return gen(patternExpression); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt index d1a95c07f28..cd6cf71199a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/codegenUtil.kt @@ -17,7 +17,6 @@ import org.jetbrains.kotlin.codegen.intrinsics.TypeIntrinsics import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper -import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl @@ -35,10 +34,8 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils.isSubclass import org.jetbrains.kotlin.resolve.annotations.hasJvmStaticAnnotation -import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoBefore import org.jetbrains.kotlin.resolve.calls.callUtil.getFirstArgumentExpression import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns import org.jetbrains.kotlin.resolve.jvm.JvmClassName import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm @@ -49,7 +46,6 @@ import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.utils.DFS -import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter @@ -274,38 +270,8 @@ private fun CallableDescriptor.isJvmStaticIn(predicate: (DeclarationDescriptor) fun Collection.filterOutDescriptorsWithSpecialNames() = filterNot { it.name.isSpecial } - -class TypeAndNullability(@JvmField val type: Type, @JvmField val isNullable: Boolean) - class JvmKotlinType(val type: Type, val kotlinType: KotlinType?) -fun calcTypeForIEEE754ArithmeticIfNeeded( - expression: KtExpression?, - bindingContext: BindingContext, - descriptor: DeclarationDescriptor, - languageVersionSettings: LanguageVersionSettings -): TypeAndNullability? { - val ktType = expression.kotlinType(bindingContext) ?: return null - - if (KotlinBuiltIns.isDoubleOrNullableDouble(ktType)) { - return TypeAndNullability(Type.DOUBLE_TYPE, TypeUtils.isNullableType(ktType)) - } - - if (KotlinBuiltIns.isFloatOrNullableFloat(ktType)) { - return TypeAndNullability(Type.FLOAT_TYPE, TypeUtils.isNullableType(ktType)) - } - - val dataFlow = DataFlowValueFactory.createDataFlowValue(expression!!, ktType, bindingContext, descriptor) - val stableTypes = bindingContext.getDataFlowInfoBefore(expression).getStableTypes(dataFlow, languageVersionSettings) - return stableTypes.firstNotNullResult { - when { - KotlinBuiltIns.isDoubleOrNullableDouble(it) -> TypeAndNullability(Type.DOUBLE_TYPE, TypeUtils.isNullableType(it)) - KotlinBuiltIns.isFloatOrNullableFloat(it) -> TypeAndNullability(Type.FLOAT_TYPE, TypeUtils.isNullableType(it)) - else -> null - } - } -} - fun KotlinType.asmType(typeMapper: KotlinTypeMapper) = typeMapper.mapType(this) fun KtExpression?.asmType(typeMapper: KotlinTypeMapper, bindingContext: BindingContext): Type = @@ -439,3 +405,22 @@ val CodegenContext<*>.parentContexts val CodegenContext<*>.contextStackText get() = parentContextsWithSelf.joinToString(separator = "\n") { it.toString() } + +inline fun FrameMap.evaluateOnce( + value: StackValue, + asType: Type, + v: InstructionAdapter, + body: (StackValue) -> Unit +) { + val valueOrTmp: StackValue = + if (value.canHaveSideEffects()) + StackValue.local(enterTemp(asType), asType).apply { store(value, v) } + else + value + + body(valueOrTmp) + + if (valueOrTmp != value) { + leaveTemp(asType) + } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ieee754.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/ieee754.kt new file mode 100644 index 00000000000..706e7cbe2fd --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ieee754.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.codegen + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper +import org.jetbrains.kotlin.config.LanguageVersionSettings +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.psi.KtExpression +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoBefore +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.utils.addToStdlib.firstNotNullResult +import org.jetbrains.org.objectweb.asm.Type + +class TypeAndNullability(@JvmField val type: Type, @JvmField val isNullable: Boolean) + +fun calcProperTypeForIeee754ArithmeticIfNeeded( + expression: KtExpression, + bindingContext: BindingContext, + inferredPrimitiveType: KotlinType?, + typeMapper: KotlinTypeMapper +): TypeAndNullability? { + if (inferredPrimitiveType == null) return null + val ktType = expression.kotlinType(bindingContext) ?: return null + val isNullable = TypeUtils.isNullableType(ktType) + val asmType = typeMapper.mapType(inferredPrimitiveType) + if (!AsmUtil.isPrimitive(asmType)) return null + return TypeAndNullability(asmType, isNullable) +} + +fun legacyCalcTypeForIeee754ArithmeticIfNeeded( + expression: KtExpression?, + bindingContext: BindingContext, + descriptor: DeclarationDescriptor, + languageVersionSettings: LanguageVersionSettings +): TypeAndNullability? { + val ktType = expression.kotlinType(bindingContext) ?: return null + + if (KotlinBuiltIns.isDoubleOrNullableDouble(ktType)) { + return TypeAndNullability( + Type.DOUBLE_TYPE, + TypeUtils.isNullableType(ktType) + ) + } + + if (KotlinBuiltIns.isFloatOrNullableFloat(ktType)) { + return TypeAndNullability( + Type.FLOAT_TYPE, + TypeUtils.isNullableType(ktType) + ) + } + + val dataFlow = DataFlowValueFactory.createDataFlowValue( + expression!!, + ktType, + bindingContext, + descriptor + ) + val stableTypes = bindingContext.getDataFlowInfoBefore(expression).getStableTypes(dataFlow, languageVersionSettings) + return stableTypes.firstNotNullResult { + when { + KotlinBuiltIns.isDoubleOrNullableDouble(it) -> TypeAndNullability( + Type.DOUBLE_TYPE, + TypeUtils.isNullableType( + it + ) + ) + KotlinBuiltIns.isFloatOrNullableFloat(it) -> TypeAndNullability( + Type.FLOAT_TYPE, + TypeUtils.isNullableType( + it + ) + ) + else -> null + } + } +} \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/inExpression/InFloatingPointRangeLiteralExpressionGenerator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/inExpression/InFloatingPointRangeLiteralExpressionGenerator.kt index 859ff96c992..925e2aa705f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/range/inExpression/InFloatingPointRangeLiteralExpressionGenerator.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/range/inExpression/InFloatingPointRangeLiteralExpressionGenerator.kt @@ -95,6 +95,7 @@ class InFloatingPointRangeLiteralExpressionGenerator( } + // TODO evaluateOnce private fun introduceTemporaryIfRequired(v: InstructionAdapter, value: StackValue, type: Type): Pair { val resultValue: StackValue val resultType: Type? diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt index 5b985da8865..1c6ee94f9ec 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/GenerationState.kt @@ -49,66 +49,67 @@ import org.jetbrains.kotlin.storage.LockBasedStorageManager import java.io.File class GenerationState private constructor( - val project: Project, - builderFactory: ClassBuilderFactory, - val module: ModuleDescriptor, - bindingContext: BindingContext, - val files: List, - val configuration: CompilerConfiguration, - val generateDeclaredClassFilter: GenerateClassFilter, - val codegenFactory: CodegenFactory, - val targetId: TargetId?, - moduleName: String?, - val outDirectory: File?, - private val onIndependentPartCompilationEnd: GenerationStateEventCallback, - wantsDiagnostics: Boolean + val project: Project, + builderFactory: ClassBuilderFactory, + val module: ModuleDescriptor, + bindingContext: BindingContext, + val files: List, + val configuration: CompilerConfiguration, + val generateDeclaredClassFilter: GenerateClassFilter, + val codegenFactory: CodegenFactory, + val targetId: TargetId?, + moduleName: String?, + val outDirectory: File?, + private val onIndependentPartCompilationEnd: GenerationStateEventCallback, + wantsDiagnostics: Boolean ) { class Builder( - private val project: Project, - private val builderFactory: ClassBuilderFactory, - private val module: ModuleDescriptor, - private val bindingContext: BindingContext, - private val files: List, - private val configuration: CompilerConfiguration + private val project: Project, + private val builderFactory: ClassBuilderFactory, + private val module: ModuleDescriptor, + private val bindingContext: BindingContext, + private val files: List, + private val configuration: CompilerConfiguration ) { private var generateDeclaredClassFilter: GenerateClassFilter = GenerateClassFilter.GENERATE_ALL fun generateDeclaredClassFilter(v: GenerateClassFilter) = - apply { generateDeclaredClassFilter = v } + apply { generateDeclaredClassFilter = v } private var codegenFactory: CodegenFactory = DefaultCodegenFactory fun codegenFactory(v: CodegenFactory) = - apply { codegenFactory = v } + apply { codegenFactory = v } private var targetId: TargetId? = null fun targetId(v: TargetId?) = - apply { targetId = v } + apply { targetId = v } private var moduleName: String? = configuration[CommonConfigurationKeys.MODULE_NAME] fun moduleName(v: String?) = - apply { moduleName = v } + apply { moduleName = v } // 'outDirectory' is a hack to correctly determine if a compiled class is from the same module as the callee during // partial compilation. Module chunks are treated as a single module. // TODO: get rid of it with the proper module infrastructure private var outDirectory: File? = null + fun outDirectory(v: File?) = - apply { outDirectory = v } + apply { outDirectory = v } private var onIndependentPartCompilationEnd: GenerationStateEventCallback = GenerationStateEventCallback.DO_NOTHING fun onIndependentPartCompilationEnd(v: GenerationStateEventCallback) = - apply { onIndependentPartCompilationEnd = v } + apply { onIndependentPartCompilationEnd = v } private var wantsDiagnostics: Boolean = true fun wantsDiagnostics(v: Boolean) = - apply { wantsDiagnostics = v } + apply { wantsDiagnostics = v } fun build() = - GenerationState( - project, builderFactory, module, bindingContext, files, configuration, - generateDeclaredClassFilter, codegenFactory, targetId, - moduleName, outDirectory, onIndependentPartCompilationEnd, wantsDiagnostics - ) + GenerationState( + project, builderFactory, module, bindingContext, files, configuration, + generateDeclaredClassFilter, codegenFactory, targetId, + moduleName, outDirectory, onIndependentPartCompilationEnd, wantsDiagnostics + ) } abstract class GenerateClassFilter { @@ -119,7 +120,8 @@ class GenerationState private constructor( open fun shouldGenerateClassMembers(processingClassOrObject: KtClassOrObject) = shouldGenerateClass(processingClassOrObject) companion object { - @JvmField val GENERATE_ALL: GenerateClassFilter = object : GenerateClassFilter() { + @JvmField + val GENERATE_ALL: GenerateClassFilter = object : GenerateClassFilter() { override fun shouldAnnotateClass(processingClassOrObject: KtClassOrObject): Boolean = true override fun shouldGenerateClass(processingClassOrObject: KtClassOrObject): Boolean = true @@ -137,7 +139,7 @@ class GenerationState private constructor( val packagesWithObsoleteParts: Set val obsoleteMultifileClasses: List val deserializationConfiguration: DeserializationConfiguration = - CompilerDeserializationConfiguration(configuration.languageVersionSettings) + CompilerDeserializationConfiguration(configuration.languageVersionSettings) val deprecationProvider = DeprecationResolver(LockBasedStorageManager.NO_LOCKS, configuration.languageVersionSettings) @@ -152,15 +154,15 @@ class GenerationState private constructor( obsoleteMultifileClasses = incrementalCacheForThisTarget.getObsoleteMultifileClasses().map { JvmClassName.byInternalName(it).fqNameForClassNameWithoutDollars } - } - else { + } else { incrementalCacheForThisTarget = null packagesWithObsoleteParts = emptySet() obsoleteMultifileClasses = emptyList() } } - val extraJvmDiagnosticsTrace: BindingTrace = DelegatingBindingTrace(bindingContext, "For extra diagnostics in ${this::class.java}", false) + val extraJvmDiagnosticsTrace: BindingTrace = + DelegatingBindingTrace(bindingContext, "For extra diagnostics in ${this::class.java}", false) private val interceptedBuilderFactory: ClassBuilderFactory private var used = false @@ -174,21 +176,23 @@ class GenerationState private constructor( val target = configuration.get(JVMConfigurationKeys.JVM_TARGET) ?: JvmTarget.DEFAULT val isJvm8Target: Boolean = target == JvmTarget.JVM_1_8 - val isJvm8TargetWithDefaults: Boolean = isJvm8Target && configuration.getBoolean(JVMConfigurationKeys.JVM8_TARGET_WITH_DEFAULTS) + val isJvm8TargetWithDefaults: Boolean = isJvm8Target && configuration.getBoolean(JVMConfigurationKeys.JVM8_TARGET_WITH_DEFAULTS) val generateDefaultImplsForJvm8: Boolean = configuration.getBoolean(JVMConfigurationKeys.INTERFACE_COMPATIBILITY) val moduleName: String = moduleName ?: JvmCodegenUtil.getModuleName(module) val classBuilderMode: ClassBuilderMode = builderFactory.classBuilderMode - val bindingTrace: BindingTrace = DelegatingBindingTrace(bindingContext, "trace in GenerationState", - filter = if (wantsDiagnostics) BindingTraceFilter.ACCEPT_ALL else BindingTraceFilter.NO_DIAGNOSTICS) + val bindingTrace: BindingTrace = DelegatingBindingTrace( + bindingContext, "trace in GenerationState", + filter = if (wantsDiagnostics) BindingTraceFilter.ACCEPT_ALL else BindingTraceFilter.NO_DIAGNOSTICS + ) val bindingContext: BindingContext = bindingTrace.bindingContext val typeMapper: KotlinTypeMapper = KotlinTypeMapper( - this.bindingContext, classBuilderMode, IncompatibleClassTrackerImpl(extraJvmDiagnosticsTrace), - this.moduleName, isJvm8Target, isJvm8TargetWithDefaults + this.bindingContext, classBuilderMode, IncompatibleClassTrackerImpl(extraJvmDiagnosticsTrace), + this.moduleName, isJvm8Target, isJvm8TargetWithDefaults ) val intrinsics: IntrinsicMethods = run { val shouldUseConsistentEquals = languageVersionSettings.supportsFeature(LanguageFeature.ThrowNpeOnExplicitEqualsForBoxedNull) && - !configuration.getBoolean(JVMConfigurationKeys.NO_EXCEPTION_ON_EXPLICIT_EQUALS_FOR_BOXED_NULL) + !configuration.getBoolean(JVMConfigurationKeys.NO_EXCEPTION_ON_EXPLICIT_EQUALS_FOR_BOXED_NULL) IntrinsicMethods(target, shouldUseConsistentEquals) } val samWrapperClasses: SamWrapperClasses = SamWrapperClasses(this) @@ -210,8 +214,8 @@ class GenerationState private constructor( val isCallAssertionsDisabled: Boolean = configuration.getBoolean(JVMConfigurationKeys.DISABLE_CALL_ASSERTIONS) val isReceiverAssertionsDisabled: Boolean = - configuration.getBoolean(JVMConfigurationKeys.DISABLE_RECEIVER_ASSERTIONS) || - !languageVersionSettings.supportsFeature(LanguageFeature.NullabilityAssertionOnExtensionReceiver) + configuration.getBoolean(JVMConfigurationKeys.DISABLE_RECEIVER_ASSERTIONS) || + !languageVersionSettings.supportsFeature(LanguageFeature.NullabilityAssertionOnExtensionReceiver) val isParamAssertionsDisabled: Boolean = configuration.getBoolean(JVMConfigurationKeys.DISABLE_PARAM_ASSERTIONS) val isInlineDisabled: Boolean = configuration.getBoolean(CommonConfigurationKeys.DISABLE_INLINE) val useTypeTableInSerializer: Boolean = configuration.getBoolean(JVMConfigurationKeys.USE_TYPE_TABLE) @@ -225,26 +229,32 @@ class GenerationState private constructor( val shouldInlineConstVals = languageVersionSettings.supportsFeature(LanguageFeature.InlineConstVals) - val constructorCallNormalizationMode = configuration.get(JVMConfigurationKeys.CONSTRUCTOR_CALL_NORMALIZATION_MODE, - JVMConstructorCallNormalizationMode.DEFAULT) + val constructorCallNormalizationMode = configuration.get( + JVMConfigurationKeys.CONSTRUCTOR_CALL_NORMALIZATION_MODE, + JVMConstructorCallNormalizationMode.DEFAULT + ) init { val disableOptimization = configuration.get(JVMConfigurationKeys.DISABLE_OPTIMIZATION, false) this.interceptedBuilderFactory = builderFactory - .wrapWith( - { OptimizationClassBuilderFactory(it, disableOptimization, constructorCallNormalizationMode) }, - { BuilderFactoryForDuplicateSignatureDiagnostics( - it, this.bindingContext, diagnostics, this.moduleName, - shouldGenerate = { !shouldOnlyCollectSignatures(it) } - ).apply { duplicateSignatureFactory = this } }, - { BuilderFactoryForDuplicateClassNameDiagnostics(it, diagnostics) }, - { configuration.get(JVMConfigurationKeys.DECLARATIONS_JSON_PATH) - ?.let { destination -> SignatureDumpingBuilderFactory(it, File(destination)) } ?: it } - ) - .wrapWith(ClassBuilderInterceptorExtension.getInstances(project)) { builderFactory, extension -> - extension.interceptClassBuilderFactory(builderFactory, bindingContext, diagnostics) + .wrapWith( + { OptimizationClassBuilderFactory(it, disableOptimization, constructorCallNormalizationMode) }, + { + BuilderFactoryForDuplicateSignatureDiagnostics( + it, this.bindingContext, diagnostics, this.moduleName, + shouldGenerate = { !shouldOnlyCollectSignatures(it) } + ).apply { duplicateSignatureFactory = this } + }, + { BuilderFactoryForDuplicateClassNameDiagnostics(it, diagnostics) }, + { + configuration.get(JVMConfigurationKeys.DECLARATIONS_JSON_PATH) + ?.let { destination -> SignatureDumpingBuilderFactory(it, File(destination)) } ?: it } + ) + .wrapWith(ClassBuilderInterceptorExtension.getInstances(project)) { builderFactory, extension -> + extension.interceptClassBuilderFactory(builderFactory, bindingContext, diagnostics) + } this.factory = ClassFileFactory(this, interceptedBuilderFactory) } @@ -269,13 +279,13 @@ class GenerationState private constructor( interceptedBuilderFactory.close() } - private fun shouldOnlyCollectSignatures(origin: JvmDeclarationOrigin) - = classBuilderMode == ClassBuilderMode.LIGHT_CLASSES && origin.originKind in doNotGenerateInLightClassMode + private fun shouldOnlyCollectSignatures(origin: JvmDeclarationOrigin) = + classBuilderMode == ClassBuilderMode.LIGHT_CLASSES && origin.originKind in doNotGenerateInLightClassMode } private val doNotGenerateInLightClassMode = setOf(CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL, BRIDGE, COLLECTION_STUB, AUGMENTED_BUILTIN_API) -private class LazyJvmDiagnostics(compute: () -> Diagnostics): Diagnostics { +private class LazyJvmDiagnostics(compute: () -> Diagnostics) : Diagnostics { private val delegate by lazy(LazyThreadSafetyMode.SYNCHRONIZED, compute) override val modificationTracker: ModificationTracker @@ -283,7 +293,7 @@ private class LazyJvmDiagnostics(compute: () -> Diagnostics): Diagnostics { override fun all(): Collection = delegate.all() - override fun forElement(psiElement: PsiElement) = delegate.forElement(psiElement) + override fun forElement(psiElement: PsiElement) = delegate.forElement(psiElement) override fun isEmpty() = delegate.isEmpty() @@ -294,17 +304,20 @@ private class LazyJvmDiagnostics(compute: () -> Diagnostics): Diagnostics { interface GenerationStateEventCallback : (GenerationState) -> Unit { companion object { - val DO_NOTHING = GenerationStateEventCallback { } + val DO_NOTHING = GenerationStateEventCallback { } } } fun GenerationStateEventCallback(block: (GenerationState) -> Unit): GenerationStateEventCallback = - object : GenerationStateEventCallback { - override fun invoke(s: GenerationState) = block(s) - } + object : GenerationStateEventCallback { + override fun invoke(s: GenerationState) = block(s) + } private fun ClassBuilderFactory.wrapWith(vararg wrappers: (ClassBuilderFactory) -> ClassBuilderFactory): ClassBuilderFactory = - wrappers.fold(this) { builderFactory, wrapper -> wrapper(builderFactory) } + wrappers.fold(this) { builderFactory, wrapper -> wrapper(builderFactory) } -private inline fun ClassBuilderFactory.wrapWith(elements: Iterable, wrapper: (ClassBuilderFactory, T) -> ClassBuilderFactory): ClassBuilderFactory = - elements.fold(this, wrapper) +private inline fun ClassBuilderFactory.wrapWith( + elements: Iterable, + wrapper: (ClassBuilderFactory, T) -> ClassBuilderFactory +): ClassBuilderFactory = + elements.fold(this, wrapper) diff --git a/compiler/testData/codegen/box/ieee754/comparableToTWithTLV13.kt b/compiler/testData/codegen/box/ieee754/comparableToTWithTLV13.kt new file mode 100644 index 00000000000..452be1dec63 --- /dev/null +++ b/compiler/testData/codegen/box/ieee754/comparableToTWithTLV13.kt @@ -0,0 +1,11 @@ +// LANGUAGE_VERSION: 1.3 + +fun less(x: Comparable, y: Float) = x is Float && x < y +fun less(x: Comparable, y: Double) = x is Double && x < y + +fun box(): String { + if (less(-0.0F, 0.0F)) return "Fail F" + if (less(-0.0, 0.0)) return "Fail D" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/ieee754/nullableDoubleEqualsLV13.kt b/compiler/testData/codegen/box/ieee754/nullableDoubleEqualsLV13.kt new file mode 100644 index 00000000000..62ff31cea93 --- /dev/null +++ b/compiler/testData/codegen/box/ieee754/nullableDoubleEqualsLV13.kt @@ -0,0 +1,27 @@ +// LANGUAGE_VERSION: 1.3 + +fun myEquals(a: Double?, b: Double?) = a == b + +fun myEquals1(a: Double?, b: Double) = a == b + +fun myEquals2(a: Double, b: Double?) = a == b + +fun myEquals0(a: Double, b: Double) = a == b + + +fun box(): String { + if (!myEquals(null, null)) return "fail 1" + if (myEquals(null, 0.0)) return "fail 2" + if (myEquals(0.0, null)) return "fail 3" + if (!myEquals(0.0, 0.0)) return "fail 4" + + if (myEquals1(null, 0.0)) return "fail 5" + if (!myEquals1(0.0, 0.0)) return "fail 6" + + if (myEquals2(0.0, null)) return "fail 7" + if (!myEquals2(0.0, 0.0)) return "fail 8" + + if (!myEquals0(0.0, 0.0)) return "fail 9" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/ieee754/smartCastOnWhenSubjectAfterCheckInBranchLV13.kt b/compiler/testData/codegen/box/ieee754/smartCastOnWhenSubjectAfterCheckInBranchLV13.kt new file mode 100644 index 00000000000..3d8ef73e615 --- /dev/null +++ b/compiler/testData/codegen/box/ieee754/smartCastOnWhenSubjectAfterCheckInBranchLV13.kt @@ -0,0 +1,25 @@ +// LANGUAGE_VERSION: 1.3 + +fun testF(x: Any) = + when (x) { + !is Float -> "!Float" + 0.0F -> "0.0" + else -> "other" + } + +fun testD(x: Any) = + when (x) { + !is Double -> "!Double" + 0.0 -> "0.0" + else -> "other" + } + +fun box(): String { + val tf = testF(0.0F) + if (tf != "0.0") return "Fail 1: $tf" + + val td = testD(0.0) + if (td != "0.0") return "Fail 2: $td" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/ieee754/smartCastToDifferentTypesLV13.kt b/compiler/testData/codegen/box/ieee754/smartCastToDifferentTypesLV13.kt new file mode 100644 index 00000000000..99ddfce8751 --- /dev/null +++ b/compiler/testData/codegen/box/ieee754/smartCastToDifferentTypesLV13.kt @@ -0,0 +1,13 @@ +// LANGUAGE_VERSION: 1.3 + +fun ne(x: Any, y: Any) = x is Double && y is Float && x != y +fun lt(x: Any, y: Any) = x is Double && y is Float && x < y +fun gt(x: Any, y: Any) = x is Double && y is Float && x > y + +fun box(): String { + if (ne(0.0, -0.0F)) return "fail 1" + if (lt(0.0, -0.0F)) return "fail 2" + if (gt(0.0, -0.0F)) return "fail 3" + + return "OK" +} diff --git a/compiler/testData/codegen/box/ieee754/smartCastToDifferentTypesWithNumericPromotionLV13.kt b/compiler/testData/codegen/box/ieee754/smartCastToDifferentTypesWithNumericPromotionLV13.kt new file mode 100644 index 00000000000..dd6918036e5 --- /dev/null +++ b/compiler/testData/codegen/box/ieee754/smartCastToDifferentTypesWithNumericPromotionLV13.kt @@ -0,0 +1,76 @@ +// LANGUAGE_VERSION: 1.3 +// IGNORE_BACKEND: JS + +fun eqDI(x: Any?, y: Any?) = x is Double? && y is Int? && x == y +fun eqDL(x: Any?, y: Any?) = x is Double? && y is Long? && x == y +fun eqID(x: Any?, y: Any?) = x is Int? && y is Double? && x == y +fun eqLD(x: Any?, y: Any?) = x is Long? && y is Double? && x == y +fun eqFI(x: Any?, y: Any?) = x is Float? && y is Int? && x == y +fun eqFL(x: Any?, y: Any?) = x is Float? && y is Long? && x == y +fun eqIF(x: Any?, y: Any?) = x is Int? && y is Float? && x == y +fun eqLF(x: Any?, y: Any?) = x is Long? && y is Float? && x == y + +fun testNullNull() { + if (!eqDI(null, null)) throw Exception() + if (!eqDL(null, null)) throw Exception() + if (!eqID(null, null)) throw Exception() + if (!eqLD(null, null)) throw Exception() + if (!eqFI(null, null)) throw Exception() + if (!eqFL(null, null)) throw Exception() + if (!eqIF(null, null)) throw Exception() + if (!eqLF(null, null)) throw Exception() +} + +fun testNull0() { + if (eqDI(null, 0)) throw Exception() + if (eqDL(null, 0L)) throw Exception() + if (eqID(null, 0.0)) throw Exception() + if (eqLD(null, 0.0)) throw Exception() + if (eqFI(null, 0)) throw Exception() + if (eqFL(null, 0L)) throw Exception() + if (eqIF(null, 0.0F)) throw Exception() + if (eqLF(null, 0.0F)) throw Exception() +} + +fun test0Null() { + if (eqDI(0.0, null)) throw Exception() + if (eqDL(0.0, null)) throw Exception() + if (eqID(0, null)) throw Exception() + if (eqLD(0L, null)) throw Exception() + if (eqFI(0.0F, null)) throw Exception() + if (eqFL(0.0F, null)) throw Exception() + if (eqIF(0, null)) throw Exception() + if (eqLF(0L, null)) throw Exception() +} + +fun testPlusMinus0() { + if (!eqDI(-0.0, 0)) throw Exception() + if (!eqDL(-0.0, 0L)) throw Exception() + if (!eqID(0, -0.0)) throw Exception() + if (!eqLD(0L, -0.0)) throw Exception() + if (!eqFI(-0.0F, 0)) throw Exception() + if (!eqFL(-0.0F, 0L)) throw Exception() + if (!eqIF(0, -0.0F)) throw Exception() + if (!eqLF(0L, -0.0F)) throw Exception() +} + +fun test01() { + if (eqDI(0.0, 1)) throw Exception() + if (eqDL(0.0, 1L)) throw Exception() + if (eqID(0, 1.0)) throw Exception() + if (eqLD(0L, 1.0)) throw Exception() + if (eqFI(0.0F, 1)) throw Exception() + if (eqFL(0.0F, 1L)) throw Exception() + if (eqIF(0, 1.0F)) throw Exception() + if (eqLF(0L, 1.0F)) throw Exception() +} + +fun box(): String { + testNullNull() + testNull0() + test0Null() + testPlusMinus0() + test01() + + return "OK" +} \ No newline at end of file diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 9ea0dde8ceb..9c245380e9b 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -9993,6 +9993,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("comparableToTWithTLV13.kt") + public void testComparableToTWithTLV13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/comparableToTWithTLV13.kt"); + doTest(fileName); + } + @TestMetadata("comparableTypeCast.kt") public void testComparableTypeCast() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/comparableTypeCast.kt"); @@ -10101,6 +10107,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("nullableDoubleEqualsLV13.kt") + public void testNullableDoubleEqualsLV13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/nullableDoubleEqualsLV13.kt"); + doTest(fileName); + } + @TestMetadata("nullableDoubleNotEquals.kt") public void testNullableDoubleNotEquals() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/nullableDoubleNotEquals.kt"); @@ -10149,12 +10161,30 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("smartCastOnWhenSubjectAfterCheckInBranchLV13.kt") + public void testSmartCastOnWhenSubjectAfterCheckInBranchLV13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastOnWhenSubjectAfterCheckInBranchLV13.kt"); + doTest(fileName); + } + @TestMetadata("smartCastToDifferentTypes.kt") public void testSmartCastToDifferentTypes() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastToDifferentTypes.kt"); doTest(fileName); } + @TestMetadata("smartCastToDifferentTypesLV13.kt") + public void testSmartCastToDifferentTypesLV13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastToDifferentTypesLV13.kt"); + doTest(fileName); + } + + @TestMetadata("smartCastToDifferentTypesWithNumericPromotionLV13.kt") + public void testSmartCastToDifferentTypesWithNumericPromotionLV13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastToDifferentTypesWithNumericPromotionLV13.kt"); + doTest(fileName); + } + @TestMetadata("when.kt") public void testWhen() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/when.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 9a8ddfa67c8..4efaa8cedd8 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -9993,6 +9993,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("comparableToTWithTLV13.kt") + public void testComparableToTWithTLV13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/comparableToTWithTLV13.kt"); + doTest(fileName); + } + @TestMetadata("comparableTypeCast.kt") public void testComparableTypeCast() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/comparableTypeCast.kt"); @@ -10101,6 +10107,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("nullableDoubleEqualsLV13.kt") + public void testNullableDoubleEqualsLV13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/nullableDoubleEqualsLV13.kt"); + doTest(fileName); + } + @TestMetadata("nullableDoubleNotEquals.kt") public void testNullableDoubleNotEquals() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/nullableDoubleNotEquals.kt"); @@ -10149,12 +10161,30 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("smartCastOnWhenSubjectAfterCheckInBranchLV13.kt") + public void testSmartCastOnWhenSubjectAfterCheckInBranchLV13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastOnWhenSubjectAfterCheckInBranchLV13.kt"); + doTest(fileName); + } + @TestMetadata("smartCastToDifferentTypes.kt") public void testSmartCastToDifferentTypes() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastToDifferentTypes.kt"); doTest(fileName); } + @TestMetadata("smartCastToDifferentTypesLV13.kt") + public void testSmartCastToDifferentTypesLV13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastToDifferentTypesLV13.kt"); + doTest(fileName); + } + + @TestMetadata("smartCastToDifferentTypesWithNumericPromotionLV13.kt") + public void testSmartCastToDifferentTypesWithNumericPromotionLV13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastToDifferentTypesWithNumericPromotionLV13.kt"); + doTest(fileName); + } + @TestMetadata("when.kt") public void testWhen() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/when.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index dd9879cf479..65bad5e2090 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -9993,6 +9993,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("comparableToTWithTLV13.kt") + public void testComparableToTWithTLV13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/comparableToTWithTLV13.kt"); + doTest(fileName); + } + @TestMetadata("comparableTypeCast.kt") public void testComparableTypeCast() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/comparableTypeCast.kt"); @@ -10101,6 +10107,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("nullableDoubleEqualsLV13.kt") + public void testNullableDoubleEqualsLV13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/nullableDoubleEqualsLV13.kt"); + doTest(fileName); + } + @TestMetadata("nullableDoubleNotEquals.kt") public void testNullableDoubleNotEquals() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/nullableDoubleNotEquals.kt"); @@ -10149,12 +10161,30 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("smartCastOnWhenSubjectAfterCheckInBranchLV13.kt") + public void testSmartCastOnWhenSubjectAfterCheckInBranchLV13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastOnWhenSubjectAfterCheckInBranchLV13.kt"); + doTest(fileName); + } + @TestMetadata("smartCastToDifferentTypes.kt") public void testSmartCastToDifferentTypes() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastToDifferentTypes.kt"); doTest(fileName); } + @TestMetadata("smartCastToDifferentTypesLV13.kt") + public void testSmartCastToDifferentTypesLV13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastToDifferentTypesLV13.kt"); + doTest(fileName); + } + + @TestMetadata("smartCastToDifferentTypesWithNumericPromotionLV13.kt") + public void testSmartCastToDifferentTypesWithNumericPromotionLV13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastToDifferentTypesWithNumericPromotionLV13.kt"); + doTest(fileName); + } + @TestMetadata("when.kt") public void testWhen() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/when.kt"); diff --git a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt index ae636bcf9ea..a7f1ac8baf4 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/LanguageVersionSettings.kt @@ -66,6 +66,7 @@ enum class LanguageFeature( NestedClassesInAnnotations(KOTLIN_1_3), JvmStaticInInterface(KOTLIN_1_3), InlineClasses(KOTLIN_1_3), + ProperNumberComparisons(KOTLIN_1_3), StrictJavaNullabilityAssertions(sinceVersion = null, defaultState = State.DISABLED), diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 2203f466016..26d95ce8b0f 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -10917,6 +10917,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("comparableToTWithTLV13.kt") + public void testComparableToTWithTLV13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/comparableToTWithTLV13.kt"); + doTest(fileName); + } + @TestMetadata("comparableTypeCast.kt") public void testComparableTypeCast() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/comparableTypeCast.kt"); @@ -11049,6 +11055,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("nullableDoubleEqualsLV13.kt") + public void testNullableDoubleEqualsLV13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/nullableDoubleEqualsLV13.kt"); + doTest(fileName); + } + @TestMetadata("nullableDoubleNotEquals.kt") public void testNullableDoubleNotEquals() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/nullableDoubleNotEquals.kt"); @@ -11103,6 +11115,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); } + @TestMetadata("smartCastOnWhenSubjectAfterCheckInBranchLV13.kt") + public void testSmartCastOnWhenSubjectAfterCheckInBranchLV13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastOnWhenSubjectAfterCheckInBranchLV13.kt"); + doTest(fileName); + } + @TestMetadata("smartCastToDifferentTypes.kt") public void testSmartCastToDifferentTypes() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastToDifferentTypes.kt"); @@ -11115,6 +11133,24 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); } + @TestMetadata("smartCastToDifferentTypesLV13.kt") + public void testSmartCastToDifferentTypesLV13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastToDifferentTypesLV13.kt"); + doTest(fileName); + } + + @TestMetadata("smartCastToDifferentTypesWithNumericPromotionLV13.kt") + public void testSmartCastToDifferentTypesWithNumericPromotionLV13() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastToDifferentTypesWithNumericPromotionLV13.kt"); + try { + doTest(fileName); + } + catch (Throwable ignore) { + return; + } + throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that."); + } + @TestMetadata("when.kt") public void testWhen() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/when.kt");