Intrinsics for 'reversed': generate in-const-bound ranges as countable

#KT-21323 In Progress
This commit is contained in:
Dmitry Petrov
2017-12-11 12:20:21 +03:00
parent 1775f294f4
commit 821843e13f
9 changed files with 147 additions and 5 deletions
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.resolve.constants.ByteValue
import org.jetbrains.kotlin.resolve.constants.IntValue
import org.jetbrains.kotlin.resolve.constants.IntegerValueConstant
import org.jetbrains.kotlin.resolve.constants.ShortValue
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class PrimitiveNumberRangeLiteralRangeValue(
@@ -40,21 +41,24 @@ class PrimitiveNumberRangeLiteralRangeValue(
SimpleBoundedValue(codegen, rangeCall)
override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression): ForLoopGenerator =
getConstRangeForInRangeLiteralGenerator(codegen, forExpression) ?:
createConstBoundedForInRangeLiteralGenerator(codegen, forExpression) ?:
ForInSimpleProgressionLoopGenerator.fromBoundedValueWithStep1(codegen, forExpression, getBoundedValue(codegen))
override fun createForInReversedLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression): ForLoopGenerator =
// TODO const-bounded version
createConstBoundedRangeForInReversedRangeLiteralGenerator(codegen, forExpression) ?:
ForInSimpleProgressionLoopGenerator.fromBoundedValueWithStepMinus1(codegen, forExpression, getBoundedValue(codegen))
private fun getConstRangeForInRangeLiteralGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression): ForLoopGenerator? {
private fun createConstBoundedForInRangeLiteralGenerator(
codegen: ExpressionCodegen,
forExpression: KtForExpression
): ForLoopGenerator? {
val rhsExpression = rangeCall.valueArgumentsByIndex?.run { get(0).arguments[0].getArgumentExpression() } ?: return null
val constValue = codegen.getCompileTimeConstant(rhsExpression).safeAs<IntegerValueConstant<*>>() ?: return null
val untilValue = when (constValue) {
is ByteValue -> constValue.value + 1
is ShortValue -> constValue.value + 1
is IntValue -> constValue.value + 1
else -> return null
else -> Int.MIN_VALUE
}
// Watch out for integer overflow
@@ -70,4 +74,32 @@ class PrimitiveNumberRangeLiteralRangeValue(
step = 1
)
}
private fun createConstBoundedRangeForInReversedRangeLiteralGenerator(
codegen: ExpressionCodegen,
forExpression: KtForExpression
): ForLoopGenerator? {
val lhsExpression = rangeCall.extensionReceiver.safeAs<ExpressionReceiver>()?.expression ?: return null
val constValue = codegen.getCompileTimeConstant(lhsExpression).safeAs<IntegerValueConstant<*>>() ?: return null
val untilValue = when (constValue) {
is ByteValue -> constValue.value - 1
is ShortValue -> constValue.value - 1
is IntValue -> constValue.value - 1
else -> Int.MAX_VALUE
}
// Watch out for integer overflow
return if (untilValue == Int.MAX_VALUE)
null
else
ForInSimpleProgressionLoopGenerator(
codegen, forExpression,
startValue = codegen.generateCallSingleArgument(rangeCall),
isStartInclusive = true,
endValue = StackValue.integerConstant(untilValue, asmElementType),
isEndInclusive = false,
step = -1
)
}
}
@@ -0,0 +1,25 @@
// WITH_RUNTIME
import kotlin.test.*
fun intLow() = 4
fun intHigh() = 1
fun longLow() = 4L
fun longHigh() = 1L
fun charLow() = '4'
fun charHigh() = '1'
fun box(): String {
for (i in (intLow() .. intHigh()).reversed()) {
throw AssertionError("Loop should not be executed")
}
for (i in (longLow() .. longHigh()).reversed()) {
throw AssertionError("Loop should not be executed")
}
for (i in (charLow() .. charHigh()).reversed()) {
throw AssertionError("Loop should not be executed")
}
return "OK"
}
@@ -0,0 +1,31 @@
// WITH_RUNTIME
import kotlin.test.*
fun intLow() = 1
fun intHigh() = 4
fun longLow() = 1L
fun longHigh() = 4L
fun charLow() = '1'
fun charHigh() = '4'
fun box(): String {
var sum = 0
for (i in (intLow() .. intHigh()).reversed()) {
sum = sum * 10 + i
}
assertEquals(4321, sum)
var sumL = 0L
for (i in (longLow() .. longHigh()).reversed()) {
sumL = sumL * 10 + i
}
assertEquals(4321L, sumL)
var sumC = 0
for (i in (charLow() .. charHigh()).reversed()) {
sumC = sumC * 10 + i.toInt() - '0'.toInt()
}
assertEquals(4321, sumC)
return "OK"
}
@@ -14,6 +14,8 @@ fun box(): String {
}
// 0 reversed
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
@@ -24,6 +24,10 @@ fun box(): String {
}
// 0 reversed
// 0 getStart
// 0 getEnd
// 0 getFirst
// 0 getLast
// 0 getStep
// 0 getStep
// 2 IF_ICMPEQ
// ^ 1 for char progression, 1 for long progression
@@ -15247,11 +15247,23 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
@TestMetadata("forInReversedEmptyRangeLiteralWithNonConstBounds.kt")
public void testForInReversedEmptyRangeLiteralWithNonConstBounds() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteralWithNonConstBounds.kt");
doTest(fileName);
}
@TestMetadata("forInReversedRangeLiteral.kt")
public void testForInReversedRangeLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteral.kt");
doTest(fileName);
}
@TestMetadata("forInReversedRangeLiteralWithNonConstBounds.kt")
public void testForInReversedRangeLiteralWithNonConstBounds() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/ranges/forInUntil")
@@ -15247,11 +15247,23 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("forInReversedEmptyRangeLiteralWithNonConstBounds.kt")
public void testForInReversedEmptyRangeLiteralWithNonConstBounds() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteralWithNonConstBounds.kt");
doTest(fileName);
}
@TestMetadata("forInReversedRangeLiteral.kt")
public void testForInReversedRangeLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteral.kt");
doTest(fileName);
}
@TestMetadata("forInReversedRangeLiteralWithNonConstBounds.kt")
public void testForInReversedRangeLiteralWithNonConstBounds() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/ranges/forInUntil")
@@ -15247,11 +15247,23 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
doTest(fileName);
}
@TestMetadata("forInReversedEmptyRangeLiteralWithNonConstBounds.kt")
public void testForInReversedEmptyRangeLiteralWithNonConstBounds() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteralWithNonConstBounds.kt");
doTest(fileName);
}
@TestMetadata("forInReversedRangeLiteral.kt")
public void testForInReversedRangeLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteral.kt");
doTest(fileName);
}
@TestMetadata("forInReversedRangeLiteralWithNonConstBounds.kt")
public void testForInReversedRangeLiteralWithNonConstBounds() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/ranges/forInUntil")
@@ -16663,11 +16663,23 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
doTest(fileName);
}
@TestMetadata("forInReversedEmptyRangeLiteralWithNonConstBounds.kt")
public void testForInReversedEmptyRangeLiteralWithNonConstBounds() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteralWithNonConstBounds.kt");
doTest(fileName);
}
@TestMetadata("forInReversedRangeLiteral.kt")
public void testForInReversedRangeLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteral.kt");
doTest(fileName);
}
@TestMetadata("forInReversedRangeLiteralWithNonConstBounds.kt")
public void testForInReversedRangeLiteralWithNonConstBounds() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/ranges/forInUntil")