KT-29229 Intrinsify 'in' operator for unsigned integer ranges
Support mixed type case, e.g., '[UByte] in [UIntRange]'.
This commit is contained in:
+11
-80
@@ -17,12 +17,11 @@
|
||||
package org.jetbrains.kotlin.codegen.range
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.UnsignedType
|
||||
import org.jetbrains.kotlin.builtins.UnsignedTypes
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.range.comparison.getComparisonGeneratorForKotlinType
|
||||
import org.jetbrains.kotlin.codegen.range.comparison.getComparisonGeneratorForRangeContainsCall
|
||||
import org.jetbrains.kotlin.codegen.range.comparison.getRangeContainsTypeInfo
|
||||
import org.jetbrains.kotlin.codegen.range.forLoop.ForInDefinitelySafeSimpleProgressionLoopGenerator
|
||||
import org.jetbrains.kotlin.codegen.range.forLoop.ForLoopGenerator
|
||||
import org.jetbrains.kotlin.codegen.range.inExpression.CallBasedInExpressionGenerator
|
||||
@@ -35,7 +34,6 @@ import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.constants.*
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
@@ -60,22 +58,20 @@ abstract class PrimitiveNumberRangeIntrinsicRangeValue(
|
||||
operatorReference: KtSimpleNameExpression,
|
||||
resolvedCall: ResolvedCall<out CallableDescriptor>
|
||||
): InExpressionGenerator {
|
||||
val comparisonGenerator = getComparisonGeneratorForRangeContainsCall(codegen, resolvedCall)
|
||||
val comparedType = comparisonGenerator?.comparedType
|
||||
val rangeContainsTypeInfo = getRangeContainsTypeInfo(resolvedCall)
|
||||
?: return CallBasedInExpressionGenerator(codegen, operatorReference)
|
||||
val comparisonGenerator = getComparisonGeneratorForRangeContainsCall(codegen, rangeContainsTypeInfo)
|
||||
?: return CallBasedInExpressionGenerator(codegen, operatorReference)
|
||||
|
||||
return when {
|
||||
comparisonGenerator == null -> CallBasedInExpressionGenerator(codegen, operatorReference)
|
||||
|
||||
comparedType == Type.DOUBLE_TYPE || comparedType == Type.FLOAT_TYPE -> {
|
||||
return when (comparisonGenerator.comparedType) {
|
||||
Type.DOUBLE_TYPE, Type.FLOAT_TYPE -> {
|
||||
val rangeLiteral = getBoundedValue(codegen) as? BoundedValue
|
||||
?: throw AssertionError("Floating point intrinsic range value should be a range literal")
|
||||
InFloatingPointRangeLiteralExpressionGenerator(operatorReference, rangeLiteral, comparisonGenerator, codegen.frameMap)
|
||||
}
|
||||
|
||||
else ->
|
||||
InIntegralContinuousRangeExpressionGenerator(
|
||||
operatorReference, getBoundedValue(codegen), comparisonGenerator, codegen.frameMap
|
||||
)
|
||||
else -> InIntegralContinuousRangeExpressionGenerator(
|
||||
operatorReference, rangeContainsTypeInfo, getBoundedValue(codegen), comparisonGenerator, codegen.frameMap
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,71 +90,6 @@ abstract class PrimitiveNumberRangeIntrinsicRangeValue(
|
||||
}
|
||||
}
|
||||
|
||||
private val StackValue.unsignedType: UnsignedType?
|
||||
get() = kotlinType?.let { UnsignedTypes.toUnsignedType(it) }
|
||||
|
||||
private fun coerceUnsignedToUInt(stackValue: StackValue, uIntKotlinType: KotlinType): StackValue {
|
||||
val valueKotlinType = stackValue.kotlinType
|
||||
val valueUnsignedType = stackValue.unsignedType
|
||||
?: throw AssertionError("Unsigned type expected: $valueKotlinType")
|
||||
|
||||
if (valueUnsignedType == UnsignedType.UINT) return stackValue
|
||||
|
||||
return StackValue.operation(Type.INT_TYPE, uIntKotlinType) { v ->
|
||||
stackValue.put(stackValue.type, valueKotlinType, v)
|
||||
when (valueUnsignedType) {
|
||||
UnsignedType.UBYTE -> {
|
||||
v.iconst(0xFF)
|
||||
v.and(Type.INT_TYPE)
|
||||
}
|
||||
|
||||
UnsignedType.USHORT -> {
|
||||
v.iconst(0xFFFF)
|
||||
v.and(Type.INT_TYPE)
|
||||
}
|
||||
|
||||
UnsignedType.ULONG -> {
|
||||
v.cast(Type.LONG_TYPE, Type.INT_TYPE)
|
||||
}
|
||||
|
||||
else -> throw AssertionError("Unexpected value type: $valueKotlinType")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun coerceUnsignedToULong(stackValue: StackValue, uLongKotlinType: KotlinType): StackValue {
|
||||
val valueKotlinType = stackValue.kotlinType
|
||||
val valueUnsignedType = stackValue.unsignedType
|
||||
?: throw AssertionError("Unsigned type expected: $valueKotlinType")
|
||||
|
||||
if (valueUnsignedType == UnsignedType.ULONG) return stackValue
|
||||
|
||||
return StackValue.operation(Type.LONG_TYPE, uLongKotlinType) { v ->
|
||||
stackValue.put(stackValue.type, valueKotlinType, v)
|
||||
when (valueUnsignedType) {
|
||||
UnsignedType.UBYTE -> {
|
||||
v.cast(Type.INT_TYPE, Type.LONG_TYPE)
|
||||
v.lconst(0xFF)
|
||||
v.and(Type.LONG_TYPE)
|
||||
}
|
||||
|
||||
UnsignedType.USHORT -> {
|
||||
v.cast(Type.INT_TYPE, Type.LONG_TYPE)
|
||||
v.lconst(0xFFFF)
|
||||
v.and(Type.LONG_TYPE)
|
||||
}
|
||||
|
||||
UnsignedType.UINT -> {
|
||||
v.cast(Type.INT_TYPE, Type.LONG_TYPE)
|
||||
v.lconst(0xFFFF_FFFFL)
|
||||
v.and(Type.LONG_TYPE)
|
||||
}
|
||||
|
||||
else -> throw AssertionError("Unexpected value type: $valueKotlinType")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fun createConstBoundedForLoopGeneratorOrNull(
|
||||
codegen: ExpressionCodegen,
|
||||
forExpression: KtForExpression,
|
||||
@@ -251,7 +182,7 @@ abstract class PrimitiveNumberRangeIntrinsicRangeValue(
|
||||
)
|
||||
|
||||
private fun isProhibitedCharConstEndValue(step: Int, endValue: Char) =
|
||||
endValue == if (step == 1) java.lang.Character.MAX_VALUE else java.lang.Character.MIN_VALUE
|
||||
endValue == if (step == 1) Char.MAX_VALUE else Char.MIN_VALUE
|
||||
|
||||
private fun isProhibitedIntConstEndValue(step: Int, endValue: Int) =
|
||||
endValue == if (step == 1) Int.MAX_VALUE else Int.MIN_VALUE
|
||||
|
||||
@@ -209,10 +209,12 @@ fun isPrimitiveRangeContains(descriptor: CallableDescriptor): Boolean {
|
||||
|
||||
fun isUnsignedIntegerRangeContains(descriptor: CallableDescriptor): Boolean {
|
||||
if (descriptor.name.asString() != "contains") return false
|
||||
val dispatchReceiverType = descriptor.dispatchReceiverParameter?.type ?: return false
|
||||
if (!isUnsignedRange(dispatchReceiverType)) return false
|
||||
|
||||
return true
|
||||
val dispatchReceiverType = descriptor.dispatchReceiverParameter?.type
|
||||
val extensionReceiverType = descriptor.extensionReceiverParameter?.type
|
||||
|
||||
return (dispatchReceiverType != null && isUnsignedRange(dispatchReceiverType)) ||
|
||||
(extensionReceiverType != null && isUnsignedRange(extensionReceiverType))
|
||||
}
|
||||
|
||||
fun isPrimitiveNumberRangeExtensionContainsPrimitiveNumber(descriptor: CallableDescriptor): Boolean {
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.codegen.range
|
||||
|
||||
import org.jetbrains.kotlin.builtins.UnsignedType
|
||||
import org.jetbrains.kotlin.builtins.UnsignedTypes
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
val StackValue.unsignedType: UnsignedType?
|
||||
get() = kotlinType?.let { UnsignedTypes.toUnsignedType(it) }
|
||||
|
||||
fun coerceUnsignedToUInt(stackValue: StackValue, uIntKotlinType: KotlinType): StackValue =
|
||||
coerceUnsignedToUInt(stackValue, stackValue.kotlinType, uIntKotlinType)
|
||||
|
||||
fun coerceUnsignedToUInt(
|
||||
stackValue: StackValue,
|
||||
valueKotlinType: KotlinType?,
|
||||
uIntKotlinType: KotlinType
|
||||
): StackValue {
|
||||
val valueUnsignedType = stackValue.unsignedType
|
||||
?: throw AssertionError("Unsigned type expected: $valueKotlinType")
|
||||
|
||||
if (valueUnsignedType == UnsignedType.UINT) return stackValue
|
||||
|
||||
return StackValue.operation(Type.INT_TYPE, uIntKotlinType) { v ->
|
||||
stackValue.put(stackValue.type, valueKotlinType, v)
|
||||
when (valueUnsignedType) {
|
||||
UnsignedType.UBYTE -> {
|
||||
v.iconst(0xFF)
|
||||
v.and(Type.INT_TYPE)
|
||||
}
|
||||
|
||||
UnsignedType.USHORT -> {
|
||||
v.iconst(0xFFFF)
|
||||
v.and(Type.INT_TYPE)
|
||||
}
|
||||
|
||||
UnsignedType.ULONG -> {
|
||||
v.cast(Type.LONG_TYPE, Type.INT_TYPE)
|
||||
}
|
||||
|
||||
else -> throw AssertionError("Unexpected value type: $valueKotlinType")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun coerceUnsignedToULong(stackValue: StackValue, uLongKotlinType: KotlinType): StackValue =
|
||||
coerceUnsignedToULong(stackValue, stackValue.kotlinType, uLongKotlinType)
|
||||
|
||||
fun coerceUnsignedToULong(
|
||||
stackValue: StackValue,
|
||||
valueKotlinType: KotlinType?,
|
||||
uLongKotlinType: KotlinType
|
||||
): StackValue {
|
||||
val valueUnsignedType = stackValue.unsignedType
|
||||
?: throw AssertionError("Unsigned type expected: $valueKotlinType")
|
||||
|
||||
if (valueUnsignedType == UnsignedType.ULONG) return stackValue
|
||||
|
||||
return StackValue.operation(Type.LONG_TYPE, uLongKotlinType) { v ->
|
||||
stackValue.put(stackValue.type, valueKotlinType, v)
|
||||
when (valueUnsignedType) {
|
||||
UnsignedType.UBYTE -> {
|
||||
v.cast(Type.INT_TYPE, Type.LONG_TYPE)
|
||||
v.lconst(0xFF)
|
||||
v.and(Type.LONG_TYPE)
|
||||
}
|
||||
|
||||
UnsignedType.USHORT -> {
|
||||
v.cast(Type.INT_TYPE, Type.LONG_TYPE)
|
||||
v.lconst(0xFFFF)
|
||||
v.and(Type.LONG_TYPE)
|
||||
}
|
||||
|
||||
UnsignedType.UINT -> {
|
||||
v.cast(Type.INT_TYPE, Type.LONG_TYPE)
|
||||
v.lconst(0xFFFF_FFFFL)
|
||||
v.and(Type.LONG_TYPE)
|
||||
}
|
||||
|
||||
else -> throw AssertionError("Unexpected value type: $valueKotlinType")
|
||||
}
|
||||
}
|
||||
}
|
||||
+22
-8
@@ -59,17 +59,25 @@ fun getComparisonGeneratorForKotlinType(kotlinType: KotlinType): ComparisonGener
|
||||
throw UnsupportedOperationException("Unexpected element type: $kotlinType")
|
||||
}
|
||||
|
||||
class RangeContainsTypeInfo(
|
||||
val rangeElementType: KotlinType,
|
||||
val valueParameterType: KotlinType
|
||||
)
|
||||
|
||||
fun getRangeContainsTypeInfo(call: ResolvedCall<out CallableDescriptor>): RangeContainsTypeInfo? {
|
||||
val descriptor = call.resultingDescriptor
|
||||
val receiverType = descriptor.extensionReceiverParameter?.type ?: descriptor.dispatchReceiverParameter?.type ?: return null
|
||||
val elementType = getRangeOrProgressionElementType(receiverType) ?: return null
|
||||
val valueParameterType = descriptor.valueParameters.singleOrNull()?.type ?: return null
|
||||
return RangeContainsTypeInfo(elementType, valueParameterType)
|
||||
}
|
||||
|
||||
fun getComparisonGeneratorForRangeContainsCall(
|
||||
codegen: ExpressionCodegen,
|
||||
call: ResolvedCall<out CallableDescriptor>
|
||||
rangeContainsTypeInfo: RangeContainsTypeInfo
|
||||
): ComparisonGenerator? {
|
||||
val descriptor = call.resultingDescriptor
|
||||
|
||||
val receiverType = descriptor.extensionReceiverParameter?.type ?: descriptor.dispatchReceiverParameter?.type ?: return null
|
||||
|
||||
val elementType = getRangeOrProgressionElementType(receiverType) ?: return null
|
||||
|
||||
val valueParameterType = descriptor.valueParameters.singleOrNull()?.type ?: return null
|
||||
val elementType = rangeContainsTypeInfo.rangeElementType
|
||||
val valueParameterType = rangeContainsTypeInfo.valueParameterType
|
||||
|
||||
val asmElementType = codegen.asmType(elementType)
|
||||
val asmValueParameterType = codegen.asmType(valueParameterType)
|
||||
@@ -78,6 +86,12 @@ fun getComparisonGeneratorForRangeContainsCall(
|
||||
asmElementType == asmValueParameterType ->
|
||||
getComparisonGeneratorForKotlinType(elementType)
|
||||
|
||||
KotlinBuiltIns.isUInt(elementType) ->
|
||||
UIntComparisonGenerator
|
||||
|
||||
KotlinBuiltIns.isULong(elementType) ->
|
||||
ULongComparisonGenerator
|
||||
|
||||
asmElementType.isPrimitiveIntOrCoercible() && asmValueParameterType.isPrimitiveIntOrCoercible() ->
|
||||
IntComparisonGenerator
|
||||
|
||||
|
||||
+24
-2
@@ -16,9 +16,13 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.range.inExpression
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.range.BoundedValue
|
||||
import org.jetbrains.kotlin.codegen.range.coerceUnsignedToUInt
|
||||
import org.jetbrains.kotlin.codegen.range.coerceUnsignedToULong
|
||||
import org.jetbrains.kotlin.codegen.range.comparison.ComparisonGenerator
|
||||
import org.jetbrains.kotlin.codegen.range.comparison.RangeContainsTypeInfo
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
@@ -27,6 +31,7 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
class InIntegralContinuousRangeExpressionGenerator(
|
||||
operatorReference: KtSimpleNameExpression,
|
||||
private val rangeContainsTypeInfo: RangeContainsTypeInfo,
|
||||
private val boundedValue: BoundedValue,
|
||||
private val comparisonGenerator: ComparisonGenerator,
|
||||
private val frameMap: FrameMap
|
||||
@@ -38,6 +43,7 @@ class InIntegralContinuousRangeExpressionGenerator(
|
||||
|
||||
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)
|
||||
@@ -54,7 +60,7 @@ class InIntegralContinuousRangeExpressionGenerator(
|
||||
|
||||
boundedValue.putHighLow(v, operandType)
|
||||
|
||||
arg1.put(operandType, v)
|
||||
putCoercedArgumentOnStack(v)
|
||||
v.store(arg1Var, operandType)
|
||||
v.load(arg1Var, operandType)
|
||||
|
||||
@@ -95,7 +101,7 @@ class InIntegralContinuousRangeExpressionGenerator(
|
||||
|
||||
boundedValue.putHighLow(v, operandType)
|
||||
|
||||
arg1.put(operandType, v)
|
||||
putCoercedArgumentOnStack(v)
|
||||
v.store(arg1Var, operandType)
|
||||
v.load(arg1Var, operandType)
|
||||
|
||||
@@ -127,5 +133,21 @@ class InIntegralContinuousRangeExpressionGenerator(
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun putCoercedArgumentOnStack(v: InstructionAdapter) {
|
||||
val argumentKotlinType = rangeContainsTypeInfo.valueParameterType
|
||||
val rangeElementKotlinType = rangeContainsTypeInfo.rangeElementType
|
||||
|
||||
val coercedValue = when {
|
||||
KotlinBuiltIns.isUInt(rangeElementKotlinType) ->
|
||||
coerceUnsignedToUInt(arg1, argumentKotlinType, rangeElementKotlinType)
|
||||
KotlinBuiltIns.isULong(rangeElementKotlinType) ->
|
||||
coerceUnsignedToULong(arg1, argumentKotlinType, rangeElementKotlinType)
|
||||
else ->
|
||||
arg1
|
||||
}
|
||||
|
||||
coercedValue.put(operandType, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user