Intrinsics for 'reversed': infrastructure & primitive range support
#KT-21323 In Progress
This commit is contained in:
@@ -188,6 +188,14 @@ fun isPrimitiveNumberRangeExtensionContainsPrimitiveNumber(descriptor: CallableD
|
|||||||
return true
|
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) =
|
private fun isPrimitiveNumberType(type: KotlinType) =
|
||||||
KotlinBuiltIns.isByte(type) ||
|
KotlinBuiltIns.isByte(type) ||
|
||||||
KotlinBuiltIns.isShort(type) ||
|
KotlinBuiltIns.isShort(type) ||
|
||||||
|
|||||||
+6
-1
@@ -33,7 +33,8 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
|||||||
|
|
||||||
class PrimitiveNumberRangeLiteralRangeValue(
|
class PrimitiveNumberRangeLiteralRangeValue(
|
||||||
rangeCall: ResolvedCall<out CallableDescriptor>
|
rangeCall: ResolvedCall<out CallableDescriptor>
|
||||||
): PrimitiveNumberRangeIntrinsicRangeValue(rangeCall) {
|
) : PrimitiveNumberRangeIntrinsicRangeValue(rangeCall),
|
||||||
|
ReversableRangeValue {
|
||||||
|
|
||||||
override fun getBoundedValue(codegen: ExpressionCodegen) =
|
override fun getBoundedValue(codegen: ExpressionCodegen) =
|
||||||
SimpleBoundedValue(codegen, rangeCall)
|
SimpleBoundedValue(codegen, rangeCall)
|
||||||
@@ -42,6 +43,10 @@ class PrimitiveNumberRangeLiteralRangeValue(
|
|||||||
getConstRangeForInRangeLiteralGenerator(codegen, forExpression) ?:
|
getConstRangeForInRangeLiteralGenerator(codegen, forExpression) ?:
|
||||||
ForInSimpleProgressionLoopGenerator.fromBoundedValueWithStep1(codegen, forExpression, getBoundedValue(codegen))
|
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? {
|
private fun getConstRangeForInRangeLiteralGenerator(codegen: ExpressionCodegen, forExpression: KtForExpression): ForLoopGenerator? {
|
||||||
val rhsExpression = rangeCall.valueArgumentsByIndex?.run { get(0).arguments[0].getArgumentExpression() } ?: return null
|
val rhsExpression = rangeCall.valueArgumentsByIndex?.run { get(0).arguments[0].getArgumentExpression() } ?: return null
|
||||||
val constValue = codegen.getCompileTimeConstant(rhsExpression).safeAs<IntegerValueConstant<*>>() ?: 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 {
|
interface BoundedValue {
|
||||||
val instanceType: Type
|
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.callUtil.getResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
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.KotlinType
|
||||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
@@ -127,7 +128,15 @@ private fun ExpressionCodegen.createIntrinsifiedRangeValueOrNull(rangeCall: Reso
|
|||||||
CharSequenceIndicesRangeValue(rangeCall)
|
CharSequenceIndicesRangeValue(rangeCall)
|
||||||
isComparableRangeTo(rangeCallee) ->
|
isComparableRangeTo(rangeCallee) ->
|
||||||
ComparableRangeLiteralRangeValue(this, rangeCall)
|
ComparableRangeLiteralRangeValue(this, rangeCall)
|
||||||
|
isPrimitiveProgressionReverse(rangeCallee) ->
|
||||||
|
createReversedRangeValueOrNull(rangeCall)
|
||||||
else ->
|
else ->
|
||||||
null
|
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)
|
||||||
|
}
|
||||||
+14
@@ -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"
|
||||||
|
}
|
||||||
+24
@@ -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"
|
||||||
|
}
|
||||||
Vendored
+19
@@ -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
|
||||||
+29
@@ -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
|
||||||
+21
@@ -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")
|
@TestMetadata("compiler/testData/codegen/box/ranges/forInUntil")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@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")
|
@TestMetadata("compiler/testData/codegen/box/ranges/forInUntil")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
@@ -1399,6 +1399,27 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
|||||||
doTest(fileName);
|
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")
|
@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")
|
@TestMetadata("compiler/testData/codegen/box/ranges/forInUntil")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
+21
@@ -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")
|
@TestMetadata("compiler/testData/codegen/box/ranges/forInUntil")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user