diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index 10fa2a42692..10b0e76e758 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -572,22 +572,14 @@ public class ExpressionCodegen extends KtVisitor impleme @Override public StackValue visitForExpression(@NotNull KtForExpression forExpression, StackValue receiver) { - // Is it a "1..2" or so - RangeCodegenUtil.BinaryCall binaryCall = RangeCodegenUtil.getRangeAsBinaryCall(forExpression); - if (binaryCall != null) { - ResolvedCall resolvedCall = CallUtilKt.getResolvedCall(binaryCall.op, bindingContext); - if (resolvedCall != null) { - if (RangeCodegenUtil.isOptimizableRangeTo(resolvedCall.getResultingDescriptor())) { - generateForLoop(new ForInRangeLiteralLoopGenerator(forExpression, binaryCall)); - return StackValue.none(); - } - } - } - ResolvedCall loopRangeCall = RangeCodegenUtil.getLoopRangeResolvedCall(forExpression, bindingContext); if (loopRangeCall != null) { CallableDescriptor loopRangeCallee = loopRangeCall.getResultingDescriptor(); - if (RangeCodegenUtil.isArrayOrPrimitiveArrayIndices(loopRangeCallee)) { + if (RangeCodegenUtil.isOptimizableRangeTo(loopRangeCallee)) { + generateForLoop(createForInRangeLiteralLoopGenerator(forExpression, loopRangeCall)); + return StackValue.none(); + } + else if (RangeCodegenUtil.isArrayOrPrimitiveArrayIndices(loopRangeCallee)) { generateForLoop(createForInArrayIndicesRangeLoopGenerator(forExpression, loopRangeCall)); return StackValue.none(); } @@ -621,6 +613,15 @@ public class ExpressionCodegen extends KtVisitor impleme return StackValue.none(); } + private AbstractForLoopGenerator createForInRangeLiteralLoopGenerator( + @NotNull KtForExpression forExpression, + @NotNull ResolvedCall loopRangeCall + ) { + ReceiverValue from = loopRangeCall.getDispatchReceiver(); + KtExpression to = loopRangeCall.getValueArgumentsByIndex().get(0).getArguments().get(0).getArgumentExpression(); + return new ForInRangeLiteralLoopGenerator(forExpression, from, to); + } + private AbstractForLoopGenerator createForInCollectionIndicesRangeLoopGenerator( @NotNull KtForExpression forExpression, @NotNull ResolvedCall loopRangeCall @@ -1075,23 +1076,23 @@ public class ExpressionCodegen extends KtVisitor impleme } private class ForInRangeLiteralLoopGenerator extends AbstractForInRangeLoopGenerator { - private final RangeCodegenUtil.BinaryCall rangeCall; + private final ReceiverValue from; + private final KtExpression to; private ForInRangeLiteralLoopGenerator( @NotNull KtForExpression forExpression, - @NotNull RangeCodegenUtil.BinaryCall rangeCall + @NotNull ReceiverValue from, + @NotNull KtExpression to ) { super(forExpression); - this.rangeCall = rangeCall; + this.from = from; + this.to = to; } @Override protected void storeRangeStartAndEnd() { - gen(rangeCall.left, loopParameterType); - v.store(loopParameterVar, loopParameterType); - - gen(rangeCall.right, asmElementType); - v.store(endVar, asmElementType); + loopParameter().store(generateReceiverValue(from, false), v); + StackValue.local(endVar, asmElementType).store(gen(to), v); } } @@ -1124,7 +1125,7 @@ public class ExpressionCodegen extends KtVisitor impleme @Override protected void storeRangeStartAndEnd() { - StackValue.local(loopParameterVar, loopParameterType).store(StackValue.constant(0, asmElementType), v); + loopParameter().store(StackValue.constant(0, asmElementType), v); StackValue receiver = generateReceiverValue(receiverValue, false); receiver.put(receiver.type, v); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/RangeCodegenUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/RangeCodegenUtil.java index cf39211abe4..281b13a8a22 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/RangeCodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/RangeCodegenUtil.java @@ -70,36 +70,6 @@ public class RangeCodegenUtil { return !rangeType.isMarkedNullable() && getPrimitiveProgressionElementType(rangeType) != null; } - @Nullable - public static BinaryCall getRangeAsBinaryCall(@NotNull KtForExpression forExpression) { - // We are looking for rangeTo() calls - // Other binary operations will succeed too, but will be filtered out later (by examining a resolvedCall) - KtExpression rangeExpression = forExpression.getLoopRange(); - assert rangeExpression != null; - KtExpression loopRange = KtPsiUtil.deparenthesize(rangeExpression); - if (loopRange instanceof KtQualifiedExpression) { - // a.rangeTo(b) - KtQualifiedExpression qualifiedExpression = (KtQualifiedExpression) loopRange; - KtExpression selector = qualifiedExpression.getSelectorExpression(); - if (selector instanceof KtCallExpression) { - KtCallExpression callExpression = (KtCallExpression) selector; - List arguments = callExpression.getValueArguments(); - if (arguments.size() == 1) { - return new BinaryCall(qualifiedExpression.getReceiverExpression(), callExpression.getCalleeExpression(), - arguments.get(0).getArgumentExpression()); - } - } - } - else if (loopRange instanceof KtBinaryExpression) { - // a rangeTo b - // a .. b - KtBinaryExpression binaryExpression = (KtBinaryExpression) loopRange; - return new BinaryCall(binaryExpression.getLeft(), binaryExpression.getOperationReference(), binaryExpression.getRight()); - - } - return null; - } - @Nullable public static ResolvedCall getLoopRangeResolvedCall(@NotNull KtForExpression forExpression, @NotNull BindingContext bindingContext) { KtExpression loopRange = KtPsiUtil.deparenthesize(forExpression.getLoopRange()); @@ -111,9 +81,12 @@ public class RangeCodegenUtil { return CallUtilKt.getResolvedCall(selector, bindingContext); } } - else if (loopRange instanceof KtSimpleNameExpression) { + else if (loopRange instanceof KtSimpleNameExpression || loopRange instanceof KtCallExpression) { return CallUtilKt.getResolvedCall(loopRange, bindingContext); } + else if (loopRange instanceof KtBinaryExpression) { + return CallUtilKt.getResolvedCall(((KtBinaryExpression) loopRange).getOperationReference(), bindingContext); + } return null; } @@ -191,16 +164,4 @@ public class RangeCodegenUtil { return true; } - - public static class BinaryCall { - public final KtExpression left; - public final KtExpression op; - public final KtExpression right; - - private BinaryCall(KtExpression left, KtExpression op, KtExpression right) { - this.left = left; - this.op = op; - this.right = right; - } - } } diff --git a/compiler/testData/codegen/box/ranges/forInRangeWithImplicitReceiver.kt b/compiler/testData/codegen/box/ranges/forInRangeWithImplicitReceiver.kt new file mode 100644 index 00000000000..a29e5a77e5e --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forInRangeWithImplicitReceiver.kt @@ -0,0 +1,17 @@ +// WITH_RUNTIME + +import kotlin.test.assertEquals + +fun Int.digitsUpto(end: Int): Int { + var sum = 0 + for (i in rangeTo(end)) { + sum = sum*10 + i + } + return sum +} + +fun box(): String { + assertEquals(1234, 1.digitsUpto(4)) + return "OK" +} + diff --git a/compiler/testData/codegen/box/ranges/forNullableIntInRangeWithImplicitReceiver.kt b/compiler/testData/codegen/box/ranges/forNullableIntInRangeWithImplicitReceiver.kt new file mode 100644 index 00000000000..8f9a4534ea6 --- /dev/null +++ b/compiler/testData/codegen/box/ranges/forNullableIntInRangeWithImplicitReceiver.kt @@ -0,0 +1,20 @@ +// WITH_RUNTIME + +import kotlin.test.assertEquals + +fun suppressBoxingOptimization(ni: Int?) {} + +fun Int.digitsUpto(end: Int): Int { + var sum = 0 + for (i: Int? in rangeTo(end)) { + suppressBoxingOptimization(i) + sum = sum*10 + i!! + } + return sum +} + +fun box(): String { + assertEquals(1234, 1.digitsUpto(4)) + return "OK" +} + diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInRangeWithImplicitReceiver.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeWithImplicitReceiver.kt new file mode 100644 index 00000000000..178849e1b5b --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeWithImplicitReceiver.kt @@ -0,0 +1,13 @@ +fun Int.digitsUpto(end: Int): Int { + var sum = 0 + for (i in rangeTo(end)) { + sum = sum*10 + i + } + return sum +} + +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 05abd530845..7ed563bf1ec 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -10486,12 +10486,24 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("forInRangeWithImplicitReceiver.kt") + public void testForInRangeWithImplicitReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forInRangeWithImplicitReceiver.kt"); + doTest(fileName); + } + @TestMetadata("forIntRange.kt") public void testForIntRange() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forIntRange.kt"); doTest(fileName); } + @TestMetadata("forNullableIntInRangeWithImplicitReceiver.kt") + public void testForNullableIntInRangeWithImplicitReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/forNullableIntInRangeWithImplicitReceiver.kt"); + doTest(fileName); + } + @TestMetadata("multiAssignmentIterationOverIntRange.kt") public void testMultiAssignmentIterationOverIntRange() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/ranges/multiAssignmentIterationOverIntRange.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 611799adb0f..2b468bd97e3 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -853,6 +853,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/forLoop"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("forInRangeWithImplicitReceiver.kt") + public void testForInRangeWithImplicitReceiver() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/forInRangeWithImplicitReceiver.kt"); + doTest(fileName); + } + @TestMetadata("loopVarInterval.kt") public void testLoopVarInterval() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/forLoop/loopVarInterval.kt");