JS: fix double compareTo behaviour for NaN and +-0 (KT-22723)
This commit is contained in:
+6
@@ -151,6 +151,12 @@ abstract class CommonCompilerArguments : CommonToolArguments() {
|
||||
)
|
||||
var useExperimental: Array<String>? by FreezableVar(null)
|
||||
|
||||
@Argument(
|
||||
value = "-Xproper-ieee754-comparisons",
|
||||
description = "Generate proper IEEE 754 comparisons in all cases if values are statically known to be of primitive numeric types"
|
||||
)
|
||||
var properIeee754Comparisons by FreezableVar(false)
|
||||
|
||||
open fun configureAnalysisFlags(collector: MessageCollector): MutableMap<AnalysisFlag<*>, Any> {
|
||||
return HashMap<AnalysisFlag<*>, Any>().apply {
|
||||
put(AnalysisFlag.skipMetadataVersionCheck, skipMetadataVersionCheck)
|
||||
|
||||
-6
@@ -224,12 +224,6 @@ class K2JVMCompilerArguments : CommonCompilerArguments() {
|
||||
)
|
||||
var noExceptionOnExplicitEqualsForBoxedNull by FreezableVar(false)
|
||||
|
||||
@Argument(
|
||||
value = "-Xproper-ieee754-comparisons",
|
||||
description = "Generate proper IEEE 754 comparisons in all cases if values are statically known to be of primitive numeric types"
|
||||
)
|
||||
var properIeee754Comparisons by FreezableVar(false)
|
||||
|
||||
// Paths to output directories for friend modules.
|
||||
var friendPaths: Array<String>? by FreezableVar(null)
|
||||
|
||||
|
||||
@@ -226,6 +226,9 @@ public abstract class CLICompiler<A extends CommonCompilerArguments> extends CLI
|
||||
extraLanguageFeatures.put(LanguageFeature.ReadDeserializedContracts, LanguageFeature.State.ENABLED);
|
||||
}
|
||||
|
||||
if (arguments.getProperIeee754Comparisons()) {
|
||||
extraLanguageFeatures.put(LanguageFeature.ProperIeee754Comparisons, LanguageFeature.State.ENABLED);
|
||||
}
|
||||
|
||||
setupPlatformSpecificLanguageFeatureSettings(extraLanguageFeatures, arguments);
|
||||
|
||||
|
||||
@@ -235,10 +235,6 @@ class K2JVMCompiler : CLICompiler<K2JVMCompilerArguments>() {
|
||||
extraLanguageFeatures[LanguageFeature.StrictJavaNullabilityAssertions] = LanguageFeature.State.ENABLED
|
||||
}
|
||||
|
||||
if (commandLineArguments.properIeee754Comparisons) {
|
||||
extraLanguageFeatures[LanguageFeature.ProperIeee754Comparisons] = LanguageFeature.State.ENABLED
|
||||
}
|
||||
|
||||
super.setupPlatformSpecificLanguageFeatureSettings(extraLanguageFeatures, commandLineArguments)
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -15,6 +15,7 @@ where advanced options include:
|
||||
-Xno-check-actual Do not check presence of 'actual' modifier in multi-platform projects
|
||||
-Xno-inline Disable method inlining
|
||||
-Xplugin=<path> Load plugins from the given classpath
|
||||
-Xproper-ieee754-comparisons Generate proper IEEE 754 comparisons in all cases if values are statically known to be of primitive numeric types
|
||||
-Xread-deserialized-contracts Enable reading of contracts from metadata
|
||||
-Xrepeat=<count> Repeat compilation (for performance analysis)
|
||||
-Xreport-output-files Report source to output files mapping
|
||||
|
||||
+1
-1
@@ -28,7 +28,6 @@ where advanced options include:
|
||||
-Xno-optimize Disable optimizations
|
||||
-Xno-param-assertions Don't generate not-null assertions on parameters of methods accessible from Java
|
||||
-Xno-receiver-assertions Don't generate not-null assertion for extension receiver arguments of platform types
|
||||
-Xproper-ieee754-comparisons Generate proper IEEE 754 comparisons in all cases if values are statically known to be of primitive numeric types
|
||||
-Xreport-perf Report detailed performance statistics
|
||||
-Xscript-resolver-environment=<key=value[,]>
|
||||
Script resolver environment in key-value pairs (the value could be quoted and escaped)
|
||||
@@ -53,6 +52,7 @@ where advanced options include:
|
||||
-Xno-check-actual Do not check presence of 'actual' modifier in multi-platform projects
|
||||
-Xno-inline Disable method inlining
|
||||
-Xplugin=<path> Load plugins from the given classpath
|
||||
-Xproper-ieee754-comparisons Generate proper IEEE 754 comparisons in all cases if values are statically known to be of primitive numeric types
|
||||
-Xread-deserialized-contracts Enable reading of contracts from metadata
|
||||
-Xrepeat=<count> Repeat compilation (for performance analysis)
|
||||
-Xreport-output-files Report source to output files mapping
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// IGNORE_BACKEND: JS, NATIVE
|
||||
// IGNORE_BACKEND: NATIVE
|
||||
|
||||
inline fun ltx(a: Comparable<Any>, b: Any) = a < b
|
||||
inline fun lex(a: Comparable<Any>, b: Any) = a <= b
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS
|
||||
// IGNORE_BACKEND: NATIVE
|
||||
|
||||
fun equals1(a: Double, b: Double) = a.equals(b)
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
val minus: Any = -0.0
|
||||
|
||||
fun box(): String {
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: +ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
val minus: Any = -0.0
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS
|
||||
fun box(): String {
|
||||
if ((-0.0 as Comparable<Double>) >= 0.0) return "fail 0"
|
||||
if ((-0.0F as Comparable<Float>) >= 0.0F) return "fail 1"
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
fun box(): String {
|
||||
if (0.toByte().compareTo(-0.0) != 1) return "fail 1.1"
|
||||
if (0.toByte().compareTo(-0.0F) != 1) return "fail 1.2"
|
||||
if (0.toByte().compareTo(Double.NaN) != -1) return "fail 1.3"
|
||||
if (0.toByte().compareTo(Float.NaN) != -1) return "fail 1.4"
|
||||
|
||||
if (0.toShort().compareTo(-0.0) != 1) return "fail 2.1"
|
||||
if (0.toShort().compareTo(-0.0F) != 1) return "fail 2.2"
|
||||
if (0.toShort().compareTo(Double.NaN) != -1) return "fail 2.3"
|
||||
if (0.toShort().compareTo(Float.NaN) != -1) return "fail 2.4"
|
||||
|
||||
if (0.compareTo(-0.0) != 1) return "fail 3.1"
|
||||
if (0.compareTo(-0.0F) != 1) return "fail 3.2"
|
||||
if (0.compareTo(Double.NaN) != -1) return "fail 3.3"
|
||||
if (0.compareTo(Float.NaN) != -1) return "fail 3.4"
|
||||
|
||||
if (0.0F.compareTo(-0.0) != 1) return "fail 4.1"
|
||||
if (0.0F.compareTo(-0.0F) != 1) return "fail 4.2"
|
||||
if (0.0F.compareTo(Double.NaN) != -1) return "fail 4.3"
|
||||
if (0.0F.compareTo(Float.NaN) != -1) return "fail 4.4"
|
||||
|
||||
if (0.0.compareTo(-0.0) != 1) return "fail 5.1"
|
||||
if (0.0.compareTo(-0.0F) != 1) return "fail 5.2"
|
||||
if (0.0.compareTo(Double.NaN) != -1) return "fail 5.3"
|
||||
if (0.0.compareTo(Float.NaN) != -1) return "fail 5.4"
|
||||
|
||||
if (0L.compareTo(-0.0) != 1) return "fail 6.1"
|
||||
if (0L.compareTo(-0.0F) != 1) return "fail 6.2"
|
||||
if (0L.compareTo(Double.NaN) != -1) return "fail 6.3"
|
||||
if (0L.compareTo(Float.NaN) != -1) return "fail 6.4"
|
||||
|
||||
|
||||
if ((-0.0).compareTo(0.toByte()) != -1) return "fail 7.1"
|
||||
if ((-0.0).compareTo(0.toShort()) != -1) return "fail 7.2"
|
||||
if ((-0.0).compareTo(0) != -1) return "fail 7.3"
|
||||
if ((-0.0).compareTo(0.0F) != -1) return "fail 7.4"
|
||||
if ((-0.0).compareTo(0.0) != -1) return "fail 7.5"
|
||||
if ((-0.0).compareTo(0L) != -1) return "fail 7.6"
|
||||
|
||||
if ((-0.0F).compareTo(0.toByte()) != -1) return "fail 8.1"
|
||||
if ((-0.0F).compareTo(0.toShort()) != -1) return "fail 8.2"
|
||||
if ((-0.0F).compareTo(0) != -1) return "fail 8.3"
|
||||
if ((-0.0F).compareTo(0.0F) != -1) return "fail 8.4"
|
||||
if ((-0.0F).compareTo(0.0) != -1) return "fail 8.5"
|
||||
if ((-0.0F).compareTo(0L) != -1) return "fail 8.6"
|
||||
|
||||
if (Double.NaN.compareTo(0.toByte()) != 1) return "fail 9.1"
|
||||
if (Double.NaN.compareTo(0.toShort()) != 1) return "fail 9.2"
|
||||
if (Double.NaN.compareTo(0) != 1) return "fail 9.3"
|
||||
if (Double.NaN.compareTo(0.0F) != 1) return "fail 9.4"
|
||||
if (Double.NaN.compareTo(0.0) != 1) return "fail 9.5"
|
||||
if (Double.NaN.compareTo(0L) != 1) return "fail 9.6"
|
||||
|
||||
if (Float.NaN.compareTo(0.toByte()) != 1) return "fail 10.1"
|
||||
if (Float.NaN.compareTo(0.toShort()) != 1) return "fail 10.2"
|
||||
if (Float.NaN.compareTo(0) != 1) return "fail 10.3"
|
||||
if (Float.NaN.compareTo(0.0F) != 1) return "fail 10.4"
|
||||
if (Float.NaN.compareTo(0.0) != 1) return "fail 10.5"
|
||||
if (Float.NaN.compareTo(0L) != 1) return "fail 10.6"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
fun equals1(a: Double, b: Double) = a == b
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
fun equals1(a: Float, b: Float) = a == b
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// WITH_RUNTIME
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
import kotlin.test.*
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
fun equals1(a: Double, b: Double?) = a == b
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
fun equals1(a: Float, b: Float?) = a == b
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS
|
||||
fun less1(a: Double, b: Double) = a.compareTo(b) == -1
|
||||
|
||||
fun less2(a: Double?, b: Double?) = a!!.compareTo(b!!) == -1
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
fun equals1(a: Double, b: Double) = a.equals(b)
|
||||
|
||||
fun equals2(a: Double?, b: Double?) = a!!.equals(b!!)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
fun greater1(a: Double, b: Double) = a > b
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
fun greater1(a: Float, b: Float) = a > b
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
inline fun less(a: Comparable<Double>, b: Double): Boolean {
|
||||
return a < b
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
fun less1(a: Double, b: Double) = a < b
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
fun less1(a: Float, b: Float) = a < b
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS
|
||||
fun box(): String {
|
||||
val plusZero: Double? = 0.0
|
||||
val minusZero: Double = -0.0
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS
|
||||
fun box(): String {
|
||||
val zero: Any = 0.0
|
||||
val floatZero: Any = -0.0F
|
||||
|
||||
-2
@@ -1,5 +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
|
||||
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: +ProperIeee754Comparisons
|
||||
// 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
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
fun box(): String {
|
||||
val plusZero: Any = 0.0
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// LANGUAGE_VERSION: 1.0
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
fun box(): String {
|
||||
val plusZero: Any = 0.0
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JS
|
||||
|
||||
fun box(): String {
|
||||
val plusZero: Any = 0.0
|
||||
|
||||
Vendored
-1
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JS
|
||||
// WITH_RUNTIME
|
||||
|
||||
val DOUBLE_RANGE = 0.0 .. -0.0
|
||||
|
||||
Generated
+6
@@ -10125,6 +10125,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("differentTypesComparison.kt")
|
||||
public void testDifferentTypesComparison() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/differentTypesComparison.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsDouble.kt")
|
||||
public void testEqualsDouble() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/equalsDouble.kt");
|
||||
|
||||
+6
@@ -10125,6 +10125,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("differentTypesComparison.kt")
|
||||
public void testDifferentTypesComparison() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/differentTypesComparison.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsDouble.kt")
|
||||
public void testEqualsDouble() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/equalsDouble.kt");
|
||||
|
||||
+6
@@ -10125,6 +10125,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("differentTypesComparison.kt")
|
||||
public void testDifferentTypesComparison() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/differentTypesComparison.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsDouble.kt")
|
||||
public void testEqualsDouble() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/equalsDouble.kt");
|
||||
|
||||
@@ -946,6 +946,10 @@ public abstract class KotlinBuiltIns {
|
||||
return isConstructedFromGivenClassAndNotNullable(type, FQ_NAMES._long);
|
||||
}
|
||||
|
||||
public static boolean isLongOrNullableLong(@NotNull KotlinType type) {
|
||||
return isConstructedFromGivenClass(type, FQ_NAMES._long);
|
||||
}
|
||||
|
||||
public static boolean isShort(@NotNull KotlinType type) {
|
||||
return isConstructedFromGivenClassAndNotNullable(type, FQ_NAMES._short);
|
||||
}
|
||||
|
||||
+31
-175
@@ -1122,13 +1122,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("boxedRealsCmp.kt")
|
||||
public void testBoxedRealsCmp() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/boxedRealsCmp.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("casts.kt")
|
||||
@@ -1152,13 +1146,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("explicitEqualsOnDouble.kt")
|
||||
public void testExplicitEqualsOnDouble() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/explicitEqualsOnDouble.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("fold.kt")
|
||||
@@ -11028,25 +11016,13 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("asComparableToDouble.kt")
|
||||
public void testAsComparableToDouble() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/asComparableToDouble.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("asComparableToDouble_properIeeeComparisons.kt")
|
||||
public void testAsComparableToDouble_properIeeeComparisons() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/asComparableToDouble_properIeeeComparisons.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("comparableToTWithT_properIeeeComparisons.kt")
|
||||
@@ -11058,13 +11034,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("comparableTypeCast.kt")
|
||||
public void testComparableTypeCast() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/comparableTypeCast.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("dataClass.kt")
|
||||
@@ -11073,16 +11043,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("differentTypesComparison.kt")
|
||||
public void testDifferentTypesComparison() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/differentTypesComparison.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsDouble.kt")
|
||||
public void testEqualsDouble() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/equalsDouble.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsDouble_properIeeeComparisons.kt")
|
||||
@@ -11094,13 +11064,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("equalsFloat.kt")
|
||||
public void testEqualsFloat() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/equalsFloat.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsFloat_properIeeeComparisons.kt")
|
||||
@@ -11112,13 +11076,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("equalsNaN.kt")
|
||||
public void testEqualsNaN() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/equalsNaN.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsNaN_properIeeeComparisons.kt")
|
||||
@@ -11130,13 +11088,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("equalsNullableDouble.kt")
|
||||
public void testEqualsNullableDouble() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/equalsNullableDouble.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsNullableDouble_properIeeeComparisons.kt")
|
||||
@@ -11148,13 +11100,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("equalsNullableFloat.kt")
|
||||
public void testEqualsNullableFloat() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/equalsNullableFloat.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("equalsNullableFloat_properIeeeComparisons.kt")
|
||||
@@ -11166,25 +11112,13 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("explicitCompareCall.kt")
|
||||
public void testExplicitCompareCall() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/explicitCompareCall.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("explicitEqualsCall.kt")
|
||||
public void testExplicitEqualsCall() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/explicitEqualsCall.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("generic.kt")
|
||||
@@ -11196,13 +11130,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("greaterDouble.kt")
|
||||
public void testGreaterDouble() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/greaterDouble.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("greaterDouble_properIeeeComparisons.kt")
|
||||
@@ -11214,13 +11142,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("greaterFloat.kt")
|
||||
public void testGreaterFloat() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/greaterFloat.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("greaterFloat_properIeeeComparisons.kt")
|
||||
@@ -11232,25 +11154,13 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("inline.kt")
|
||||
public void testInline() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/inline.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("lessDouble.kt")
|
||||
public void testLessDouble() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/lessDouble.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("lessDouble_properIeeeComparisons.kt")
|
||||
@@ -11262,13 +11172,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("lessFloat.kt")
|
||||
public void testLessFloat() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/lessFloat.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("lessFloat_properIeeeComparisons.kt")
|
||||
@@ -11346,13 +11250,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("safeCall.kt")
|
||||
public void testSafeCall() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/safeCall.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("smartCastOnWhenSubjectAfterCheckInBranch_properIeeeComparisons.kt")
|
||||
@@ -11364,37 +11262,19 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("smartCastToDifferentTypes.kt")
|
||||
public void testSmartCastToDifferentTypes() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastToDifferentTypes.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("smartCastToDifferentTypesWithNumericPromotion.kt")
|
||||
public void testSmartCastToDifferentTypesWithNumericPromotion() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastToDifferentTypesWithNumericPromotion.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("smartCastToDifferentTypesWithNumericPromotion_properIeeeComparisons.kt")
|
||||
public void testSmartCastToDifferentTypesWithNumericPromotion_properIeeeComparisons() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/smartCastToDifferentTypesWithNumericPromotion_properIeeeComparisons.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("smartCastToDifferentTypes_properIeeeComparisons.kt")
|
||||
@@ -11412,25 +11292,13 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("when.kt")
|
||||
public void testWhen() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/when.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("when10.kt")
|
||||
public void testWhen10() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/when10.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("when10_properIeeeComparisons.kt")
|
||||
@@ -11442,13 +11310,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("whenNoSubject.kt")
|
||||
public void testWhenNoSubject() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ieee754/whenNoSubject.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("whenNoSubject_properIeeeComparisons.kt")
|
||||
@@ -16823,13 +16685,7 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
||||
@TestMetadata("inDoubleRangeLiteralVsComparableRangeLiteral.kt")
|
||||
public void testInDoubleRangeLiteralVsComparableRangeLiteral() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inDoubleRangeLiteralVsComparableRangeLiteral.kt");
|
||||
try {
|
||||
doTest(fileName);
|
||||
}
|
||||
catch (Throwable ignore) {
|
||||
return;
|
||||
}
|
||||
throw new AssertionError("Looks like this test can be unmuted. Remove IGNORE_BACKEND directive for that.");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("inExtensionRange.kt")
|
||||
|
||||
+16
-8
@@ -34,16 +34,14 @@ import org.jetbrains.kotlin.js.translate.general.Translation;
|
||||
import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.ArrayFIF;
|
||||
import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.TopLevelFIF;
|
||||
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator;
|
||||
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils;
|
||||
import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
|
||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
|
||||
import org.jetbrains.kotlin.js.translate.utils.*;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpressionWithTypeRHS;
|
||||
import org.jetbrains.kotlin.psi.KtExpression;
|
||||
import org.jetbrains.kotlin.psi.KtIsExpression;
|
||||
import org.jetbrains.kotlin.psi.KtTypeReference;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.checkers.PrimitiveNumericComparisonInfo;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -272,16 +270,26 @@ public final class PatternTranslator extends AbstractTranslator {
|
||||
|
||||
@NotNull
|
||||
public JsExpression translateExpressionPattern(
|
||||
@NotNull KotlinType type,
|
||||
@NotNull KtExpression subjectExpression,
|
||||
@NotNull JsExpression expressionToMatch,
|
||||
@NotNull KtExpression patternExpression
|
||||
) {
|
||||
JsExpression expressionToMatchAgainst = translateExpressionForExpressionPattern(patternExpression);
|
||||
KotlinType patternType = BindingUtils.getTypeForExpression(bindingContext(), patternExpression);
|
||||
PrimitiveNumericComparisonInfo ieeeInfo = UtilsKt.getPrimitiveNumericComparisonInfo(context(), patternExpression);
|
||||
|
||||
EqualityType matchEquality = equalityType(type);
|
||||
KotlinType subjectType, patternType;
|
||||
if (ieeeInfo != null) {
|
||||
subjectType = ieeeInfo.getLeftType();
|
||||
patternType = ieeeInfo.getRightType();
|
||||
} else {
|
||||
subjectType = UtilsKt.getPrecisePrimitiveTypeNotNull(context(), subjectExpression);
|
||||
patternType = UtilsKt.getPrecisePrimitiveTypeNotNull(context(), patternExpression);
|
||||
}
|
||||
|
||||
EqualityType matchEquality = equalityType(subjectType);
|
||||
EqualityType patternEquality = equalityType(patternType);
|
||||
|
||||
JsExpression expressionToMatchAgainst = translateExpressionForExpressionPattern(patternExpression);
|
||||
|
||||
if (matchEquality == EqualityType.PRIMITIVE && patternEquality == EqualityType.PRIMITIVE) {
|
||||
return equality(expressionToMatch, expressionToMatchAgainst);
|
||||
}
|
||||
|
||||
@@ -326,10 +326,8 @@ private constructor(private val whenExpression: KtWhenExpression, context: Trans
|
||||
val patternTranslator = Translation.patternTranslator(context)
|
||||
return if (expressionToMatch == null) {
|
||||
patternTranslator.translateExpressionForExpressionPattern(patternExpression)
|
||||
}
|
||||
else {
|
||||
val type = bindingContext().getType(whenExpression.subjectExpression!!)!!
|
||||
patternTranslator.translateExpressionPattern(type, expressionToMatch, patternExpression)
|
||||
} else {
|
||||
patternTranslator.translateExpressionPattern(whenExpression.subjectExpression!!, expressionToMatch, patternExpression)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -59,7 +59,7 @@ object LongOperationFIF : FunctionIntrinsicFactory {
|
||||
|
||||
private val floatBinaryIntrinsics: Map<String, BaseBinaryIntrinsic> =
|
||||
mapOf(
|
||||
"compareTo" to BaseBinaryIntrinsic(::primitiveCompareTo),
|
||||
"compareTo" to BaseBinaryIntrinsic(::compareTo),
|
||||
"plus" to BaseBinaryIntrinsic(::sum),
|
||||
"minus" to BaseBinaryIntrinsic(::subtract),
|
||||
"times" to BaseBinaryIntrinsic(::mul),
|
||||
|
||||
+7
-2
@@ -110,8 +110,11 @@ public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory {
|
||||
private static final DescriptorPredicate PRIMITIVE_NUMBERS_BINARY_OPERATIONS =
|
||||
pattern(NamePredicate.PRIMITIVE_NUMBERS_MAPPED_TO_PRIMITIVE_JS, BINARY_OPERATIONS);
|
||||
|
||||
private static final DescriptorPredicate PRIMITIVE_INTEGRAL_NUMBERS_COMPARE_TO_INTEGRAL_OPERATIONS =
|
||||
pattern("Byte|Short|Int.compareTo(Byte|Short|Int)");
|
||||
private static final DescriptorPredicate PRIMITIVE_NUMBERS_COMPARE_TO_OPERATIONS =
|
||||
pattern(NamePredicate.PRIMITIVE_NUMBERS_MAPPED_TO_PRIMITIVE_JS, "compareTo");
|
||||
|
||||
private static final Predicate<FunctionDescriptor> INT_WITH_BIT_OPERATIONS = pattern("Int.or|and|xor|shl|shr|ushr")
|
||||
.or(pattern("Short|Byte.or|and|xor"));
|
||||
private static final DescriptorPredicate BOOLEAN_OPERATIONS = pattern("Boolean.or|and|xor");
|
||||
@@ -140,10 +143,12 @@ public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory {
|
||||
return new RangeToIntrinsic(descriptor);
|
||||
}
|
||||
|
||||
if (PRIMITIVE_NUMBERS_COMPARE_TO_OPERATIONS.test(descriptor)) {
|
||||
if (PRIMITIVE_INTEGRAL_NUMBERS_COMPARE_TO_INTEGRAL_OPERATIONS.test(descriptor)) {
|
||||
return PRIMITIVE_NUMBER_COMPARE_TO_INTRINSIC;
|
||||
}
|
||||
|
||||
if (PRIMITIVE_NUMBERS_COMPARE_TO_OPERATIONS.test(descriptor)) {
|
||||
return BUILTINS_COMPARE_TO_INTRINSIC;
|
||||
}
|
||||
|
||||
if (KotlinBuiltIns.isBuiltIn(descriptor) && descriptor.getName().equals(OperatorNameConventions.COMPARE_TO)) {
|
||||
return BUILTINS_COMPARE_TO_INTRINSIC;
|
||||
|
||||
+8
-13
@@ -21,7 +21,6 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsBinaryOperation
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsExpression
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsIntLiteral
|
||||
import org.jetbrains.kotlin.js.patterns.PatternBuilder.pattern
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.js.translate.operation.OperatorTable
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
|
||||
@@ -33,10 +32,6 @@ import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
|
||||
object CompareToBOIF : BinaryOperationIntrinsicFactory {
|
||||
val COMPARE_TO_CHAR = pattern("Int|Short|Byte|Double|Float.compareTo(Char)")
|
||||
val CHAR_COMPARE_TO = pattern("Char.compareTo(Int|Short|Byte|Double|Float)")
|
||||
val PRIMITIVE_COMPARE_TO = pattern("Int|Short|Byte|Double|Float|Char|String|Boolean.compareTo")
|
||||
|
||||
private object CompareToIntrinsic : AbstractBinaryOperationIntrinsic() {
|
||||
override fun apply(expression: KtBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression {
|
||||
val operator = OperatorTable.getBinaryOperator(getOperationToken(expression))
|
||||
@@ -71,17 +66,17 @@ object CompareToBOIF : BinaryOperationIntrinsicFactory {
|
||||
override fun getIntrinsic(descriptor: FunctionDescriptor, leftType: KotlinType?, rightType: KotlinType?): BinaryOperationIntrinsic? {
|
||||
if (descriptor.isDynamic()) return CompareToIntrinsic
|
||||
|
||||
if (leftType == null || rightType == null) return null
|
||||
|
||||
if (!KotlinBuiltIns.isBuiltIn(descriptor)) return null
|
||||
|
||||
// Types may be nullable if properIeeeComparisons are switched off, e.g. fun foo(a: Double?) = a != null && a < 0.0
|
||||
return when {
|
||||
COMPARE_TO_CHAR.test(descriptor) ->
|
||||
CompareToCharIntrinsic
|
||||
CHAR_COMPARE_TO.test(descriptor) ->
|
||||
CompareCharToPrimitiveIntrinsic
|
||||
PRIMITIVE_COMPARE_TO.test(descriptor) ->
|
||||
CompareToIntrinsic
|
||||
else ->
|
||||
CompareToFunctionIntrinsic
|
||||
KotlinBuiltIns.isCharOrNullableChar(rightType) -> CompareToCharIntrinsic
|
||||
KotlinBuiltIns.isCharOrNullableChar(leftType) -> CompareCharToPrimitiveIntrinsic
|
||||
KotlinBuiltIns.isPrimitiveTypeOrNullablePrimitiveType(leftType) &&
|
||||
KotlinBuiltIns.isPrimitiveTypeOrNullablePrimitiveType(rightType) -> CompareToIntrinsic
|
||||
else -> CompareToFunctionIntrinsic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+26
-49
@@ -18,7 +18,6 @@ package org.jetbrains.kotlin.js.translate.intrinsic.operation
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsBinaryOperation
|
||||
import org.jetbrains.kotlin.js.backend.ast.JsBinaryOperator
|
||||
@@ -31,11 +30,8 @@ import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getOperationToken
|
||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfoBefore
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactoryImpl
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.expressions.OperatorConventions
|
||||
@@ -46,9 +42,8 @@ import java.util.*
|
||||
object EqualsBOIF : BinaryOperationIntrinsicFactory {
|
||||
private object EqualsIntrinsic : AbstractBinaryOperationIntrinsic() {
|
||||
|
||||
// Primitives with no boxing or tricky NaN behavior, represented by a Number at runtime.
|
||||
// Whenever the left side of equality is of this type, equality could be tested via '==='.
|
||||
private val SIMPLE_PRIMITIVES = EnumSet.of(PrimitiveType.BYTE, PrimitiveType.SHORT, PrimitiveType.INT)
|
||||
private val JS_NUMBER_PRIMITIVES =
|
||||
EnumSet.of(PrimitiveType.BYTE, PrimitiveType.SHORT, PrimitiveType.INT, PrimitiveType.DOUBLE, PrimitiveType.FLOAT)
|
||||
|
||||
override fun apply(expression: KtBinaryExpression, left: JsExpression, right: JsExpression, context: TranslationContext): JsExpression {
|
||||
val isNegated = expression.isNegated()
|
||||
@@ -60,25 +55,38 @@ object EqualsBOIF : BinaryOperationIntrinsicFactory {
|
||||
return TranslationUtils.nullCheck(coercedSubject, isNegated)
|
||||
}
|
||||
|
||||
val ktLeft = checkNotNull(expression.left) { "No left-hand side: " + expression.text }
|
||||
val ktRight = checkNotNull(expression.right) { "No right-hand side: " + expression.text }
|
||||
val leftKotlinType = getRefinedType(ktLeft, context)
|
||||
val rightKotlinType = getRefinedType(ktRight, context)
|
||||
val (leftKotlinType, rightKotlinType) = binaryOperationTypes(expression, context)
|
||||
|
||||
val leftType = leftKotlinType?.let { KotlinBuiltIns.getPrimitiveType(it) }
|
||||
val rightType = rightKotlinType?.let { KotlinBuiltIns.getPrimitiveType(it) }
|
||||
|
||||
if (leftType != null && (leftType in SIMPLE_PRIMITIVES || leftType == rightType && leftType != PrimitiveType.LONG)) {
|
||||
if (leftType != null && rightType != null && (
|
||||
leftType in JS_NUMBER_PRIMITIVES && rightType in JS_NUMBER_PRIMITIVES ||
|
||||
leftType in JS_NUMBER_PRIMITIVES && rightType == PrimitiveType.LONG ||
|
||||
leftType == PrimitiveType.LONG && rightType in JS_NUMBER_PRIMITIVES ||
|
||||
leftType == PrimitiveType.BOOLEAN && rightType == PrimitiveType.BOOLEAN ||
|
||||
leftType == PrimitiveType.CHAR && rightType == PrimitiveType.CHAR
|
||||
)) {
|
||||
val useEq = leftType == PrimitiveType.LONG || rightType == PrimitiveType.LONG
|
||||
|
||||
val operator = when {
|
||||
useEq && isNegated -> JsBinaryOperator.NEQ
|
||||
useEq && !isNegated -> JsBinaryOperator.EQ
|
||||
!useEq && isNegated -> JsBinaryOperator.REF_NEQ
|
||||
else /* !useEq && !isNegated */ -> JsBinaryOperator.REF_EQ
|
||||
}
|
||||
|
||||
val coercedLeft = TranslationUtils.coerce(context, left, leftKotlinType)
|
||||
val coercedRight = TranslationUtils.coerce(context, right, rightKotlinType!!)
|
||||
return JsBinaryOperation(if (isNegated) JsBinaryOperator.REF_NEQ else JsBinaryOperator.REF_EQ, coercedLeft, coercedRight)
|
||||
val coercedRight = TranslationUtils.coerce(context, right, rightKotlinType)
|
||||
return JsBinaryOperation(operator, coercedLeft, coercedRight)
|
||||
}
|
||||
|
||||
val resolvedCall = expression.getResolvedCall(context.bindingContext())
|
||||
val appliedToDynamic =
|
||||
resolvedCall != null &&
|
||||
with(resolvedCall.dispatchReceiver) {
|
||||
if (this != null) type.isDynamic() else false
|
||||
}
|
||||
resolvedCall != null &&
|
||||
with(resolvedCall.dispatchReceiver) {
|
||||
if (this != null) type.isDynamic() else false
|
||||
}
|
||||
|
||||
if (appliedToDynamic) {
|
||||
return JsBinaryOperation(if (isNegated) JsBinaryOperator.NEQ else JsBinaryOperator.EQ, left, right)
|
||||
@@ -89,37 +97,6 @@ object EqualsBOIF : BinaryOperationIntrinsicFactory {
|
||||
val result = TopLevelFIF.KOTLIN_EQUALS.apply(coercedLeft, listOf(coercedRight), context)
|
||||
return if (isNegated) JsAstUtils.not(result) else result
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to get as precise statically known primitive type as possible - taking smart-casts and generic supertypes into account.
|
||||
* This is needed to be compatible with JVM NaN behaviour, in particular the following two cases.
|
||||
*
|
||||
* // Smart-casts
|
||||
* val a: Any = Double.NaN
|
||||
* println(a == a) // true
|
||||
* if (a is Double) {
|
||||
* println(a == a) // false
|
||||
* }
|
||||
*
|
||||
* // Generics with Double super-type
|
||||
* fun <T: Double> foo(v: T) = println(v == v)
|
||||
* foo(Double.NaN) // false
|
||||
*
|
||||
* Also see org/jetbrains/kotlin/codegen/codegenUtil.kt#calcTypeForIEEE754ArithmeticIfNeeded
|
||||
*/
|
||||
private fun getRefinedType(expression: KtExpression, context: TranslationContext): KotlinType? {
|
||||
val bindingContext = context.bindingContext()
|
||||
val descriptor = context.declarationDescriptor ?: context.currentModule
|
||||
val ktType = bindingContext.getType(expression) ?: return null
|
||||
|
||||
val dataFlow = DataFlowValueFactoryImpl().createDataFlowValue(expression, ktType, bindingContext, descriptor)
|
||||
val isPrimitiveFn = KotlinBuiltIns::isPrimitiveTypeOrNullablePrimitiveType
|
||||
|
||||
val languageVersionSettings = context.config.configuration.languageVersionSettings
|
||||
return bindingContext.getDataFlowInfoBefore(expression).getStableTypes(dataFlow, languageVersionSettings).find(isPrimitiveFn) ?: // Smart-casts
|
||||
TypeUtils.getAllSupertypes(ktType).find(isPrimitiveFn) ?: // Generic super-types
|
||||
ktType // Default
|
||||
}
|
||||
}
|
||||
|
||||
object EnumEqualsIntrinsic : AbstractBinaryOperationIntrinsic() {
|
||||
|
||||
+12
-2
@@ -21,6 +21,8 @@ import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression
|
||||
import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getOperationToken
|
||||
import org.jetbrains.kotlin.js.translate.utils.getPrecisePrimitiveType
|
||||
import org.jetbrains.kotlin.js.translate.utils.getPrimitiveNumericComparisonInfo
|
||||
import org.jetbrains.kotlin.lexer.KtToken
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
@@ -45,8 +47,7 @@ class BinaryOperationIntrinsics {
|
||||
return NO_INTRINSIC
|
||||
}
|
||||
|
||||
val leftType = expression.left?.let { context.bindingContext().getType(it) }
|
||||
val rightType = expression.right?.let { context.bindingContext().getType(it) }
|
||||
val (leftType, rightType) = binaryOperationTypes(expression, context)
|
||||
|
||||
val key = IntrinsicKey(token, descriptor, leftType, rightType)
|
||||
return intrinsicCache.getOrPut(key) { computeIntrinsic(token, descriptor, leftType, rightType) }
|
||||
@@ -68,6 +69,15 @@ class BinaryOperationIntrinsics {
|
||||
}
|
||||
}
|
||||
|
||||
// Takes into account smart-casts (needed for IEEE 754 comparisons)
|
||||
fun binaryOperationTypes(expression: KtBinaryExpression, context: TranslationContext): Pair<KotlinType?, KotlinType?> {
|
||||
val info = context.getPrimitiveNumericComparisonInfo(expression)
|
||||
if (info != null) {
|
||||
return info.leftType to info.rightType
|
||||
}
|
||||
return expression.left?.let { context.getPrecisePrimitiveType(it) } to expression.right?.let { context.getPrecisePrimitiveType(it) }
|
||||
}
|
||||
|
||||
private data class IntrinsicKey(
|
||||
val token: KtToken, val function: FunctionDescriptor,
|
||||
val leftType: KotlinType?, val rightType: KotlinType?
|
||||
|
||||
@@ -19,6 +19,9 @@ package org.jetbrains.kotlin.js.translate.utils
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.util.SmartList
|
||||
import org.jetbrains.kotlin.backend.common.COROUTINE_SUSPENDED_NAME
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.languageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
@@ -31,15 +34,14 @@ import org.jetbrains.kotlin.js.translate.context.TranslationContext
|
||||
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsicWithReceiverComputed
|
||||
import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator
|
||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.simpleReturnFunction
|
||||
import org.jetbrains.kotlin.psi.KtBlockExpression
|
||||
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
|
||||
import org.jetbrains.kotlin.psi.KtFunctionLiteral
|
||||
import org.jetbrains.kotlin.psi.KtLambdaExpression
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.hasOrInheritsParametersWithDefaultValue
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
|
||||
fun generateDelegateCall(
|
||||
classDescriptor: ClassDescriptor,
|
||||
@@ -242,4 +244,34 @@ fun TranslationContext.createCoroutineResult(resolvedCall: ResolvedCall<*>): JsE
|
||||
coroutineResult = true
|
||||
synthetic = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to get precise statically known primitive type. Takes generic supertypes into account. Doesn't handle smart-casts.
|
||||
* This is needed to be compatible with JVM NaN behaviour:
|
||||
*
|
||||
* // Generics with Double super-type
|
||||
* fun <T: Double> foo(v: T) = println(v == v)
|
||||
* foo(Double.NaN) // false
|
||||
*
|
||||
* Also see org/jetbrains/kotlin/codegen/codegenUtil.kt#calcTypeForIEEE754ArithmeticIfNeeded
|
||||
*/
|
||||
fun TranslationContext.getPrecisePrimitiveType(expression: KtExpression): KotlinType? {
|
||||
val bindingContext = bindingContext()
|
||||
val ktType = bindingContext.getType(expression) ?: return null
|
||||
|
||||
return TypeUtils.getAllSupertypes(ktType).find(KotlinBuiltIns::isPrimitiveTypeOrNullablePrimitiveType) ?: ktType
|
||||
}
|
||||
|
||||
fun TranslationContext.getPrecisePrimitiveTypeNotNull(expression: KtExpression): KotlinType {
|
||||
return getPrecisePrimitiveType(expression) ?: throw IllegalStateException("Type must be not null for " + expression)
|
||||
}
|
||||
|
||||
fun TranslationContext.getPrimitiveNumericComparisonInfo(expression: KtExpression) =
|
||||
config.configuration.languageVersionSettings.let {
|
||||
if (it.supportsFeature(LanguageFeature.ProperIeee754Comparisons)) {
|
||||
bindingContext().get(BindingContext.PRIMITIVE_NUMERIC_COMPARISON_INFO, expression)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -130,5 +130,5 @@ Kotlin.arrayDeepHashCode = function (arr) {
|
||||
};
|
||||
|
||||
Kotlin.primitiveArraySort = function (array) {
|
||||
array.sort(Kotlin.primitiveCompareTo)
|
||||
array.sort(Kotlin.doubleCompareTo)
|
||||
};
|
||||
|
||||
@@ -31,6 +31,10 @@ Kotlin.equals = function (obj1, obj2) {
|
||||
return obj1.equals(obj2);
|
||||
}
|
||||
|
||||
if (typeof obj1 === "number" && typeof obj2 === "number") {
|
||||
return obj1 === obj2 && (obj1 !== 0 || 1 / obj1 === 1 / obj2)
|
||||
}
|
||||
|
||||
return obj1 === obj2;
|
||||
};
|
||||
|
||||
|
||||
@@ -16,14 +16,13 @@
|
||||
|
||||
Kotlin.compareTo = function (a, b) {
|
||||
var typeA = typeof a;
|
||||
var typeB = typeof a;
|
||||
if (Kotlin.isChar(a) && typeB === "number") {
|
||||
return Kotlin.primitiveCompareTo(a.charCodeAt(0), b);
|
||||
if (typeA === "number") {
|
||||
if (typeof b === "number") {
|
||||
return Kotlin.doubleCompareTo(a, b);
|
||||
}
|
||||
return Kotlin.primitiveCompareTo(a, b);
|
||||
}
|
||||
if (typeA === "number" && Kotlin.isChar(b)) {
|
||||
return Kotlin.primitiveCompareTo(a, b.charCodeAt(0));
|
||||
}
|
||||
if (typeA === "number" || typeA === "string" || typeA === "boolean") {
|
||||
if (typeA === "string" || typeA === "boolean") {
|
||||
return Kotlin.primitiveCompareTo(a, b);
|
||||
}
|
||||
return a.compareTo_11rb$(b);
|
||||
@@ -33,6 +32,20 @@ Kotlin.primitiveCompareTo = function (a, b) {
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
};
|
||||
|
||||
Kotlin.doubleCompareTo = function (a, b) {
|
||||
if (a < b) return -1;
|
||||
if (a > b) return 1;
|
||||
|
||||
if (a === b) {
|
||||
if (a !== 0) return 0;
|
||||
|
||||
var ia = 1 / a;
|
||||
return ia === 1 / b ? 0 : (ia < 0 ? -1 : 1);
|
||||
}
|
||||
|
||||
return a !== a ? (b !== b ? 0 : 1) : -1
|
||||
};
|
||||
|
||||
Kotlin.charInc = function (value) {
|
||||
return Kotlin.toChar(value+1);
|
||||
};
|
||||
|
||||
@@ -104,7 +104,6 @@ class NaNPropagationTest {
|
||||
|
||||
}
|
||||
|
||||
@JvmVersion
|
||||
class NaNTotalOrderTest {
|
||||
|
||||
private fun <T : Comparable<T>> totalOrderMinOf2(f2t: (T, T) -> T, function: String) {
|
||||
|
||||
Reference in New Issue
Block a user