Generate proper 'compareTo' calls for non-primitive 'a in x .. y'
When we have some custom implementation of Comparable, it's important that we compare values exactly as 'lowBound <= a && a <= highBound'. Make sure that evaluation order and compareTo calls match for optimized and non-optimized case.
This commit is contained in:
+6
-4
@@ -18,9 +18,8 @@ package org.jetbrains.kotlin.codegen.range
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||||
import org.jetbrains.kotlin.codegen.isClosedRangeContains
|
import org.jetbrains.kotlin.codegen.isClosedRangeContains
|
||||||
import org.jetbrains.kotlin.codegen.range.comparison.ObjectComparisonGenerator
|
|
||||||
import org.jetbrains.kotlin.codegen.range.forLoop.IteratorForLoopGenerator
|
import org.jetbrains.kotlin.codegen.range.forLoop.IteratorForLoopGenerator
|
||||||
import org.jetbrains.kotlin.codegen.range.inExpression.InContinuousRangeExpressionGenerator
|
import org.jetbrains.kotlin.codegen.range.inExpression.InContinuousRangeOfComparableExpressionGenerator
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.psi.KtForExpression
|
import org.jetbrains.kotlin.psi.KtForExpression
|
||||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||||
@@ -38,6 +37,9 @@ class ComparableRangeLiteralRangeValue(
|
|||||||
override fun isIntrinsicInCall(resolvedCallForIn: ResolvedCall<out CallableDescriptor>) =
|
override fun isIntrinsicInCall(resolvedCallForIn: ResolvedCall<out CallableDescriptor>) =
|
||||||
isClosedRangeContains(resolvedCallForIn.resultingDescriptor)
|
isClosedRangeContains(resolvedCallForIn.resultingDescriptor)
|
||||||
|
|
||||||
override fun createIntrinsicInExpressionGenerator(codegen: ExpressionCodegen, operatorReference: KtSimpleNameExpression, resolvedCall: ResolvedCall<out CallableDescriptor>)=
|
override fun createIntrinsicInExpressionGenerator(
|
||||||
InContinuousRangeExpressionGenerator(operatorReference, boundedValue, ObjectComparisonGenerator)
|
codegen: ExpressionCodegen,
|
||||||
|
operatorReference: KtSimpleNameExpression,
|
||||||
|
resolvedCall: ResolvedCall<out CallableDescriptor>
|
||||||
|
) = InContinuousRangeOfComparableExpressionGenerator(operatorReference, boundedValue)
|
||||||
}
|
}
|
||||||
+2
-2
@@ -19,7 +19,7 @@ package org.jetbrains.kotlin.codegen.range
|
|||||||
import org.jetbrains.kotlin.codegen.*
|
import org.jetbrains.kotlin.codegen.*
|
||||||
import org.jetbrains.kotlin.codegen.range.comparison.getComparisonGeneratorForRangeContainsCall
|
import org.jetbrains.kotlin.codegen.range.comparison.getComparisonGeneratorForRangeContainsCall
|
||||||
import org.jetbrains.kotlin.codegen.range.inExpression.CallBasedInExpressionGenerator
|
import org.jetbrains.kotlin.codegen.range.inExpression.CallBasedInExpressionGenerator
|
||||||
import org.jetbrains.kotlin.codegen.range.inExpression.InContinuousRangeExpressionGenerator
|
import org.jetbrains.kotlin.codegen.range.inExpression.InPrimitiveContinuousRangeExpressionGenerator
|
||||||
import org.jetbrains.kotlin.codegen.range.inExpression.InExpressionGenerator
|
import org.jetbrains.kotlin.codegen.range.inExpression.InExpressionGenerator
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||||
@@ -42,7 +42,7 @@ abstract class PrimitiveNumberRangeIntrinsicRangeValue(rangeCall: ResolvedCall<o
|
|||||||
): InExpressionGenerator {
|
): InExpressionGenerator {
|
||||||
val comparisonGenerator = getComparisonGeneratorForRangeContainsCall(codegen, resolvedCall)
|
val comparisonGenerator = getComparisonGeneratorForRangeContainsCall(codegen, resolvedCall)
|
||||||
return if (comparisonGenerator != null)
|
return if (comparisonGenerator != null)
|
||||||
InContinuousRangeExpressionGenerator(operatorReference, getBoundedValue(codegen), comparisonGenerator)
|
InPrimitiveContinuousRangeExpressionGenerator(operatorReference, getBoundedValue(codegen), comparisonGenerator)
|
||||||
else
|
else
|
||||||
CallBasedInExpressionGenerator(codegen, operatorReference)
|
CallBasedInExpressionGenerator(codegen, operatorReference)
|
||||||
}
|
}
|
||||||
|
|||||||
+136
@@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* 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.inExpression
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||||
|
import org.jetbrains.kotlin.codegen.BranchedValue
|
||||||
|
import org.jetbrains.kotlin.codegen.Invert
|
||||||
|
import org.jetbrains.kotlin.codegen.StackValue
|
||||||
|
import org.jetbrains.kotlin.codegen.range.BoundedValue
|
||||||
|
import org.jetbrains.kotlin.codegen.range.comparison.ObjectComparisonGenerator
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||||
|
import org.jetbrains.org.objectweb.asm.Label
|
||||||
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
|
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||||
|
|
||||||
|
class InContinuousRangeOfComparableExpressionGenerator(
|
||||||
|
operatorReference: KtSimpleNameExpression,
|
||||||
|
private val boundedValue: BoundedValue
|
||||||
|
) : InExpressionGenerator {
|
||||||
|
private val isNotIn = operatorReference.getReferencedNameElementType() == KtTokens.NOT_IN
|
||||||
|
private val comparisonGenerator = ObjectComparisonGenerator
|
||||||
|
|
||||||
|
override fun generate(argument: StackValue): BranchedValue =
|
||||||
|
gen(argument).let { if (isNotIn) Invert(it) else it }
|
||||||
|
|
||||||
|
private fun gen(argument: StackValue): BranchedValue =
|
||||||
|
object : BranchedValue(argument, null, comparisonGenerator.comparedType, Opcodes.IFEQ) {
|
||||||
|
override fun condJump(jumpLabel: Label, v: InstructionAdapter, jumpIfFalse: Boolean) {
|
||||||
|
if (jumpIfFalse) {
|
||||||
|
genJumpIfFalse(v, jumpLabel)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
genJumpIfTrue(v, jumpLabel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun genJumpIfTrue(v: InstructionAdapter, jumpLabel: Label) {
|
||||||
|
// if (arg is in range) goto jumpLabel
|
||||||
|
|
||||||
|
val exitLabel1 = Label()
|
||||||
|
val exitLabel2 = Label()
|
||||||
|
|
||||||
|
boundedValue.putHighLow(v, operandType)
|
||||||
|
arg1.put(operandType, v)
|
||||||
|
AsmUtil.dupx(v, operandType)
|
||||||
|
|
||||||
|
// On stack: high arg low arg
|
||||||
|
// if (low bound is NOT satisfied) goto exitLabel1
|
||||||
|
if (boundedValue.isLowInclusive) {
|
||||||
|
// arg < low
|
||||||
|
v.swap()
|
||||||
|
comparisonGenerator.jumpIfLess(v, exitLabel1)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// arg <= low
|
||||||
|
v.swap()
|
||||||
|
comparisonGenerator.jumpIfLessOrEqual(v, exitLabel1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// On stack: high arg
|
||||||
|
// if (high bound is satisfied) goto jumpLabel
|
||||||
|
if (boundedValue.isHighInclusive) {
|
||||||
|
// arg <= high
|
||||||
|
v.swap()
|
||||||
|
comparisonGenerator.jumpIfLessOrEqual(v, jumpLabel)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// arg < high
|
||||||
|
v.swap()
|
||||||
|
comparisonGenerator.jumpIfLess(v, jumpLabel)
|
||||||
|
}
|
||||||
|
v.goTo(exitLabel2)
|
||||||
|
|
||||||
|
v.mark(exitLabel1)
|
||||||
|
AsmUtil.pop2(v, operandType)
|
||||||
|
|
||||||
|
v.mark(exitLabel2)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun genJumpIfFalse(v: InstructionAdapter, jumpLabel: Label) {
|
||||||
|
// if (arg is NOT in range) goto jumpLabel
|
||||||
|
|
||||||
|
val cmpHighLabel = Label()
|
||||||
|
|
||||||
|
boundedValue.putHighLow(v, operandType)
|
||||||
|
arg1.put(operandType, v)
|
||||||
|
AsmUtil.dupx(v, operandType)
|
||||||
|
|
||||||
|
// On stack: high arg low arg
|
||||||
|
// if ([low bound is satisfied]) goto cmpHighLabel
|
||||||
|
if (boundedValue.isLowInclusive) {
|
||||||
|
// arg >= low
|
||||||
|
v.swap()
|
||||||
|
comparisonGenerator.jumpIfGreaterOrEqual(v, cmpHighLabel)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// arg > low
|
||||||
|
v.swap()
|
||||||
|
comparisonGenerator.jumpIfGreater(v, cmpHighLabel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Low bound is NOT satisfied, clear stack and goto jumpLabel
|
||||||
|
AsmUtil.pop2(v, operandType)
|
||||||
|
v.goTo(jumpLabel)
|
||||||
|
|
||||||
|
v.mark(cmpHighLabel)
|
||||||
|
// On stack: high arg
|
||||||
|
// if ([high bound is NOT satisfied]) goto jumpLabel
|
||||||
|
if (boundedValue.isHighInclusive) {
|
||||||
|
// arg > high
|
||||||
|
v.swap()
|
||||||
|
comparisonGenerator.jumpIfGreater(v, jumpLabel)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// arg >= high
|
||||||
|
v.swap()
|
||||||
|
comparisonGenerator.jumpIfGreaterOrEqual(v, jumpLabel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -28,7 +28,7 @@ import org.jetbrains.org.objectweb.asm.Label
|
|||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||||
|
|
||||||
class InContinuousRangeExpressionGenerator(
|
class InPrimitiveContinuousRangeExpressionGenerator(
|
||||||
operatorReference: KtSimpleNameExpression,
|
operatorReference: KtSimpleNameExpression,
|
||||||
private val boundedValue: BoundedValue,
|
private val boundedValue: BoundedValue,
|
||||||
private val comparisonGenerator: ComparisonGenerator
|
private val comparisonGenerator: ComparisonGenerator
|
||||||
+19
-7
@@ -22,14 +22,26 @@ fun z(i: Int): Z {
|
|||||||
return Z(i)
|
return Z(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun zr(i: Int, j: Int) = z(i) .. z(j)
|
||||||
expectOrder("z0 in z1 .. z3", "z:1 z:3 z:0 c:1,0 ") { z(0) in z(1) .. z(3) }
|
|
||||||
expectOrder("z2 in z1 .. z3", "z:1 z:3 z:2 c:1,2 c:3,2 ") { z(2) in z(1) .. z(3) }
|
|
||||||
expectOrder("z4 in z1 .. z3", "z:1 z:4 z:2 c:1,2 c:4,2 ") { z(2) in z(1) .. z(4) }
|
|
||||||
|
|
||||||
expectOrder("z0 !in z1 .. z3", "z:1 z:3 z:0 c:1,0 ") { z(0) !in z(1) .. z(3) }
|
fun box(): String {
|
||||||
expectOrder("z2 !in z1 .. z3", "z:1 z:3 z:2 c:1,2 c:3,2 ") { z(2) !in z(1) .. z(3) }
|
expectOrder("#1", "z:1 z:3 z:0 c:0,1 ") { z(0) in z(1) .. z(3) }
|
||||||
expectOrder("z4 !in z1 .. z3", "z:1 z:4 z:2 c:1,2 c:4,2 ") { z(2) !in z(1) .. z(4) }
|
expectOrder("#1a", "z:1 z:3 z:0 c:0,1 ") { z(0) in zr(1, 3) }
|
||||||
|
|
||||||
|
expectOrder("#2", "z:1 z:3 z:2 c:2,1 c:2,3 ") { z(2) in z(1) .. z(3) }
|
||||||
|
expectOrder("#2a", "z:1 z:3 z:2 c:2,1 c:2,3 ") { z(2) in zr(1, 3) }
|
||||||
|
|
||||||
|
expectOrder("#3", "z:1 z:3 z:4 c:4,1 c:4,3 ") { z(4) in z(1) .. z(3) }
|
||||||
|
expectOrder("#3a", "z:1 z:3 z:4 c:4,1 c:4,3 ") { z(4) in zr(1, 3) }
|
||||||
|
|
||||||
|
expectOrder("#4", "z:1 z:3 z:0 c:0,1 ") { z(0) !in z(1) .. z(3) }
|
||||||
|
expectOrder("#4a", "z:1 z:3 z:0 c:0,1 ") { z(0) !in zr(1, 3) }
|
||||||
|
|
||||||
|
expectOrder("#5", "z:1 z:3 z:2 c:2,1 c:2,3 ") { z(2) !in z(1) .. z(3) }
|
||||||
|
expectOrder("#5a", "z:1 z:3 z:2 c:2,1 c:2,3 ") { z(2) !in zr(1, 3) }
|
||||||
|
|
||||||
|
expectOrder("#6", "z:1 z:3 z:4 c:4,1 c:4,3 ") { z(4) !in z(1) .. z(3) }
|
||||||
|
expectOrder("#6a", "z:1 z:3 z:4 c:4,1 c:4,3 ") { z(4) !in zr(1, 3) }
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user