Fix tests for in/in! with primitive range literals
- ClosedFloatingPointRange<T> - IntRange, etc - extensions in kotlin.ranges for ranges of Byte/Int/Short
This commit is contained in:
@@ -21,19 +21,10 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns.RANGES_PACKAGE_FQ_NAME
|
||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitiveNumberClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtBinaryExpression
|
||||
import org.jetbrains.kotlin.psi.KtExpression
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCallWithAssert
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
private val RANGE_TO_ELEMENT_TYPE: Map<FqName, PrimitiveType> =
|
||||
supportedRangeTypes().associateBy {
|
||||
@@ -77,8 +68,8 @@ fun isRangeOrProgression(className: FqName) =
|
||||
getPrimitiveRangeOrProgressionElementType(className) != null
|
||||
|
||||
fun isPrimitiveNumberRangeTo(rangeTo: CallableDescriptor) =
|
||||
"rangeTo" == rangeTo.name.asString() &&
|
||||
isPrimitiveNumberClassDescriptor(rangeTo.containingDeclaration)
|
||||
"rangeTo" == rangeTo.name.asString() && isPrimitiveNumberClassDescriptor(rangeTo.containingDeclaration) ||
|
||||
isPrimitiveRangeToExtension(rangeTo)
|
||||
|
||||
private fun isPrimitiveRangeToExtension(descriptor: CallableDescriptor): Boolean {
|
||||
if (!isTopLevelInPackage(descriptor, "rangeTo", "kotlin.ranges")) return false
|
||||
@@ -139,7 +130,6 @@ fun isComparableRangeTo(descriptor: CallableDescriptor): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
fun isClosedRangeContains(descriptor: CallableDescriptor): Boolean {
|
||||
if (descriptor.name.asString() != "contains") return false
|
||||
val containingClassDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return false
|
||||
@@ -148,40 +138,47 @@ fun isClosedRangeContains(descriptor: CallableDescriptor): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
fun isPrimitiveRangeToExtension(operationReference: KtSimpleNameExpression, bindingContext: BindingContext): Boolean {
|
||||
val resolvedCall = operationReference.getResolvedCallWithAssert(bindingContext)
|
||||
val receiver = resolvedCall.dispatchReceiver as? ExpressionReceiver ?: return false
|
||||
fun isPrimitiveRangeContains(descriptor: CallableDescriptor): Boolean {
|
||||
if (descriptor.name.asString() != "contains") return false
|
||||
val dispatchReceiverType = descriptor.dispatchReceiverParameter?.type ?: return false
|
||||
if (!isPrimitiveRange(dispatchReceiverType)) return false
|
||||
|
||||
/*
|
||||
* Range is optimizable if
|
||||
* receiver is a call for 'rangeTo' from stdlib package
|
||||
* and its argument has same primitive type as generic range parameter.
|
||||
* For non-matching primitive types (e.g. int in double range)
|
||||
* dispatch receiver will be null, because extension method will be called.
|
||||
*/
|
||||
|
||||
val resolvedReceiver = receiver.expression.getResolvedCall(bindingContext) ?: return false
|
||||
return isPrimitiveRangeToExtension(resolvedReceiver.resultingDescriptor)
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks whether for expression 'x in a..b' a..b is primitive integral range
|
||||
* with same type as x.
|
||||
*/
|
||||
fun isPrimitiveRangeSpecializationOfType(
|
||||
argumentType: Type,
|
||||
rangeExpression: KtExpression,
|
||||
bindingContext: BindingContext
|
||||
): Boolean {
|
||||
if (rangeExpression is KtBinaryExpression && rangeExpression.operationReference.getReferencedNameElementType() === KtTokens.RANGE) {
|
||||
val kotlinType = bindingContext.getType(rangeExpression)!!
|
||||
val descriptor = kotlinType.constructor.declarationDescriptor ?: return false
|
||||
val fqName = DescriptorUtils.getFqName(descriptor)
|
||||
return (fqName == KotlinBuiltIns.FQ_NAMES.longRange && argumentType === Type.LONG_TYPE) ||
|
||||
(fqName == KotlinBuiltIns.FQ_NAMES.charRange || fqName == KotlinBuiltIns.FQ_NAMES.intRange) && AsmUtil.isIntPrimitive(argumentType)
|
||||
}
|
||||
fun isIntPrimitiveRangeExtensionForInt(descriptor: CallableDescriptor): Boolean {
|
||||
if (descriptor.name.asString() != "contains") return false
|
||||
|
||||
return false
|
||||
val extensionReceiverType = descriptor.extensionReceiverParameter?.type ?: return false
|
||||
val extensionReceiverClassDescriptor = extensionReceiverType.constructor.declarationDescriptor as? ClassDescriptor ?: return false
|
||||
if (!isTopLevelInPackage(extensionReceiverClassDescriptor, "ClosedRange", "kotlin.ranges")) return false
|
||||
|
||||
val rangeElementType = extensionReceiverType.arguments.singleOrNull()?.type ?: return false
|
||||
if (!isIntPrimitiveType(rangeElementType)) return false
|
||||
|
||||
val argumentType = descriptor.valueParameters.singleOrNull()?.type ?: return false
|
||||
if (!isIntPrimitiveType(argumentType)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun isIntPrimitiveType(type: KotlinType) =
|
||||
KotlinBuiltIns.isByte(type) ||
|
||||
KotlinBuiltIns.isShort(type) ||
|
||||
KotlinBuiltIns.isInt(type)
|
||||
|
||||
fun isClosedFloatingPointRangeContains(descriptor: CallableDescriptor): Boolean {
|
||||
if (descriptor.name.asString() != "contains") return false
|
||||
val containingClassDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return false
|
||||
if (!isTopLevelInPackage(containingClassDescriptor, "ClosedFloatingPointRange", "kotlin.ranges")) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
fun getClosedFloatingPointRangeElementType(rangeType: KotlinType): KotlinType? {
|
||||
val classDescriptor = rangeType.constructor.declarationDescriptor as? ClassDescriptor ?: return null
|
||||
if (!isTopLevelInPackage(classDescriptor, "ClosedFloatingPointRange", "kotlin.ranges")) return null
|
||||
return rangeType.arguments.singleOrNull()?.type
|
||||
}
|
||||
|
||||
private fun isTopLevelInPackage(descriptor: DeclarationDescriptor, name: String, packageName: String): Boolean {
|
||||
|
||||
+24
-1
@@ -16,8 +16,11 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.range
|
||||
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.getClosedFloatingPointRangeElementType
|
||||
import org.jetbrains.kotlin.codegen.getPrimitiveRangeElementType
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
@@ -31,7 +34,8 @@ abstract class AbstractInPrimitiveNumberRangeExpressionGenerator(
|
||||
rangeCall: ResolvedCall<*>,
|
||||
isInclusiveHighBound: Boolean
|
||||
) : AbstractInRangeWithKnownBoundsExpressionGenerator(
|
||||
codegen, operatorReference, isInclusiveHighBound, AsmTypes.valueTypeForPrimitive(getPrimitiveRangeElementType(rangeCall.resultingDescriptor.returnType!!))
|
||||
codegen, operatorReference, isInclusiveHighBound,
|
||||
getAsmRangeElementTypeForPrimitiveRange(rangeCall.resultingDescriptor)
|
||||
) {
|
||||
override val comparisonGenerator: ComparisonGenerator =
|
||||
when (asmElementType) {
|
||||
@@ -92,3 +96,22 @@ abstract class AbstractInPrimitiveNumberRangeExpressionGenerator(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal fun getAsmRangeElementTypeForPrimitiveRange(rangeCallee: CallableDescriptor): Type {
|
||||
val rangeType = rangeCallee.returnType!!
|
||||
|
||||
getPrimitiveRangeElementType(rangeType)?.let {
|
||||
return AsmTypes.valueTypeForPrimitive(it)
|
||||
}
|
||||
|
||||
val floatingPointElementType = getClosedFloatingPointRangeElementType(rangeType) ?:
|
||||
throw AssertionError("Unexpected range type: $rangeType")
|
||||
|
||||
if (KotlinBuiltIns.isDouble(floatingPointElementType))
|
||||
return Type.DOUBLE_TYPE
|
||||
else if (KotlinBuiltIns.isFloat(floatingPointElementType))
|
||||
return Type.FLOAT_TYPE
|
||||
else
|
||||
throw AssertionError("Unexpected ClosedFloatingPointRange element type: $floatingPointElementType")
|
||||
}
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ class InPrimitiveNumberRangeLiteralGenerator(
|
||||
operatorReference: KtSimpleNameExpression,
|
||||
rangeCall: ResolvedCall<*>
|
||||
) : AbstractInPrimitiveNumberRangeExpressionGenerator(codegen, operatorReference, rangeCall, isInclusiveHighBound = true) {
|
||||
private val from: ReceiverValue = rangeCall.dispatchReceiver!!
|
||||
private val from: ReceiverValue = rangeCall.dispatchReceiver ?: rangeCall.extensionReceiver!!
|
||||
private val to: KtExpression = ExpressionCodegen.getSingleArgumentExpression(rangeCall)!!
|
||||
|
||||
override fun genLowBound(): StackValue = codegen.generateReceiverValue(from, false)
|
||||
|
||||
@@ -16,10 +16,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.codegen.range
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.forLoop.*
|
||||
import org.jetbrains.kotlin.codegen.isClosedRangeContains
|
||||
import org.jetbrains.kotlin.codegen.isPrimitiveRange
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.psi.KtForExpression
|
||||
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
|
||||
@@ -88,9 +86,11 @@ abstract class CallIntrinsicRangeValue(protected val rangeCall: ResolvedCall<out
|
||||
|
||||
abstract class PrimitiveNumberRangeIntrinsicRangeValue(rangeCall: ResolvedCall<out CallableDescriptor>): CallIntrinsicRangeValue(rangeCall) {
|
||||
override fun isIntrinsicInCall(resolvedCallForIn: ResolvedCall<out CallableDescriptor>) =
|
||||
resolvedCallForIn.resultingDescriptor.dispatchReceiverParameter?.let {
|
||||
isPrimitiveRange(it.type)
|
||||
} ?: false
|
||||
resolvedCallForIn.resultingDescriptor.let {
|
||||
isPrimitiveRangeContains(it) ||
|
||||
isClosedFloatingPointRangeContains(it) ||
|
||||
isIntPrimitiveRangeExtensionForInt(it)
|
||||
}
|
||||
}
|
||||
|
||||
class PrimitiveNumberRangeLiteralRangeValue(rangeCall: ResolvedCall<out CallableDescriptor>): PrimitiveNumberRangeIntrinsicRangeValue(rangeCall) {
|
||||
|
||||
@@ -1,28 +1,20 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun Byte.in2(left: Byte, right: Byte): Boolean {
|
||||
return this in left..right
|
||||
}
|
||||
fun Byte.inByte(left: Byte, right: Byte) = this in left..right
|
||||
|
||||
fun inInt(x: Int, left: Int, right: Int): Boolean {
|
||||
return x in left..right
|
||||
}
|
||||
fun Short.inInt(left: Int, right: Int) = this in left .. right
|
||||
|
||||
fun inDouble(x: Double, left: Double, right: Double): Boolean {
|
||||
return x in left..right
|
||||
}
|
||||
fun Short.inByte(left: Byte, right: Byte) = this in left..right
|
||||
|
||||
fun inFloat(x: Float, left: Float, right: Float): Boolean {
|
||||
return x in left..right
|
||||
}
|
||||
fun inInt(x: Int, left: Int, right: Int) = x in left..right
|
||||
|
||||
fun inLong(x: Long, left: Long, right: Long): Boolean {
|
||||
return x in left..right
|
||||
}
|
||||
fun inDouble(x: Double, left: Double, right: Double) = x in left..right
|
||||
|
||||
fun inCharWithNullableParameter(x: Char?, left: Char, right: Char): Boolean {
|
||||
return x!! in left..right
|
||||
}
|
||||
fun inFloat(x: Float, left: Float, right: Float) = x in left..right
|
||||
|
||||
fun inLong(x: Long, left: Long, right: Long) = x in left..right
|
||||
|
||||
fun inCharWithNullableParameter(x: Char?, left: Char, right: Char) = x!! in left..right
|
||||
|
||||
// 0 INVOKESPECIAL
|
||||
// 0 NEW
|
||||
|
||||
Reference in New Issue
Block a user