[FIR] Introduce new kind of integer literal types for constant operator calls

This commit is contained in:
Dmitriy Novozhilov
2022-02-04 17:14:33 +03:00
committed by teamcity
parent bb5217be72
commit b980f5ab32
12 changed files with 185 additions and 111 deletions
@@ -436,7 +436,8 @@ internal class KtSymbolByFirBuilder private constructor(
is ConeIntersectionType -> KtFirIntersectionType(coneType, token, this@KtSymbolByFirBuilder)
is ConeDefinitelyNotNullType -> KtFirDefinitelyNotNullType(coneType, token, this@KtSymbolByFirBuilder)
is ConeCapturedType -> KtFirCapturedType(coneType, token, this@KtSymbolByFirBuilder)
is ConeIntegerLiteralType -> KtFirIntegerLiteralType(coneType, token, this@KtSymbolByFirBuilder)
is ConeIntegerLiteralConstantType -> KtFirIntegerLiteralType(coneType, token, this@KtSymbolByFirBuilder)
is ConeIntegerConstantOperatorType -> buildKtType(coneType.getApproximatedType())
is ConeStubTypeForChainInference -> {
// TODO this is a temporary hack to prevent FIR IDE from crashing on builder inference, see KT-50916
val typeVariable = coneType.constructor.variable as? ConeTypeParameterBasedTypeVariable
@@ -230,7 +230,7 @@ internal class KtFirIntersectionType(
}
internal class KtFirIntegerLiteralType(
override val coneType: ConeIntegerLiteralType,
override val coneType: ConeIntegerLiteralConstantType,
override val token: ValidityToken,
private val builder: KtSymbolByFirBuilder,
) : KtIntegerLiteralType(), KtFirType {
@@ -255,7 +255,8 @@ object ConeTypeCompatibilityChecker {
is ConeIntersectionType -> intersectedTypes.flatMap { it.collectUpperBounds() }.toSet()
is ConeFlexibleType -> upperBound.collectUpperBounds()
is ConeCapturedType -> constructor.supertypes?.flatMap { it.collectUpperBounds() }?.toSet().orEmpty()
is ConeStubType, is ConeIntegerLiteralType -> throw IllegalStateException("$this should not reach here")
is ConeIntegerConstantOperatorType -> setOf(getApproximatedType())
is ConeStubType, is ConeIntegerLiteralConstantType -> throw IllegalStateException("$this should not reach here")
}
}
@@ -278,7 +279,8 @@ object ConeTypeCompatibilityChecker {
is ConeIntersectionType -> intersectedTypes.flatMap { it.collectLowerBounds() }.toSet()
is ConeFlexibleType -> lowerBound.collectLowerBounds()
is ConeCapturedType -> constructor.supertypes?.flatMap { it.collectLowerBounds() }?.toSet().orEmpty()
is ConeStubType, is ConeIntegerLiteralType -> throw IllegalStateException("$this should not reach here")
is ConeIntegerConstantOperatorType -> setOf(getApproximatedType())
is ConeStubType, is ConeIntegerLiteralConstantType -> throw IllegalStateException("$this should not reach here")
}
}
@@ -93,10 +93,14 @@ interface FirDeclarationPresenter {
append(it.lookupTag.name)
append(it.nullability.suffix)
}
is ConeIntegerLiteralType -> {
is ConeIntegerLiteralConstantType -> {
append(it.value)
append(it.nullability.suffix)
}
is ConeIntegerConstantOperatorType -> {
append("IOT")
append(it.nullability.suffix)
}
is ConeFlexibleType,
is ConeIntersectionType,
is ConeStubType -> {
@@ -7,31 +7,45 @@ package org.jetbrains.kotlin.fir.types
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
abstract class ConeIntegerLiteralType(
val value: Long,
sealed class ConeIntegerLiteralType(
val isUnsigned: Boolean,
override val nullability: ConeNullability
final override val nullability: ConeNullability
) : ConeSimpleKotlinType(), TypeConstructorMarker {
abstract val possibleTypes: Collection<ConeClassLikeType>
abstract val supertypes: List<ConeClassLikeType>
override val typeArguments: Array<out ConeTypeProjection> = emptyArray()
final override val typeArguments: Array<out ConeTypeProjection> = emptyArray()
final override val attributes: ConeAttributes get() = ConeAttributes.Empty
abstract fun getApproximatedType(expectedType: ConeKotlinType? = null): ConeClassLikeType
override fun equals(other: Any?): Boolean {
final override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ConeIntegerLiteralType
if (isUnsigned != other.isUnsigned) return false
if (possibleTypes != other.possibleTypes) return false
if (nullability != other.nullability) return false
return true
}
override fun hashCode(): Int {
final override fun hashCode(): Int {
return 31 * possibleTypes.hashCode() + nullability.hashCode()
}
companion object
}
abstract class ConeIntegerLiteralConstantType(
val value: Long,
isUnsigned: Boolean,
nullability: ConeNullability
) : ConeIntegerLiteralType(isUnsigned, nullability)
abstract class ConeIntegerConstantOperatorType(
isUnsigned: Boolean,
nullability: ConeNullability
) : ConeIntegerLiteralType(isUnsigned, nullability)
@@ -49,7 +49,8 @@ fun ConeKotlinType.render(): String {
is ConeStubTypeForChainInference -> "${renderAttributes()}Stub (chain inference): ${constructor.variable}"
is ConeStubTypeForSyntheticFixation -> "${renderAttributes()}Stub (fixation): ${constructor.variable}"
is ConeStubType -> "${renderAttributes()}Stub (subtyping): ${constructor.variable}"
is ConeIntegerLiteralType -> "${renderAttributes()}ILT: $value"
is ConeIntegerLiteralConstantType -> "${renderAttributes()}ILT: $value"
is ConeIntegerConstantOperatorType -> "${renderAttributes()}IOT"
} + nullabilitySuffix
}
@@ -397,7 +397,7 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
}
override fun findCommonIntegerLiteralTypesSuperType(explicitSupertypes: List<SimpleTypeMarker>): SimpleTypeMarker? {
return ConeIntegerLiteralTypeImpl.findCommonSuperType(explicitSupertypes)
return ConeIntegerLiteralType.findCommonSuperType(explicitSupertypes)
}
override fun unionTypeAttributes(types: List<KotlinTypeMarker>): List<AnnotationMarker> {
@@ -175,7 +175,8 @@ fun <T : ConeKotlinType> T.withNullability(
ConeNullability.NULLABLE -> original.withNullability(nullability, typeContext)
ConeNullability.UNKNOWN -> original.withNullability(nullability, typeContext)
}
is ConeIntegerLiteralTypeImpl -> ConeIntegerLiteralTypeImpl(value, possibleTypes, isUnsigned, nullability)
is ConeIntegerLiteralConstantType -> ConeIntegerLiteralConstantTypeImpl(value, possibleTypes, isUnsigned, nullability)
is ConeIntegerConstantOperatorType -> ConeIntegerConstantOperatorTypeImpl(isUnsigned, nullability)
else -> error("sealed: ${this::class}")
} as T
}
@@ -906,7 +906,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
val type = when (val kind = constExpression.kind) {
ConstantValueKind.IntegerLiteral, ConstantValueKind.UnsignedIntegerLiteral -> {
val expressionType = ConeIntegerLiteralTypeImpl.create(
val expressionType = ConeIntegerLiteralConstantTypeImpl.create(
constExpression.value as Long,
isUnsigned = kind == ConstantValueKind.UnsignedIntegerLiteral
)
@@ -918,7 +918,7 @@ open class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransform
expressionType
}
expectedTypeRef != null -> {
require(expressionType is ConeIntegerLiteralTypeImpl)
require(expressionType is ConeIntegerLiteralConstantTypeImpl)
val coneType = expectedTypeRef.coneTypeSafe<ConeKotlinType>()?.fullyExpandedType(session)
val approximatedType= expressionType.getApproximatedType(coneType)
constExpression.replaceKind(approximatedType.toConstKind() as ConstantValueKind<T>)
@@ -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()}")
}
@@ -40,7 +40,8 @@ fun ConeKotlinType.renderForDebugInfo(): String {
) { it.renderForDebugInfo() }
}
is ConeStubType -> "Stub: ${constructor.variable}"
is ConeIntegerLiteralType -> "ILT: $value"
is ConeIntegerLiteralConstantType -> "ILT: $value"
is ConeIntegerConstantOperatorType -> "IOT"
} + nullabilitySuffix
}