Intrinsify some mismatching range/element combinations for in/in!

It's safe to upcast integer types to Long,
floating-point types to Double.
So we don't have to create a range instance for cases such as

fun testLongInInt(x: Long, a: Int, b: Int) =
    x in a .. b

which is equivalent to

fun testLongInInt(x: Long, a: Int, b: Int) =
    x in a.toLong() .. b.toLong()
This commit is contained in:
Dmitry Petrov
2017-07-06 17:11:21 +03:00
parent d137b04b0a
commit 9d1901fc7c
10 changed files with 130 additions and 20 deletions
@@ -174,26 +174,28 @@ fun isPrimitiveRangeContains(descriptor: CallableDescriptor): Boolean {
return true
}
fun isIntPrimitiveRangeExtensionForInt(descriptor: CallableDescriptor): Boolean {
fun isPrimitiveNumberRangeExtensionContainsPrimitiveNumber(descriptor: CallableDescriptor): Boolean {
if (descriptor.name.asString() != "contains") return false
val extensionReceiverType = descriptor.extensionReceiverParameter?.type ?: return false
val extensionReceiverClassDescriptor = extensionReceiverType.constructor.declarationDescriptor as? ClassDescriptor ?: return false
if (!isTopLevelInPackage(extensionReceiverClassDescriptor, "ClosedRange", "kotlin.ranges")) return false
val rangeElementType = extensionReceiverType.arguments.singleOrNull()?.type ?: return false
if (!isIntPrimitiveType(rangeElementType)) return false
val rangeElementType = getRangeOrProgressionElementType(extensionReceiverType) ?: return false
if (!isPrimitiveNumberType(rangeElementType)) return false
val argumentType = descriptor.valueParameters.singleOrNull()?.type ?: return false
if (!isIntPrimitiveType(argumentType)) return false
if (!isPrimitiveNumberType(argumentType)) return false
return true
}
private fun isIntPrimitiveType(type: KotlinType) =
private fun isPrimitiveNumberType(type: KotlinType) =
KotlinBuiltIns.isByte(type) ||
KotlinBuiltIns.isShort(type) ||
KotlinBuiltIns.isInt(type)
KotlinBuiltIns.isInt(type) ||
KotlinBuiltIns.isChar(type) ||
KotlinBuiltIns.isLong(type) ||
KotlinBuiltIns.isFloat(type) ||
KotlinBuiltIns.isDouble(type)
fun isClosedFloatingPointRangeContains(descriptor: CallableDescriptor): Boolean {
if (descriptor.name.asString() != "contains") return false
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.codegen.range
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.range.comparison.ComparisonGenerator
import org.jetbrains.kotlin.codegen.range.comparison.getComparisonGeneratorForRangeContainsCall
import org.jetbrains.kotlin.codegen.range.inExpression.CallBasedInExpressionGenerator
import org.jetbrains.kotlin.codegen.range.inExpression.InContinuousRangeExpressionGenerator
@@ -33,7 +32,7 @@ abstract class PrimitiveNumberRangeIntrinsicRangeValue(rangeCall: ResolvedCall<o
resolvedCallForIn.resultingDescriptor.let {
isPrimitiveRangeContains(it) ||
isClosedFloatingPointRangeContains(it) ||
isIntPrimitiveRangeExtensionForInt(it)
isPrimitiveNumberRangeExtensionContainsPrimitiveNumber(it)
}
override fun createIntrinsicInExpressionGenerator(
@@ -0,0 +1,55 @@
// WITH_RUNTIME
fun inInt(x: Long): Boolean {
return x in 1..2
}
fun notInInt(x: Long): Boolean {
return x !in 1..2
}
fun inLong(x: Int): Boolean {
return x in 1L..2L
}
fun notInLong(x: Int): Boolean {
return x !in 1L..2L
}
fun inFloat(x: Double): Boolean {
return x in 1.0f..2.0f
}
fun notInFloat(x: Double): Boolean {
return x !in 1.0f..2.0f
}
fun inDouble(x: Float): Boolean {
return x in 1.0..2.0
}
fun notInDouble(x: Float): Boolean {
return x !in 1.0..2.0
}
fun box(): String {
return when {
!inInt(1L) -> "Fail !inInt"
inInt(0L) -> "Fail inInt"
notInInt(1L) -> "Fail notInInt"
!notInInt(0L) -> "Fail !notInInt"
!inLong(1) -> "Fail !inLong"
inLong(0) -> "Fail inLong"
notInLong(1) -> "Fail notInLong"
!notInLong(0) -> "Fail !notInLong"
!inFloat(1.0) -> "Fain !inFloat"
inFloat(0.0) -> "Fain inFloat"
notInFloat(1.0) -> "Fail notInFloat"
!notInFloat(0.0) -> "Fail !notInFloat"
!inDouble(1.0F) -> "Fail !inDouble"
inDouble(0.0F) -> "Fail inDouble"
notInDouble(1.0F) -> "Fail notInDouble"
!notInDouble(0.0F) -> "Fail !notInDouble"
else -> "OK"
}
}
@@ -16,10 +16,12 @@ fun inDouble(x: Float): Boolean {
return x in 1.0..2.0
}
// 2 INVOKESPECIAL
// 2 NEW
// 2 INVOKESTATIC kotlin/ranges/RangesKt.rangeTo
// 1 longRangeContains
// 1 intRangeContains
// 1 doubleRangeContains
// 1 floatRangeContains
// 3 I2L
// 3 F2D
// 0 INVOKESPECIAL
// 0 NEW
// 0 rangeTo
// 0 longRangeContains
// 0 intRangeContains
// 0 doubleRangeContains
// 0 floatRangeContains
@@ -0,0 +1,22 @@
// WITH_RUNTIME
fun byteInFloat(x: Byte, a: Float, b: Float) = x in a .. b
fun byteInDouble(x: Byte, a: Double, b: Double) = x in a .. b
fun shortInFloat(x: Short, a: Float, b: Float) = x in a .. b
fun shortInDouble(x: Short, a: Double, b: Double) = x in a .. b
fun intInFloat(x: Int, a: Float, b: Float) = x in a .. b
fun intInDouble(x: Int, a: Double, b: Double) = x in a .. b
fun longInFloat(x: Long, a: Float, b: Float) = x in a .. b
fun longInDouble(x: Long, a: Double, b: Double) = x in a .. b
fun floatInInt(x: Float, a: Int, b: Int) = x in a .. b
fun floatInLong(x: Float, a: Long, b: Long) = x in a .. b
fun doubleInInt(x: Double, a: Int, b: Int) = x in a .. b
fun doubleInLong(x: Double, a: Long, b: Long) = x in a .. b
// 4 INVOKESPECIAL
// 4 NEW
// 8 rangeTo
// 2 longRangeContains
// 2 intRangeContains
// 4 doubleRangeContains
// 4 floatRangeContains
@@ -13237,6 +13237,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
@TestMetadata("inNonMatchingRange.kt")
public void testInNonMatchingRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inNonMatchingRange.kt");
doTest(fileName);
}
@TestMetadata("inOptimizableDoubleRange.kt")
public void testInOptimizableDoubleRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inOptimizableDoubleRange.kt");
@@ -13237,6 +13237,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("inNonMatchingRange.kt")
public void testInNonMatchingRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inNonMatchingRange.kt");
doTest(fileName);
}
@TestMetadata("inOptimizableDoubleRange.kt")
public void testInOptimizableDoubleRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inOptimizableDoubleRange.kt");
@@ -1889,9 +1889,15 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
doTest(fileName);
}
@TestMetadata("inNonMatchingRange.kt")
public void testInNonMatchingRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/ranges/inNonMatchingRange.kt");
@TestMetadata("inNonMatchingRangeIntrinsified.kt")
public void testInNonMatchingRangeIntrinsified() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/ranges/inNonMatchingRangeIntrinsified.kt");
doTest(fileName);
}
@TestMetadata("inNonMatchingRangeNotIntrinsified.kt")
public void testInNonMatchingRangeNotIntrinsified() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/ranges/inNonMatchingRangeNotIntrinsified.kt");
doTest(fileName);
}
@@ -13237,6 +13237,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
doTest(fileName);
}
@TestMetadata("inNonMatchingRange.kt")
public void testInNonMatchingRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inNonMatchingRange.kt");
doTest(fileName);
}
@TestMetadata("inOptimizableDoubleRange.kt")
public void testInOptimizableDoubleRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inOptimizableDoubleRange.kt");
@@ -14869,6 +14869,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
doTest(fileName);
}
@TestMetadata("inNonMatchingRange.kt")
public void testInNonMatchingRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inNonMatchingRange.kt");
doTest(fileName);
}
@TestMetadata("inOptimizableDoubleRange.kt")
public void testInOptimizableDoubleRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/contains/inOptimizableDoubleRange.kt");