KT-24156 Do not optimize for-loops over Strings with custom iterator

'fun CharSequence.iterator()' is an extension function, so one can
overload it with custom implementation.
Other "predefined" containers such as arrays and ranges have member
'fun iterator()', so these containers are not affected.

Check that 'iterator' call corresponds to an extension function
'iterator' defined in package 'kotlin.text' with a receiver of type
'kotlin.CharSequence'.

 #KT-24156 Fixed
This commit is contained in:
Dmitry Petrov
2018-05-10 15:40:55 +03:00
parent f568149863
commit c22dfeaf82
6 changed files with 54 additions and 3 deletions
@@ -254,3 +254,9 @@ fun getAsmRangeElementTypeForPrimitiveRangeOrProgression(rangeCallee: CallableDe
throw AssertionError("Unexpected range type: $rangeType")
}
fun isCharSequenceIterator(descriptor: CallableDescriptor) =
descriptor.isTopLevelExtensionOnType("iterator", "kotlin.text") {
it.constructor.declarationDescriptor?.isTopLevelInPackage("CharSequence", "kotlin")
?: false
}
@@ -47,6 +47,8 @@ fun ExpressionCodegen.createRangeValueForExpression(rangeExpression: KtExpressio
val rangeType = bindingContext.getType(rangeExpression)!!
val asmRangeType = asmType(rangeType)
val loopRangeIteratorResolvedCall = bindingContext[BindingContext.LOOP_RANGE_ITERATOR_RESOLVED_CALL, rangeExpression]
val builtIns = state.module.builtIns
return when {
asmRangeType.sort == Type.ARRAY -> {
@@ -62,9 +64,9 @@ fun ExpressionCodegen.createRangeValueForExpression(rangeExpression: KtExpressio
PrimitiveRangeRangeValue(rangeExpression)
isPrimitiveProgression(rangeType) ->
PrimitiveProgressionRangeValue(rangeExpression)
isSubtypeOfString(rangeType, builtIns) ->
isSubtypeOfString(rangeType, builtIns) && isCharSequenceIteratorCall(loopRangeIteratorResolvedCall) ->
CharSequenceRangeValue(true, AsmTypes.JAVA_STRING_TYPE)
isSubtypeOfCharSequence(rangeType, builtIns) ->
isSubtypeOfCharSequence(rangeType, builtIns) && isCharSequenceIteratorCall(loopRangeIteratorResolvedCall) ->
CharSequenceRangeValue(false, null)
else ->
IterableRangeValue()
@@ -147,4 +149,7 @@ private fun ExpressionCodegen.createReversedRangeValueOrNull(rangeCall: Resolved
val receiver = rangeCall.extensionReceiver as? ExpressionReceiver ?: return null
val receiverRangeValue = createRangeValueForExpression(receiver.expression) as? ReversableRangeValue ?: return null
return ReversedRangeValue(receiverRangeValue)
}
}
private fun isCharSequenceIteratorCall(iteratorCall: ResolvedCall<*>?) =
iteratorCall?.resultingDescriptor?.let { isCharSequenceIterator(it) } ?: false
@@ -0,0 +1,25 @@
// TARGET_BACKEND: JVM
// WITH_RUNTIME
operator fun String.iterator(): IntIterator = object : IntIterator() {
private var index = 0
override fun nextInt() = codePointAt(index).apply {
index += Character.charCount(this)
}
override fun hasNext(): Boolean = index < length
}
fun String.collectInts(): List<Int> {
val result = ArrayList<Int>()
for (c in this) {
result.add(c)
}
return result
}
fun box(): String {
val ints = String(Character.toChars(127849)).collectInts()
return if (ints == listOf(127849)) "OK" else "Fail: $ints"
}
@@ -15480,6 +15480,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/ranges/forInStringVarUpdatedInLoopBody.kt");
}
@TestMetadata("forInStringWithCustomIterator.kt")
public void testForInStringWithCustomIterator() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInStringWithCustomIterator.kt");
}
@TestMetadata("forIntRange.kt")
public void testForIntRange() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forIntRange.kt");
@@ -15480,6 +15480,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/ranges/forInStringVarUpdatedInLoopBody.kt");
}
@TestMetadata("forInStringWithCustomIterator.kt")
public void testForInStringWithCustomIterator() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInStringWithCustomIterator.kt");
}
@TestMetadata("forIntRange.kt")
public void testForIntRange() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forIntRange.kt");
@@ -15480,6 +15480,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/ranges/forInStringVarUpdatedInLoopBody.kt");
}
@TestMetadata("forInStringWithCustomIterator.kt")
public void testForInStringWithCustomIterator() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forInStringWithCustomIterator.kt");
}
@TestMetadata("forIntRange.kt")
public void testForIntRange() throws Exception {
runTest("compiler/testData/codegen/box/ranges/forIntRange.kt");