[FIR] Introduce new kind of integer literal types for constant operator calls
This commit is contained in:
committed by
teamcity
parent
bb5217be72
commit
b980f5ab32
+130
-84
@@ -9,35 +9,28 @@ import org.jetbrains.kotlin.fir.isLong
|
||||
import org.jetbrains.kotlin.fir.isULong
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.ConeClassLikeLookupTagImpl
|
||||
import org.jetbrains.kotlin.fir.types.ConeIntegerLiteralTypeExtensions.approximateIntegerLiteralBounds
|
||||
import org.jetbrains.kotlin.fir.types.ConeIntegerLiteralTypeExtensions.createClassLikeType
|
||||
import org.jetbrains.kotlin.fir.types.ConeIntegerLiteralTypeExtensions.createSupertypeList
|
||||
import org.jetbrains.kotlin.fir.types.ConeIntegerLiteralTypeExtensions.getApproximatedTypeImpl
|
||||
import org.jetbrains.kotlin.fir.types.ConeIntegerLiteralTypeExtensions.withNullability
|
||||
import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.types.model.SimpleTypeMarker
|
||||
|
||||
class ConeIntegerLiteralTypeImpl(
|
||||
class ConeIntegerLiteralConstantTypeImpl(
|
||||
value: Long,
|
||||
override val possibleTypes: Collection<ConeClassLikeType>,
|
||||
isUnsigned: Boolean,
|
||||
nullability: ConeNullability
|
||||
) : ConeIntegerLiteralType(value, isUnsigned, nullability) {
|
||||
override val attributes: ConeAttributes
|
||||
get() = ConeAttributes.Empty
|
||||
|
||||
) : ConeIntegerLiteralConstantType(value, isUnsigned, nullability) {
|
||||
override val supertypes: List<ConeClassLikeType> by lazy {
|
||||
listOf(
|
||||
createType(StandardClassIds.Number),
|
||||
ConeClassLikeTypeImpl(COMPARABLE_TAG, arrayOf(ConeKotlinTypeProjectionIn(this)), false)
|
||||
)
|
||||
createSupertypeList(this)
|
||||
}
|
||||
|
||||
override fun getApproximatedType(expectedType: ConeKotlinType?): ConeClassLikeType {
|
||||
val expectedTypeForApproximation = (expectedType?.lowerBoundIfFlexible() as? ConeClassLikeType)
|
||||
?.withNullability(ConeNullability.NOT_NULL)
|
||||
val approximatedType = when (expectedTypeForApproximation) {
|
||||
null, !in possibleTypes -> possibleTypes.first()
|
||||
else -> expectedTypeForApproximation
|
||||
}
|
||||
return approximatedType.withNullability(nullability)
|
||||
return getApproximatedTypeImpl(expectedType)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalUnsignedTypes::class)
|
||||
@@ -81,7 +74,7 @@ class ConeIntegerLiteralTypeImpl(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ConeIntegerLiteralTypeImpl(value, possibleTypes, isUnsigned, nullability)
|
||||
ConeIntegerLiteralConstantTypeImpl(value, possibleTypes, isUnsigned, nullability)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,61 +89,42 @@ class ConeIntegerLiteralTypeImpl(
|
||||
private val UBYTE_RANGE = UByte.MIN_VALUE.toLong()..UByte.MAX_VALUE.toLong()
|
||||
private val USHORT_RANGE = UShort.MIN_VALUE.toLong()..UShort.MAX_VALUE.toLong()
|
||||
private val UINT_RANGE = UInt.MIN_VALUE.toLong()..UInt.MAX_VALUE.toLong()
|
||||
|
||||
private val COMPARABLE_TAG = ConeClassLikeLookupTagImpl(StandardClassIds.Comparable)
|
||||
|
||||
fun findCommonSuperType(types: Collection<SimpleTypeMarker>): SimpleTypeMarker? {
|
||||
return findCommonSuperTypeOrIntersectionType(types, Mode.COMMON_SUPER_TYPE)
|
||||
}
|
||||
|
||||
fun findIntersectionType(types: Collection<SimpleTypeMarker>): SimpleTypeMarker? {
|
||||
return findCommonSuperTypeOrIntersectionType(types, Mode.INTERSECTION_TYPE)
|
||||
}
|
||||
|
||||
private enum class Mode {
|
||||
COMMON_SUPER_TYPE, INTERSECTION_TYPE
|
||||
}
|
||||
|
||||
/**
|
||||
* intersection(ILT(types), PrimitiveType) = commonSuperType(ILT(types), PrimitiveType) =
|
||||
* PrimitiveType in types -> PrimitiveType
|
||||
* PrimitiveType !in types -> null
|
||||
*
|
||||
* intersection(ILT(types_1), ILT(types_2)) = ILT(types_1 union types_2)
|
||||
*
|
||||
* commonSuperType(ILT(types_1), ILT(types_2)) = ILT(types_1 intersect types_2)
|
||||
*/
|
||||
private fun findCommonSuperTypeOrIntersectionType(types: Collection<SimpleTypeMarker>, mode: Mode): SimpleTypeMarker? {
|
||||
if (types.isEmpty()) return null
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return types.reduce { left: SimpleTypeMarker?, right: SimpleTypeMarker? -> fold(left, right, mode) }
|
||||
}
|
||||
|
||||
private fun fold(left: SimpleTypeMarker?, right: SimpleTypeMarker?, mode: Mode): SimpleTypeMarker? {
|
||||
if (left == null || right == null) return null
|
||||
return when {
|
||||
left is ConeIntegerLiteralType && right is ConeIntegerLiteralType ->
|
||||
fold(left, right, mode)
|
||||
|
||||
left is ConeIntegerLiteralType -> fold(left, right)
|
||||
right is ConeIntegerLiteralType -> fold(right, left)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun fold(left: ConeIntegerLiteralType, right: ConeIntegerLiteralType, mode: Mode): ConeIntegerLiteralType {
|
||||
val possibleTypes = when (mode) {
|
||||
Mode.COMMON_SUPER_TYPE -> left.possibleTypes intersect right.possibleTypes
|
||||
Mode.INTERSECTION_TYPE -> left.possibleTypes union right.possibleTypes
|
||||
}
|
||||
return ConeIntegerLiteralTypeImpl(left.value, possibleTypes, left.isUnsigned, left.nullability)
|
||||
}
|
||||
|
||||
private fun fold(left: ConeIntegerLiteralType, right: SimpleTypeMarker): SimpleTypeMarker? =
|
||||
if (right in left.possibleTypes) right else null
|
||||
}
|
||||
}
|
||||
|
||||
class ConeIntegerConstantOperatorTypeImpl(
|
||||
isUnsigned: Boolean,
|
||||
nullability: ConeNullability
|
||||
) : ConeIntegerConstantOperatorType(isUnsigned, nullability) {
|
||||
override val possibleTypes: Collection<ConeClassLikeType> = when (isUnsigned) {
|
||||
false -> setOf(
|
||||
createClassLikeType(StandardClassIds.Int),
|
||||
createClassLikeType(StandardClassIds.Long),
|
||||
)
|
||||
true -> setOf(
|
||||
createClassLikeType(StandardClassIds.UInt),
|
||||
createClassLikeType(StandardClassIds.ULong),
|
||||
)
|
||||
}
|
||||
|
||||
override val supertypes: List<ConeClassLikeType> by lazy {
|
||||
createSupertypeList(this)
|
||||
}
|
||||
|
||||
override fun getApproximatedType(expectedType: ConeKotlinType?): ConeClassLikeType {
|
||||
return getApproximatedTypeImpl(expectedType)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This methods detects common super type only for special rules for integer literal types
|
||||
* If it returns null then CST will be found by regular rules using real supertypes
|
||||
* of integer literal types
|
||||
*/
|
||||
fun ConeIntegerLiteralType.Companion.findCommonSuperType(types: Collection<SimpleTypeMarker>): SimpleTypeMarker? {
|
||||
return ConeIntegerLiteralTypeExtensions.findCommonSuperType(types)
|
||||
}
|
||||
|
||||
fun ConeKotlinType.approximateIntegerLiteralType(expectedType: ConeKotlinType? = null): ConeKotlinType {
|
||||
return when (this) {
|
||||
is ConeIntegerLiteralType -> getApproximatedType(expectedType)
|
||||
@@ -159,26 +133,98 @@ fun ConeKotlinType.approximateIntegerLiteralType(expectedType: ConeKotlinType? =
|
||||
}
|
||||
}
|
||||
|
||||
private fun ConeFlexibleType.approximateIntegerLiteralBounds(expectedType: ConeKotlinType? = null): ConeFlexibleType {
|
||||
val newLowerBound = lowerBound.approximateIntegerLiteralType(expectedType)
|
||||
val newUpperBound = upperBound.approximateIntegerLiteralType(expectedType)
|
||||
private object ConeIntegerLiteralTypeExtensions {
|
||||
private val COMPARABLE_TAG = ConeClassLikeLookupTagImpl(StandardClassIds.Comparable)
|
||||
|
||||
if (newLowerBound !== lowerBound || newUpperBound !== upperBound) {
|
||||
return ConeFlexibleType(
|
||||
newLowerBound.lowerBoundIfFlexible(),
|
||||
newUpperBound.upperBoundIfFlexible()
|
||||
fun createSupertypeList(type: ConeIntegerLiteralType): List<ConeClassLikeType> {
|
||||
return listOf(
|
||||
createClassLikeType(StandardClassIds.Number),
|
||||
ConeClassLikeTypeImpl(COMPARABLE_TAG, arrayOf(ConeKotlinTypeProjectionIn(type)), false)
|
||||
)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
fun createClassLikeType(classId: ClassId): ConeClassLikeType {
|
||||
return ConeClassLikeTypeImpl(ConeClassLikeLookupTagImpl(classId), emptyArray(), false)
|
||||
}
|
||||
|
||||
private fun ConeClassLikeType.withNullability(nullability: ConeNullability): ConeClassLikeType {
|
||||
if (nullability == this.nullability) return this
|
||||
fun ConeIntegerLiteralType.getApproximatedTypeImpl(expectedType: ConeKotlinType?): ConeClassLikeType {
|
||||
val expectedTypeForApproximation = (expectedType?.lowerBoundIfFlexible() as? ConeClassLikeType)
|
||||
?.withNullability(ConeNullability.NOT_NULL)
|
||||
val approximatedType = when (expectedTypeForApproximation) {
|
||||
null, !in possibleTypes -> possibleTypes.first()
|
||||
else -> expectedTypeForApproximation
|
||||
}
|
||||
return approximatedType.withNullability(nullability)
|
||||
}
|
||||
|
||||
return when (this) {
|
||||
is ConeErrorType -> this
|
||||
is ConeClassLikeTypeImpl -> ConeClassLikeTypeImpl(lookupTag, typeArguments, nullability.isNullable)
|
||||
else -> error("sealed")
|
||||
|
||||
fun findCommonSuperType(types: Collection<SimpleTypeMarker>): SimpleTypeMarker? {
|
||||
if (types.isEmpty()) return null
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return types.reduce { left: SimpleTypeMarker?, right: SimpleTypeMarker? -> commonSuperType(left, right) }
|
||||
}
|
||||
|
||||
private fun commonSuperType(left: SimpleTypeMarker?, right: SimpleTypeMarker?): SimpleTypeMarker? {
|
||||
if (left == null || right == null) return null
|
||||
|
||||
return when {
|
||||
left is ConeIntegerLiteralType && right !is ConeIntegerLiteralType -> {
|
||||
commonSuperTypeBetweenIntegerTypeAndRegularType(left, right)
|
||||
}
|
||||
|
||||
right is ConeIntegerLiteralType && left !is ConeIntegerLiteralType -> {
|
||||
commonSuperTypeBetweenIntegerTypeAndRegularType(right, left)
|
||||
}
|
||||
|
||||
left is ConeIntegerLiteralConstantType && right is ConeIntegerLiteralConstantType -> {
|
||||
commonSuperTypeBetweenTwoConstantTypes(left, right)
|
||||
}
|
||||
|
||||
left is ConeIntegerConstantOperatorType -> left
|
||||
right is ConeIntegerConstantOperatorType -> right
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun commonSuperTypeBetweenIntegerTypeAndRegularType(
|
||||
integerLiteralType: ConeIntegerLiteralType,
|
||||
regularType: SimpleTypeMarker
|
||||
): SimpleTypeMarker? {
|
||||
return when (regularType) {
|
||||
in integerLiteralType.possibleTypes -> regularType
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun commonSuperTypeBetweenTwoConstantTypes(
|
||||
left: ConeIntegerLiteralConstantType,
|
||||
right: ConeIntegerLiteralConstantType
|
||||
): ConeIntegerLiteralConstantType {
|
||||
val possibleTypes = left.possibleTypes intersect right.possibleTypes
|
||||
return ConeIntegerLiteralConstantTypeImpl(left.value, possibleTypes, left.isUnsigned, left.nullability)
|
||||
}
|
||||
|
||||
fun ConeFlexibleType.approximateIntegerLiteralBounds(expectedType: ConeKotlinType? = null): ConeFlexibleType {
|
||||
val newLowerBound = lowerBound.approximateIntegerLiteralType(expectedType)
|
||||
val newUpperBound = upperBound.approximateIntegerLiteralType(expectedType)
|
||||
|
||||
if (newLowerBound !== lowerBound || newUpperBound !== upperBound) {
|
||||
return ConeFlexibleType(
|
||||
newLowerBound.lowerBoundIfFlexible(),
|
||||
newUpperBound.upperBoundIfFlexible()
|
||||
)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
fun ConeClassLikeType.withNullability(nullability: ConeNullability): ConeClassLikeType {
|
||||
if (nullability == this.nullability) return this
|
||||
|
||||
return when (this) {
|
||||
is ConeErrorType -> this
|
||||
is ConeClassLikeTypeImpl -> ConeClassLikeTypeImpl(lookupTag, typeArguments, nullability.isNullable)
|
||||
else -> error("sealed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,14 +8,15 @@ package org.jetbrains.kotlin.fir.types
|
||||
object ConeKotlinTypeComparator : Comparator<ConeKotlinType> {
|
||||
private val ConeKotlinType.priority : Int
|
||||
get() = when (this) {
|
||||
is ConeErrorType -> 8
|
||||
is ConeLookupTagBasedType -> 7
|
||||
is ConeFlexibleType -> 6
|
||||
is ConeCapturedType -> 5
|
||||
is ConeDefinitelyNotNullType -> 4
|
||||
is ConeIntersectionType -> 3
|
||||
is ConeStubType -> 2
|
||||
is ConeIntegerLiteralType -> 1
|
||||
is ConeErrorType -> 9
|
||||
is ConeLookupTagBasedType -> 8
|
||||
is ConeFlexibleType -> 7
|
||||
is ConeCapturedType -> 6
|
||||
is ConeDefinitelyNotNullType -> 5
|
||||
is ConeIntersectionType -> 4
|
||||
is ConeStubType -> 3
|
||||
is ConeIntegerLiteralConstantType -> 2
|
||||
is ConeIntegerConstantOperatorType -> 1
|
||||
else -> 0
|
||||
}
|
||||
|
||||
@@ -151,8 +152,8 @@ object ConeKotlinTypeComparator : Comparator<ConeKotlinType> {
|
||||
}
|
||||
return compare(a.nullability, b.nullability)
|
||||
}
|
||||
is ConeIntegerLiteralType -> {
|
||||
require(b is ConeIntegerLiteralType) {
|
||||
is ConeIntegerLiteralConstantType -> {
|
||||
require(b is ConeIntegerLiteralConstantType) {
|
||||
"priority is inconsistent: ${a.render()} v.s. ${b.render()}"
|
||||
}
|
||||
val valueDiff = a.value - b.value
|
||||
@@ -166,6 +167,9 @@ object ConeKotlinTypeComparator : Comparator<ConeKotlinType> {
|
||||
// Can't compare individual types from each side, since their orders are not guaranteed.
|
||||
return a.hashCode() - b.hashCode()
|
||||
}
|
||||
is ConeIntegerConstantOperatorType -> {
|
||||
return compare(a.nullability, b.nullability)
|
||||
}
|
||||
else ->
|
||||
error("Unsupported type comparison: ${a.render()} v.s. ${b.render()}")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user