Intrinsics for 'reversed': infrastructure & primitive range support

#KT-21323 In Progress
This commit is contained in:
Dmitry Petrov
2017-12-11 12:00:42 +03:00
parent 83ae34bb29
commit 1775f294f4
14 changed files with 253 additions and 1 deletions
@@ -188,6 +188,14 @@ fun isPrimitiveNumberRangeExtensionContainsPrimitiveNumber(descriptor: CallableD
return true
}
fun isPrimitiveProgressionReverse(descriptor: CallableDescriptor): Boolean {
if (!isTopLevelInPackage(descriptor, "reversed", "kotlin.ranges")) return false
if (descriptor.valueParameters.isNotEmpty()) return false
val extensionReceiverType = descriptor.extensionReceiverParameter?.type ?: return false
if (!isPrimitiveProgression(extensionReceiverType)) return false
return true
}
private fun isPrimitiveNumberType(type: KotlinType) =
KotlinBuiltIns.isByte(type) ||
KotlinBuiltIns.isShort(type) ||
@@ -33,7 +33,8 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class PrimitiveNumberRangeLiteralRangeValue(
rangeCall: ResolvedCall<out CallableDescriptor>
): PrimitiveNumberRangeIntrinsicRangeValue(rangeCall) {
) : PrimitiveNumberRangeIntrinsicRangeValue(rangeCall),
ReversableRangeValue {
override fun getBoundedValue(codegen: ExpressionCodegen) =
SimpleBoundedValue(codegen, rangeCall)
@@ -42,6 +43,10 @@ class PrimitiveNumberRangeLiteralRangeValue(
getConstRangeForInRangeLiteralGenerator(codegen, forExpression) ?:
ForInSimpleProgressionLoopGenerator.fromBoundedValueWithStep1(codegen, forExpression, getBoundedValue(codegen))
override fun createForInReversedLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression): ForLoopGenerator =
// TODO const-bounded version
ForInSimpleProgressionLoopGenerator.fromBoundedValueWithStepMinus1(codegen, forExpression, getBoundedValue(codegen))
private fun getConstRangeForInRangeLiteralGenerator(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
@@ -32,6 +32,11 @@ interface RangeValue {
}
interface ReversableRangeValue : RangeValue {
fun createForInReversedLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression): ForLoopGenerator
}
interface BoundedValue {
val instanceType: Type
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
import org.jetbrains.org.objectweb.asm.Type
@@ -127,7 +128,15 @@ private fun ExpressionCodegen.createIntrinsifiedRangeValueOrNull(rangeCall: Reso
CharSequenceIndicesRangeValue(rangeCall)
isComparableRangeTo(rangeCallee) ->
ComparableRangeLiteralRangeValue(this, rangeCall)
isPrimitiveProgressionReverse(rangeCallee) ->
createReversedRangeValueOrNull(rangeCall)
else ->
null
}
}
private fun ExpressionCodegen.createReversedRangeValueOrNull(rangeCall: ResolvedCall<out CallableDescriptor>): RangeValue? {
val receiver = rangeCall.extensionReceiver as? ExpressionReceiver ?: return null
val receiverRangeValue = createRangeValueForExpression(receiver.expression) as? ReversableRangeValue ?: return null
return ReversedRangeValue(receiverRangeValue)
}
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.codegen.range
import org.jetbrains.kotlin.codegen.ExpressionCodegen
import org.jetbrains.kotlin.codegen.range.forLoop.ForLoopGenerator
import org.jetbrains.kotlin.codegen.range.inExpression.InExpressionGenerator
import org.jetbrains.kotlin.psi.KtForExpression
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
class ReversedRangeValue(private val original: ReversableRangeValue) : RangeValue, ReversableRangeValue {
override fun createInExpressionGenerator(codegen: ExpressionCodegen, operatorReference: KtSimpleNameExpression): InExpressionGenerator =
original.createInExpressionGenerator(codegen, operatorReference)
override fun createForLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression): ForLoopGenerator =
original.createForInReversedLoopGenerator(codegen, forExpression)
override fun createForInReversedLoopGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression): ForLoopGenerator =
original.createForLoopGenerator(codegen, forExpression)
}
@@ -0,0 +1,14 @@
// WITH_RUNTIME
fun box(): String {
for (i in (4 .. 1).reversed()) {
throw AssertionError("Loop should not be executed")
}
for (i in (4L .. 1L).reversed()) {
throw AssertionError("Loop should not be executed")
}
for (i in ('D' .. 'A').reversed()) {
throw AssertionError("Loop should not be executed")
}
return "OK"
}
@@ -0,0 +1,24 @@
// WITH_RUNTIME
import kotlin.test.*
fun box(): String {
var sum = 0
for (i in (1 .. 4).reversed()) {
sum = sum * 10 + i
}
assertEquals(4321, sum)
var sumL = 0L
for (i in (1L .. 4L).reversed()) {
sumL = sumL * 10 + i
}
assertEquals(4321L, sumL)
var sumC = 0
for (i in ('1' .. '4').reversed()) {
sumC = sumC * 10 + i.toInt() - '0'.toInt()
}
assertEquals(4321, sumC)
return "OK"
}
@@ -0,0 +1,19 @@
// WITH_RUNTIME
fun box(): String {
for (i in (4 .. 1).reversed()) {
throw AssertionError("Loop should not be executed")
}
for (i in (4L .. 1L).reversed()) {
throw AssertionError("Loop should not be executed")
}
for (i in ('D' .. 'A').reversed()) {
throw AssertionError("Loop should not be executed")
}
return "OK"
}
// 0 reversed
// 0 getFirst
// 0 getLast
// 0 getStep
@@ -0,0 +1,29 @@
// WITH_RUNTIME
import kotlin.test.*
fun box(): String {
var sum = 0
for (i in (1 .. 4).reversed()) {
sum = sum * 10 + i
}
assertEquals(4321, sum)
var sumL = 0L
for (i in (1L .. 4L).reversed()) {
sumL = sumL * 10 + i
}
assertEquals(4321L, sumL)
var sumC = 0
for (i in ('1' .. '4').reversed()) {
sumC = sumC * 10 + i.toInt() - '0'.toInt()
}
assertEquals(4321, sumC)
return "OK"
}
// 0 reversed
// 0 getFirst
// 0 getLast
// 0 getStep
@@ -15233,6 +15233,27 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
}
}
@TestMetadata("compiler/testData/codegen/box/ranges/forInReversed")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ForInReversed extends AbstractIrBlackBoxCodegenTest {
public void testAllFilesPresentInForInReversed() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("forInReversedEmptyRangeLiteral.kt")
public void testForInReversedEmptyRangeLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteral.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("compiler/testData/codegen/box/ranges/forInUntil")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -15233,6 +15233,27 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
}
}
@TestMetadata("compiler/testData/codegen/box/ranges/forInReversed")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ForInReversed extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInForInReversed() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("forInReversedEmptyRangeLiteral.kt")
public void testForInReversedEmptyRangeLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteral.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("compiler/testData/codegen/box/ranges/forInUntil")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -1399,6 +1399,27 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInReversed")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ForInReversed extends AbstractBytecodeTextTest {
public void testAllFilesPresentInForInReversed() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/forLoop/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
}
@TestMetadata("forInReversedEmptyRangeLiteral.kt")
public void testForInReversedEmptyRangeLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInReversed/forInReversedEmptyRangeLiteral.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("compiler/testData/codegen/bytecodeText/hashCode")
@@ -15233,6 +15233,27 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
}
}
@TestMetadata("compiler/testData/codegen/box/ranges/forInReversed")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ForInReversed extends AbstractLightAnalysisModeTest {
public void testAllFilesPresentInForInReversed() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("forInReversedEmptyRangeLiteral.kt")
public void testForInReversedEmptyRangeLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteral.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("compiler/testData/codegen/box/ranges/forInUntil")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -16649,6 +16649,27 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
}
}
@TestMetadata("compiler/testData/codegen/box/ranges/forInReversed")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class ForInReversed extends AbstractJsCodegenBoxTest {
public void testAllFilesPresentInForInReversed() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/ranges/forInReversed"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true);
}
@TestMetadata("forInReversedEmptyRangeLiteral.kt")
public void testForInReversedEmptyRangeLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInReversed/forInReversedEmptyRangeLiteral.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("compiler/testData/codegen/box/ranges/forInUntil")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)