Support unsigned integers in intrinsic range values

This commit is contained in:
Dmitry Petrov
2018-12-27 14:54:06 +03:00
parent d22a31cb90
commit 68e2d8dcd9
13 changed files with 496 additions and 20 deletions
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.codegen.range
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.KotlinBuiltIns.RANGES_PACKAGE_FQ_NAME
import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.builtins.UnsignedTypes
import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitiveNumberClassDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.diagnostics.PsiDiagnosticUtils
@@ -74,31 +75,30 @@ private const val CLOSED_FLOATING_POINT_RANGE_FQN = "kotlin.ranges.ClosedFloatin
private const val COMPARABLE_RANGE_FQN = "kotlin.ranges.ComparableRange"
private const val UINT_RANGE_FQN = "kotlin.ranges.UIntRange"
private const val ULONG_RANGE_FQN = "kotlin.ranges.ULongRange"
private const val UINT_PROGRESSION_FQN = "kotlin.ranges.UIntProgression"
private const val ULONG_PROGRESSION_FQN = "kotlin.ranges.ULongProgression"
fun getRangeOrProgressionElementType(rangeType: KotlinType): KotlinType? {
val rangeClassDescriptor = rangeType.constructor.declarationDescriptor as? ClassDescriptor ?: return null
val builtIns = rangeClassDescriptor.builtIns
return when (rangeClassDescriptor.fqNameSafe.asString()) {
CHAR_RANGE_FQN -> builtIns.charType
INT_RANGE_FQN -> builtIns.intType
LONG_RANGE_FQN -> builtIns.longType
CHAR_PROGRESSION_FQN -> builtIns.charType
INT_PROGRESSION_FQN -> builtIns.intType
LONG_PROGRESSION_FQN -> builtIns.longType
CHAR_RANGE_FQN, CHAR_PROGRESSION_FQN -> builtIns.charType
INT_RANGE_FQN, INT_PROGRESSION_FQN -> builtIns.intType
LONG_RANGE_FQN, LONG_PROGRESSION_FQN -> builtIns.longType
CLOSED_FLOAT_RANGE_FQN -> builtIns.floatType
CLOSED_DOUBLE_RANGE_FQN -> builtIns.doubleType
CLOSED_RANGE_FQN -> rangeType.arguments.singleOrNull()?.type
CLOSED_FLOATING_POINT_RANGE_FQN -> rangeType.arguments.singleOrNull()?.type
COMPARABLE_RANGE_FQN -> rangeType.arguments.singleOrNull()?.type
UINT_RANGE_FQN -> rangeClassDescriptor.findTypeInModuleByTopLevelClassFqName(KotlinBuiltIns.FQ_NAMES.uIntFqName)
ULONG_RANGE_FQN -> rangeClassDescriptor.findTypeInModuleByTopLevelClassFqName(KotlinBuiltIns.FQ_NAMES.uLongFqName)
UINT_RANGE_FQN, UINT_PROGRESSION_FQN ->
rangeClassDescriptor.findTypeInModuleByTopLevelClassFqName(KotlinBuiltIns.FQ_NAMES.uIntFqName)
ULONG_RANGE_FQN, ULONG_PROGRESSION_FQN ->
rangeClassDescriptor.findTypeInModuleByTopLevelClassFqName(KotlinBuiltIns.FQ_NAMES.uLongFqName)
else -> null
}
@@ -125,12 +125,10 @@ fun isPrimitiveNumberRangeTo(rangeTo: CallableDescriptor) =
isPrimitiveRangeToExtension(rangeTo)
fun isUnsignedIntegerRangeTo(rangeTo: CallableDescriptor) =
"rangeTo" == rangeTo.name.asString() && isUnsignedIntegerClass(rangeTo.containingDeclaration)
"rangeTo" == rangeTo.name.asString() && isUnsignedIntegerClassDescriptor(rangeTo.containingDeclaration)
fun isUnsignedIntegerClass(descriptor: DeclarationDescriptor) =
descriptor is ClassDescriptor && descriptor.defaultType.let { type ->
KotlinBuiltIns.isUByte(type) || KotlinBuiltIns.isUShort(type) || KotlinBuiltIns.isUInt(type) || KotlinBuiltIns.isULong(type)
}
fun isUnsignedIntegerClassDescriptor(descriptor: DeclarationDescriptor?) =
descriptor != null && UnsignedTypes.isUnsignedClass(descriptor)
private inline fun CallableDescriptor.isTopLevelExtensionOnType(
name: String,
@@ -152,11 +150,21 @@ fun isPrimitiveNumberDownTo(descriptor: CallableDescriptor) =
isPrimitiveNumberClassDescriptor(it.constructor.declarationDescriptor)
}
fun isUnsignedIntegerDownTo(descriptor: CallableDescriptor) =
descriptor.isTopLevelExtensionOnType("downTo", "kotlin.ranges") {
isUnsignedIntegerClassDescriptor(it.constructor.declarationDescriptor)
}
fun isPrimitiveNumberUntil(descriptor: CallableDescriptor) =
descriptor.isTopLevelExtensionOnType("until", "kotlin.ranges") {
isPrimitiveNumberClassDescriptor(it.constructor.declarationDescriptor)
}
fun isUnsignedIntegerUntil(descriptor: CallableDescriptor) =
descriptor.isTopLevelExtensionOnType("until", "kotlin.ranges") {
isUnsignedIntegerClassDescriptor(it.constructor.declarationDescriptor)
}
fun isArrayOrPrimitiveArrayIndices(descriptor: CallableDescriptor) =
descriptor.isTopLevelExtensionOnType("indices", "kotlin.collections") {
KotlinBuiltIns.isArray(it) || KotlinBuiltIns.isPrimitiveArray(it)
@@ -17,7 +17,7 @@
package org.jetbrains.kotlin.codegen.range
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.ExpressionCodegen
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
@@ -118,11 +118,11 @@ private fun ExpressionCodegen.createIntrinsifiedRangeValueOrNull(rangeCall: Reso
val rangeCallee = rangeCall.resultingDescriptor
return when {
isPrimitiveNumberRangeTo(rangeCallee) ->
isPrimitiveNumberRangeTo(rangeCallee) || isUnsignedIntegerRangeTo(rangeCallee) ->
PrimitiveNumberRangeLiteralRangeValue(rangeCall)
isPrimitiveNumberDownTo(rangeCallee) ->
isPrimitiveNumberDownTo(rangeCallee) || isUnsignedIntegerDownTo(rangeCallee) ->
DownToProgressionRangeValue(rangeCall)
isPrimitiveNumberUntil(rangeCallee) ->
isPrimitiveNumberUntil(rangeCallee) || isUnsignedIntegerUntil(rangeCallee) ->
PrimitiveNumberUntilRangeValue(rangeCall)
isArrayOrPrimitiveArrayIndices(rangeCallee) ->
ArrayIndicesRangeValue(rangeCall)
@@ -47,6 +47,10 @@ fun getComparisonGeneratorForKotlinType(kotlinType: KotlinType): ComparisonGener
FloatComparisonGenerator
KotlinBuiltIns.isDouble(kotlinType) ->
DoubleComparisonGenerator
KotlinBuiltIns.isUInt(kotlinType) ->
UIntComparisonGenerator
KotlinBuiltIns.isULong(kotlinType) ->
ULongComparisonGenerator
else ->
throw UnsupportedOperationException("Unexpected element type: $kotlinType")
}
@@ -0,0 +1,43 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.comparison
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
class UnsignedIntegerComparisonGenerator(
override val comparedType: Type,
private val compareMethodName: String
) : ComparisonGenerator {
private val compareMethodDescriptor = Type.getMethodDescriptor(Type.INT_TYPE, comparedType, comparedType)
override fun jumpIfGreaterOrEqual(v: InstructionAdapter, label: Label) {
compareAndJump(v, label, Opcodes.IFGE)
}
override fun jumpIfLessOrEqual(v: InstructionAdapter, label: Label) {
compareAndJump(v, label, Opcodes.IFLE)
}
override fun jumpIfGreater(v: InstructionAdapter, label: Label) {
compareAndJump(v, label, Opcodes.IFGT)
}
override fun jumpIfLess(v: InstructionAdapter, label: Label) {
compareAndJump(v, label, Opcodes.IFLT)
}
private fun compareAndJump(v: InstructionAdapter, label: Label, opcode: Int) {
v.invokestatic("kotlin/UnsignedKt", compareMethodName, compareMethodDescriptor, false)
v.visitJumpInsn(opcode, label)
}
}
val UIntComparisonGenerator = UnsignedIntegerComparisonGenerator(Type.INT_TYPE, "uintCompare")
val ULongComparisonGenerator = UnsignedIntegerComparisonGenerator(Type.LONG_TYPE, "ulongCompare")