Implement "proper numeric comparisons" support in JVM BE
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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<StackValue, StackValue> 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<StackValue, StackValue> 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<StackValue, StackValue> 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<StackValue, StackValue> 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<StackValue, StackValue> 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<StackValue, StackValue> 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<StackValue, StackValue> 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<StackValue, StackValue> 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);
|
||||
|
||||
@@ -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<VariableDescriptor>.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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -95,6 +95,7 @@ class InFloatingPointRangeLiteralExpressionGenerator(
|
||||
|
||||
}
|
||||
|
||||
// TODO evaluateOnce
|
||||
private fun introduceTemporaryIfRequired(v: InstructionAdapter, value: StackValue, type: Type): Pair<StackValue, Type?> {
|
||||
val resultValue: StackValue
|
||||
val resultType: Type?
|
||||
|
||||
@@ -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<KtFile>,
|
||||
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<KtFile>,
|
||||
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<KtFile>,
|
||||
private val configuration: CompilerConfiguration
|
||||
private val project: Project,
|
||||
private val builderFactory: ClassBuilderFactory,
|
||||
private val module: ModuleDescriptor,
|
||||
private val bindingContext: BindingContext,
|
||||
private val files: List<KtFile>,
|
||||
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<FqName>
|
||||
val obsoleteMultifileClasses: List<FqName>
|
||||
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<Diagnostic> = 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 <T> ClassBuilderFactory.wrapWith(elements: Iterable<T>, wrapper: (ClassBuilderFactory, T) -> ClassBuilderFactory): ClassBuilderFactory =
|
||||
elements.fold(this, wrapper)
|
||||
private inline fun <T> ClassBuilderFactory.wrapWith(
|
||||
elements: Iterable<T>,
|
||||
wrapper: (ClassBuilderFactory, T) -> ClassBuilderFactory
|
||||
): ClassBuilderFactory =
|
||||
elements.fold(this, wrapper)
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
// LANGUAGE_VERSION: 1.3
|
||||
|
||||
fun less(x: Comparable<Float>, y: Float) = x is Float && x < y
|
||||
fun less(x: Comparable<Double>, 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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
+25
@@ -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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
+76
@@ -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"
|
||||
}
|
||||
Generated
+30
@@ -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");
|
||||
|
||||
+30
@@ -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");
|
||||
|
||||
+30
@@ -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");
|
||||
|
||||
@@ -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),
|
||||
|
||||
|
||||
+36
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user