Intrinsics for 'reversed': support non-literal range expressions

#KT-21323 In Progress
This commit is contained in:
Dmitry Petrov
2017-12-11 13:57:17 +03:00
parent 1493805f8e
commit beff4a1b92
18 changed files with 355 additions and 25 deletions
@@ -20,12 +20,14 @@ import org.jetbrains.kotlin.codegen.ExpressionCodegen
import org.jetbrains.kotlin.codegen.range.forLoop.ForInProgressionExpressionLoopGenerator
import org.jetbrains.kotlin.codegen.range.inExpression.CallBasedInExpressionGenerator
import org.jetbrains.kotlin.codegen.range.inExpression.InExpressionGenerator
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtForExpression
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
class PrimitiveProgressionRangeValue : RangeValue {
class PrimitiveProgressionRangeValue(private val rangeExpression: KtExpression) : RangeValue {
override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) =
ForInProgressionExpressionLoopGenerator(codegen, forExpression)
ForInProgressionExpressionLoopGenerator(codegen, forExpression, rangeExpression)
override fun createInExpressionGenerator(codegen: ExpressionCodegen, operatorReference: KtSimpleNameExpression): InExpressionGenerator =
CallBasedInExpressionGenerator(codegen, operatorReference)
@@ -18,15 +18,21 @@ package org.jetbrains.kotlin.codegen.range
import org.jetbrains.kotlin.codegen.ExpressionCodegen
import org.jetbrains.kotlin.codegen.range.forLoop.ForInRangeInstanceLoopGenerator
import org.jetbrains.kotlin.codegen.range.forLoop.ForLoopGenerator
import org.jetbrains.kotlin.codegen.range.inExpression.CallBasedInExpressionGenerator
import org.jetbrains.kotlin.codegen.range.inExpression.InExpressionGenerator
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtForExpression
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
class PrimitiveRangeRangeValue : RangeValue {
class PrimitiveRangeRangeValue(private val rangeExpression: KtExpression) : ReversableRangeValue {
override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression) =
ForInRangeInstanceLoopGenerator(codegen, forExpression)
ForInRangeInstanceLoopGenerator(codegen, forExpression, rangeExpression, reversed = false)
override fun createInExpressionGenerator(codegen: ExpressionCodegen, operatorReference: KtSimpleNameExpression): InExpressionGenerator =
CallBasedInExpressionGenerator(codegen, operatorReference)
override fun createForInReversedLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression): ForLoopGenerator =
ForInRangeInstanceLoopGenerator(codegen, forExpression, rangeExpression, reversed = true)
}
@@ -59,9 +59,9 @@ fun ExpressionCodegen.createRangeValueForExpression(rangeExpression: KtExpressio
}
isPrimitiveRange(rangeType) ->
PrimitiveRangeRangeValue()
PrimitiveRangeRangeValue(rangeExpression)
isPrimitiveProgression(rangeType) ->
PrimitiveProgressionRangeValue()
PrimitiveProgressionRangeValue(rangeExpression)
isSubtypeOfString(rangeType, builtIns) ->
CharSequenceRangeValue(true, AsmTypes.JAVA_STRING_TYPE)
isSubtypeOfCharSequence(rangeType, builtIns) ->
@@ -24,15 +24,11 @@ import org.jetbrains.kotlin.codegen.AsmUtil.genIncrement
import org.jetbrains.kotlin.codegen.ExpressionCodegen
import org.jetbrains.kotlin.codegen.StackValue
abstract class AbstractForInRangeLoopGenerator : AbstractForInProgressionOrRangeLoopGenerator {
protected val step: Int
constructor(codegen: ExpressionCodegen, forExpression: KtForExpression, step: Int) : super(codegen, forExpression) {
assert(step == 1 || step == -1) { "'step' should be either 1 or -1: " + step }
this.step = step
}
constructor(codegen: ExpressionCodegen, forExpression: KtForExpression) : this(codegen, forExpression, 1)
abstract class AbstractForInRangeLoopGenerator(
codegen: ExpressionCodegen,
forExpression: KtForExpression,
protected val step: Int
) : AbstractForInProgressionOrRangeLoopGenerator(codegen, forExpression) {
override fun beforeLoop() {
super.beforeLoop()
@@ -83,4 +79,8 @@ abstract class AbstractForInRangeLoopGenerator : AbstractForInProgressionOrRange
loopParameter.store(StackValue.onStack(asmElementType), v)
}
}
init {
assert(step == 1 || step == -1) { "'step' should be either 1 or -1: " + step }
}
}
@@ -17,13 +17,16 @@
package org.jetbrains.kotlin.codegen.range.forLoop
import org.jetbrains.kotlin.codegen.ExpressionCodegen
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtForExpression
class ForInProgressionExpressionLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression)
: AbstractForInProgressionLoopGenerator(codegen, forExpression)
{
class ForInProgressionExpressionLoopGenerator(
codegen: ExpressionCodegen,
forExpression: KtForExpression,
private val rangeExpression: KtExpression
) : AbstractForInProgressionLoopGenerator(codegen, forExpression) {
override fun storeProgressionParametersToLocalVars() {
codegen.gen(forExpression.loopRange, asmLoopRangeType)
codegen.gen(rangeExpression, asmLoopRangeType)
v.dup()
v.dup()
@@ -17,21 +17,30 @@
package org.jetbrains.kotlin.codegen.range.forLoop
import org.jetbrains.kotlin.codegen.ExpressionCodegen
import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtForExpression
class ForInRangeInstanceLoopGenerator(
codegen: ExpressionCodegen,
forExpression: KtForExpression
) : AbstractForInRangeLoopGenerator(codegen, forExpression) {
forExpression: KtForExpression,
private val rangeExpression: KtExpression,
private val reversed: Boolean
) : AbstractForInRangeLoopGenerator(codegen, forExpression, if (reversed) -1 else 1) {
override fun storeRangeStartAndEnd() {
val loopRangeType = codegen.bindingContext.getType(forExpression.loopRange!!)!!
val loopRangeType = codegen.bindingContext.getType(rangeExpression)!!
val asmLoopRangeType = codegen.asmType(loopRangeType)
codegen.gen(forExpression.loopRange, asmLoopRangeType)
codegen.gen(rangeExpression, asmLoopRangeType)
v.dup()
// ranges inherit first and last from corresponding progressions
generateRangeOrProgressionProperty(asmLoopRangeType, "getFirst", asmElementType, loopParameterType, loopParameterVar)
generateRangeOrProgressionProperty(asmLoopRangeType, "getLast", asmElementType, asmElementType, endVar)
if (reversed) {
generateRangeOrProgressionProperty(asmLoopRangeType, "getLast", asmElementType, loopParameterType, loopParameterVar)
generateRangeOrProgressionProperty(asmLoopRangeType, "getFirst", asmElementType, asmElementType, endVar)
}
else {
generateRangeOrProgressionProperty(asmLoopRangeType, "getFirst", asmElementType, loopParameterType, loopParameterVar)
generateRangeOrProgressionProperty(asmLoopRangeType, "getLast", asmElementType, asmElementType, endVar)
}
}
}
@@ -0,0 +1,28 @@
// WITH_RUNTIME
import kotlin.test.*
fun intRange() = 1 .. 4
fun longRange() = 1L .. 4L
fun charRange() = '1' .. '4'
fun box(): String {
var sum = 0
for (i in intRange().reversed().reversed()) {
sum = sum * 10 + i
}
assertEquals(1234, sum)
var sumL = 0L
for (i in longRange().reversed().reversed()) {
sumL = sumL * 10 + i
}
assertEquals(1234L, sumL)
var sumC = 0
for (i in charRange().reversed().reversed()) {
sumC = sumC * 10 + i.toInt() - '0'.toInt()
}
assertEquals(1234, sumC)
return "OK"
}
@@ -0,0 +1,22 @@
// WITH_RUNTIME
import kotlin.test.*
fun intRange() = 4 .. 1
fun longRange() = 4L .. 1L
fun charRange() = '4' .. '1'
fun box(): String {
for (i in intRange().reversed()) {
throw AssertionError("Loop should not be executed")
}
for (i in longRange().reversed()) {
throw AssertionError("Loop should not be executed")
}
for (i in charRange().reversed()) {
throw AssertionError("Loop should not be executed")
}
return "OK"
}
@@ -0,0 +1,28 @@
// WITH_RUNTIME
import kotlin.test.*
fun intRange() = 1 .. 4
fun longRange() = 1L .. 4L
fun charRange() = '1' .. '4'
fun box(): String {
var sum = 0
for (i in intRange().reversed()) {
sum = sum * 10 + i
}
assertEquals(4321, sum)
var sumL = 0L
for (i in longRange().reversed()) {
sumL = sumL * 10 + i
}
assertEquals(4321L, sumL)
var sumC = 0
for (i in charRange().reversed()) {
sumC = sumC * 10 + i.toInt() - '0'.toInt()
}
assertEquals(4321, sumC)
return "OK"
}
@@ -0,0 +1,28 @@
// WITH_RUNTIME
import kotlin.test.*
fun intRange() = 1 .. 4
fun longRange() = 1L .. 4L
fun charRange() = '1' .. '4'
fun box(): String {
var sum = 0
for (i in intRange().reversed().reversed().reversed()) {
sum = sum * 10 + i
}
assertEquals(4321, sum)
var sumL = 0L
for (i in longRange().reversed().reversed().reversed()) {
sumL = sumL * 10 + i
}
assertEquals(4321L, sumL)
var sumC = 0
for (i in charRange().reversed().reversed().reversed()) {
sumC = sumC * 10 + i.toInt() - '0'.toInt()
}
assertEquals(4321, sumC)
return "OK"
}
@@ -0,0 +1,30 @@
// WITH_RUNTIME
import kotlin.test.*
fun intRange() = 1 .. 4
fun longRange() = 1L .. 4L
fun charRange() = '1' .. '4'
fun box(): String {
var sum = 0
for (i in intRange().reversed().reversed()) {
sum = sum * 10 + i
}
assertEquals(1234, sum)
var sumL = 0L
for (i in longRange().reversed().reversed()) {
sumL = sumL * 10 + i
}
assertEquals(1234L, sumL)
var sumC = 0
for (i in charRange().reversed().reversed()) {
sumC = sumC * 10 + i.toInt() - '0'.toInt()
}
assertEquals(1234, sumC)
return "OK"
}
// 0 reversed
@@ -0,0 +1,30 @@
// WITH_RUNTIME
import kotlin.test.*
fun intRange() = 1 .. 4
fun longRange() = 1L .. 4L
fun charRange() = '1' .. '4'
fun box(): String {
var sum = 0
for (i in intRange().reversed()) {
sum = sum * 10 + i
}
assertEquals(4321, sum)
var sumL = 0L
for (i in longRange().reversed()) {
sumL = sumL * 10 + i
}
assertEquals(4321L, sumL)
var sumC = 0
for (i in charRange().reversed()) {
sumC = sumC * 10 + i.toInt() - '0'.toInt()
}
assertEquals(4321, sumC)
return "OK"
}
// 0 reversed
@@ -0,0 +1,30 @@
// WITH_RUNTIME
import kotlin.test.*
fun intRange() = 1 .. 4
fun longRange() = 1L .. 4L
fun charRange() = '1' .. '4'
fun box(): String {
var sum = 0
for (i in intRange().reversed().reversed().reversed()) {
sum = sum * 10 + i
}
assertEquals(4321, sum)
var sumL = 0L
for (i in longRange().reversed().reversed().reversed()) {
sumL = sumL * 10 + i
}
assertEquals(4321L, sumL)
var sumC = 0
for (i in charRange().reversed().reversed().reversed()) {
sumC = sumC * 10 + i.toInt() - '0'.toInt()
}
assertEquals(4321, sumC)
return "OK"
}
// 0 reversed
@@ -15241,6 +15241,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("forInReversedEmptyRange.kt")
public void testForInReversedEmptyRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRange.kt");
doTest(fileName);
}
@TestMetadata("forInReversedEmptyRangeLiteral.kt")
public void testForInReversedEmptyRangeLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteral.kt");
@@ -15253,6 +15259,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
@TestMetadata("forInReversedRange.kt")
public void testForInReversedRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRange.kt");
doTest(fileName);
}
@TestMetadata("forInReversedRangeLiteral.kt")
public void testForInReversedRangeLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteral.kt");
@@ -15264,6 +15276,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt");
doTest(fileName);
}
@TestMetadata("ForInReversedReversedRange.kt")
public void testForInReversedReversedRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/ForInReversedReversedRange.kt");
doTest(fileName);
}
@TestMetadata("forInReversedReversedReversedRange.kt")
public void testForInReversedReversedReversedRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedReversedRange.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/ranges/forInUntil")
@@ -15241,6 +15241,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("forInReversedEmptyRange.kt")
public void testForInReversedEmptyRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRange.kt");
doTest(fileName);
}
@TestMetadata("forInReversedEmptyRangeLiteral.kt")
public void testForInReversedEmptyRangeLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteral.kt");
@@ -15253,6 +15259,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("forInReversedRange.kt")
public void testForInReversedRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRange.kt");
doTest(fileName);
}
@TestMetadata("forInReversedRangeLiteral.kt")
public void testForInReversedRangeLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteral.kt");
@@ -15264,6 +15276,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt");
doTest(fileName);
}
@TestMetadata("ForInReversedReversedRange.kt")
public void testForInReversedReversedRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/ForInReversedReversedRange.kt");
doTest(fileName);
}
@TestMetadata("forInReversedReversedReversedRange.kt")
public void testForInReversedReversedReversedRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedReversedRange.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/ranges/forInUntil")
@@ -1414,11 +1414,29 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
doTest(fileName);
}
@TestMetadata("forInReversedRange.kt")
public void testForInReversedRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRange.kt");
doTest(fileName);
}
@TestMetadata("forInReversedRangeLiteral.kt")
public void testForInReversedRangeLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedRangeLiteral.kt");
doTest(fileName);
}
@TestMetadata("ForInReversedReversedRange.kt")
public void testForInReversedReversedRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/ForInReversedReversedRange.kt");
doTest(fileName);
}
@TestMetadata("forInReversedReversedReversedRange.kt")
public void testForInReversedReversedReversedRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedReversedReversedRange.kt");
doTest(fileName);
}
}
}
@@ -15241,6 +15241,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("forInReversedEmptyRange.kt")
public void testForInReversedEmptyRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRange.kt");
doTest(fileName);
}
@TestMetadata("forInReversedEmptyRangeLiteral.kt")
public void testForInReversedEmptyRangeLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteral.kt");
@@ -15253,6 +15259,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
doTest(fileName);
}
@TestMetadata("forInReversedRange.kt")
public void testForInReversedRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRange.kt");
doTest(fileName);
}
@TestMetadata("forInReversedRangeLiteral.kt")
public void testForInReversedRangeLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteral.kt");
@@ -15264,6 +15276,18 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt");
doTest(fileName);
}
@TestMetadata("ForInReversedReversedRange.kt")
public void testForInReversedReversedRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/ForInReversedReversedRange.kt");
doTest(fileName);
}
@TestMetadata("forInReversedReversedReversedRange.kt")
public void testForInReversedReversedReversedRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedReversedRange.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/ranges/forInUntil")
@@ -16657,6 +16657,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true);
}
@TestMetadata("forInReversedEmptyRange.kt")
public void testForInReversedEmptyRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRange.kt");
doTest(fileName);
}
@TestMetadata("forInReversedEmptyRangeLiteral.kt")
public void testForInReversedEmptyRangeLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteral.kt");
@@ -16669,6 +16675,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
doTest(fileName);
}
@TestMetadata("forInReversedRange.kt")
public void testForInReversedRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRange.kt");
doTest(fileName);
}
@TestMetadata("forInReversedRangeLiteral.kt")
public void testForInReversedRangeLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteral.kt");
@@ -16680,6 +16692,18 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedRangeLiteralWithNonConstBounds.kt");
doTest(fileName);
}
@TestMetadata("ForInReversedReversedRange.kt")
public void testForInReversedReversedRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/ForInReversedReversedRange.kt");
doTest(fileName);
}
@TestMetadata("forInReversedReversedReversedRange.kt")
public void testForInReversedReversedReversedRange() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedReversedReversedRange.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/ranges/forInUntil")