[FIR] Support unsigned integer literals

This commit is contained in:
simon.ogorodnik
2020-03-24 02:22:53 +03:00
parent 0afbf25943
commit 27136ef8be
12 changed files with 111 additions and 91 deletions
@@ -221,6 +221,7 @@ class ConeTypeVariableTypeConstructor(val debugName: String) : ConeClassifierLoo
abstract class ConeIntegerLiteralType(
val value: Long,
val isUnsigned: Boolean,
override val nullability: ConeNullability
) : ConeSimpleKotlinType(), TypeConstructorMarker {
abstract val possibleTypes: Collection<ConeClassLikeType>
@@ -784,6 +784,10 @@ class HtmlFirDump internal constructor(private var linkResolver: FirLinkResolver
+value.toString()
keyword("L")
}
FirConstKind.UnsignedLong -> {
+value.toString()
keyword("UL")
}
FirConstKind.Float -> {
+value.toString()
keyword("F")
@@ -145,14 +145,21 @@ private fun FirConstKind<*>.toIrConstKind(): IrConstKind<*> = when (this) {
FirConstKind.Null -> IrConstKind.Null
FirConstKind.Boolean -> IrConstKind.Boolean
FirConstKind.Char -> IrConstKind.Char
FirConstKind.Byte -> IrConstKind.Byte
FirConstKind.Short -> IrConstKind.Short
FirConstKind.Int -> IrConstKind.Int
FirConstKind.Long -> IrConstKind.Long
FirConstKind.UnsignedByte -> IrConstKind.Byte
FirConstKind.UnsignedShort -> IrConstKind.Short
FirConstKind.UnsignedInt -> IrConstKind.Int
FirConstKind.UnsignedLong -> IrConstKind.Long
FirConstKind.String -> IrConstKind.String
FirConstKind.Float -> IrConstKind.Float
FirConstKind.Double -> IrConstKind.Double
FirConstKind.IntegerLiteral -> throw IllegalArgumentException()
FirConstKind.IntegerLiteral, FirConstKind.UnsignedIntegerLiteral -> throw IllegalArgumentException()
}
internal fun FirClass<*>.collectCallableNamesFromSupertypes(session: FirSession, result: MutableList<Name> = mutableListOf()): List<Name> {
@@ -224,9 +224,15 @@ abstract class BaseFirBuilder<T>(val baseSession: FirSession, val context: Conte
)
}
hasLongSuffix(text) || hasUnsignedSuffix(text) || hasUnsignedLongSuffix(text) -> {
hasUnsignedLongSuffix(text) -> {
FirConstKind.UnsignedLong
}
hasLongSuffix(text) -> {
FirConstKind.Long
}
hasUnsignedSuffix(text) -> {
FirConstKind.UnsignedIntegerLiteral
}
else -> {
FirConstKind.IntegerLiteral
@@ -58,10 +58,13 @@ fun ConeKotlinType.scope(useSiteSession: FirSession, scopeSession: ScopeSession)
@Suppress("USELESS_CAST") // TODO: remove once fixed: https://youtrack.jetbrains.com/issue/KT-35635
scopeSession.getOrBuild(
FirIntegerLiteralTypeScope.ILT_SYMBOL,
when {
isUnsigned -> FirIntegerLiteralTypeScope.ILTKey.Unsigned
else -> FirIntegerLiteralTypeScope.ILTKey.Signed
},
FirIntegerLiteralTypeScope.SCOPE_SESSION_KEY
) {
FirIntegerLiteralTypeScope(useSiteSession)
FirIntegerLiteralTypeScope(useSiteSession, isUnsigned)
} as FirScope
}
else -> null
@@ -116,16 +116,6 @@ class IntegerLiteralTypeApproximationTransformer(
}
}
fun ConeClassLikeType.toConstKind(): FirConstKind<*> {
return when (classId) {
StandardClassIds.Int -> FirConstKind.Int
StandardClassIds.Long -> FirConstKind.Long
StandardClassIds.Short -> FirConstKind.Short
StandardClassIds.Byte -> FirConstKind.Byte
else -> throw IllegalStateException()
}
}
fun FirFunctionCall.getOriginalFunction(): FirCallableDeclaration<*>? {
val symbol: AbstractFirBasedSymbol<*>? = when (val reference = calleeReference) {
is FirResolvedNamedReference -> reference.resolvedSymbol
@@ -148,7 +138,8 @@ class IntegerOperatorsTypeUpdater(val approximator: IntegerLiteralTypeApproximat
return functionCall.transformExplicitReceiver(approximator, expectedType).compose()
}
// TODO: maybe unsafe?
val receiverValue = functionCall.explicitReceiver!!.typeRef.coneTypeSafe<ConeIntegerLiteralType>()?.value ?: return functionCall.compose()
val receiverType = functionCall.explicitReceiver!!.typeRef.coneTypeSafe<ConeIntegerLiteralType>() ?: return functionCall.compose()
val receiverValue = receiverType.value
val kind = function.kind
val resultValue = when {
kind.unary -> when (kind) {
@@ -193,7 +184,7 @@ class IntegerOperatorsTypeUpdater(val approximator: IntegerLiteralTypeApproximat
}
}
}
functionCall.replaceTypeRef(functionCall.resultType.resolvedTypeFromPrototype(ConeIntegerLiteralTypeImpl(resultValue)))
functionCall.replaceTypeRef(functionCall.resultType.resolvedTypeFromPrototype(ConeIntegerLiteralTypeImpl(resultValue, isUnsigned = receiverType.isUnsigned)))
return functionCall.toOperatorCall().compose()
}
}
@@ -504,44 +504,51 @@ class FirExpressionsResolveTransformer(transformer: FirBodyResolveTransformer) :
data: ResolutionMode,
): CompositeTransformResult<FirStatement> {
constExpression.annotations.forEach { it.accept(this, data) }
val kind = constExpression.kind
val symbol = when (kind) {
FirConstKind.Null -> StandardClassIds.Nothing(symbolProvider)
FirConstKind.Boolean -> StandardClassIds.Boolean(symbolProvider)
FirConstKind.Char -> StandardClassIds.Char(symbolProvider)
FirConstKind.Byte -> StandardClassIds.Byte(symbolProvider)
FirConstKind.Short -> StandardClassIds.Short(symbolProvider)
FirConstKind.Int -> StandardClassIds.Int(symbolProvider)
FirConstKind.Long -> StandardClassIds.Long(symbolProvider)
FirConstKind.String -> StandardClassIds.String(symbolProvider)
FirConstKind.Float -> StandardClassIds.Float(symbolProvider)
FirConstKind.Double -> StandardClassIds.Double(symbolProvider)
FirConstKind.IntegerLiteral -> null
fun constructLiteralType(classId: ClassId, isNullable: Boolean = false): ConeKotlinType {
val symbol = symbolProvider.getClassLikeSymbolByFqName(classId) ?: return ConeClassErrorType("Missing stdlib class: ${classId}")
return symbol.toLookupTag().constructClassType(emptyArray(), isNullable)
}
val type = if (symbol != null) {
ConeClassLikeTypeImpl(symbol.toLookupTag(), emptyArray(), isNullable = kind == FirConstKind.Null)
} else {
val integerLiteralType = ConeIntegerLiteralTypeImpl(constExpression.value as Long)
val expectedType = data.expectedType?.coneTypeSafe<ConeKotlinType>()
if (expectedType != null) {
val approximatedType = integerLiteralType.getApproximatedType(expectedType)
val newConstKind = approximatedType.toConstKind()
if (newConstKind == null) {
constExpression.replaceKind(FirConstKind.Int as FirConstKind<T>)
dataFlowAnalyzer.exitConstExpresion(constExpression as FirConstExpression<*>)
constExpression.resultType = buildErrorTypeRef {
source = constExpression.source
diagnostic = FirTypeMismatchError(expectedType, integerLiteralType.getApproximatedType())
val kind = constExpression.kind
val type = when (kind) {
FirConstKind.Null -> session.builtinTypes.nullableNothingType.type
FirConstKind.Boolean -> session.builtinTypes.booleanType.type
FirConstKind.Char -> constructLiteralType(StandardClassIds.Char)
FirConstKind.Byte -> constructLiteralType(StandardClassIds.Byte)
FirConstKind.Short -> constructLiteralType(StandardClassIds.Short)
FirConstKind.Int -> constructLiteralType(StandardClassIds.Int)
FirConstKind.Long -> constructLiteralType(StandardClassIds.Long)
FirConstKind.String -> constructLiteralType(StandardClassIds.String)
FirConstKind.Float -> constructLiteralType(StandardClassIds.Float)
FirConstKind.Double -> constructLiteralType(StandardClassIds.Double)
FirConstKind.IntegerLiteral, FirConstKind.UnsignedIntegerLiteral -> {
val integerLiteralType = ConeIntegerLiteralTypeImpl(constExpression.value as Long, isUnsigned = kind == FirConstKind.UnsignedIntegerLiteral)
val expectedType = data.expectedType?.coneTypeSafe<ConeKotlinType>()
if (expectedType != null) {
val approximatedType = integerLiteralType.getApproximatedType(expectedType)
val newConstKind = approximatedType.toConstKind()
if (newConstKind == null) {
constExpression.replaceKind(FirConstKind.Int as FirConstKind<T>)
dataFlowAnalyzer.exitConstExpresion(constExpression as FirConstExpression<*>)
constExpression.resultType = buildErrorTypeRef {
source = constExpression.source
diagnostic = FirTypeMismatchError(expectedType, integerLiteralType.getApproximatedType())
}
return constExpression.compose()
}
return constExpression.compose()
constExpression.replaceKind(newConstKind as FirConstKind<T>)
approximatedType
} else {
integerLiteralType
}
constExpression.replaceKind(newConstKind as FirConstKind<T>)
approximatedType
} else {
integerLiteralType
}
FirConstKind.UnsignedByte -> constructLiteralType(StandardClassIds.UByte)
FirConstKind.UnsignedShort -> constructLiteralType(StandardClassIds.UShort)
FirConstKind.UnsignedInt -> constructLiteralType(StandardClassIds.UInt)
FirConstKind.UnsignedLong -> constructLiteralType(StandardClassIds.ULong)
}
dataFlowAnalyzer.exitConstExpresion(constExpression as FirConstExpression<*>)
constExpression.resultType = constExpression.resultType.resolvedTypeFromPrototype(type)
return constExpression.compose()
@@ -19,9 +19,7 @@ import org.jetbrains.kotlin.fir.declarations.impl.FirSimpleFunctionImpl
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
import org.jetbrains.kotlin.fir.resolve.scopeSessionKey
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.CallableId
import org.jetbrains.kotlin.fir.symbols.ConeClassifierLookupTag
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeIntegerLiteralType
import org.jetbrains.kotlin.fir.types.ConeIntegerLiteralTypeImpl
@@ -32,45 +30,19 @@ import org.jetbrains.kotlin.fir.visitors.FirVisitor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.util.OperatorNameConventions
private object FirIntegerLiteralTypeClassifierSymbol : FirClassifierSymbol<FirIntegerLiteralTypeClassifier>() {
override fun toLookupTag(): ConeClassifierLookupTag {
throw IllegalStateException("Should not be called")
}
}
private object FirIntegerLiteralTypeClassifier : FirDeclaration, FirSymbolOwner<FirIntegerLiteralTypeClassifier> {
override val symbol: AbstractFirBasedSymbol<FirIntegerLiteralTypeClassifier>
get() = FirIntegerLiteralTypeClassifierSymbol
override val source: FirSourceElement? get() = throw IllegalStateException("Should not be called")
override val session: FirSession get() = throw IllegalStateException("Should not be called")
override val resolvePhase: FirResolvePhase get() = throw IllegalStateException("Should not be called")
override fun <R, D> accept(visitor: FirVisitor<R, D>, data: D): R {
throw IllegalStateException("Should not be called")
class FirIntegerLiteralTypeScope(private val session: FirSession, val isUnsigned: Boolean) : FirScope() {
sealed class ILTKey {
object Signed : ILTKey()
object Unsigned : ILTKey()
}
override fun replaceResolvePhase(newResolvePhase: FirResolvePhase) {
throw IllegalStateException("Should not be called")
}
override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {
throw IllegalStateException("Should not be called")
}
override fun <D> transformChildren(transformer: FirTransformer<D>, data: D): FirElement {
throw IllegalStateException("Should not be called")
}
}
class FirIntegerLiteralTypeScope(private val session: FirSession) : FirScope() {
companion object {
val BINARY_OPERATOR_NAMES = FirIntegerOperator.Kind.values().filterNot { it.unary }.map { it.operatorName }
val UNARY_OPERATOR_NAMES = FirIntegerOperator.Kind.values().filter { it.unary }.map { it.operatorName }
private val ALL_OPERATORS = FirIntegerOperator.Kind.values().map { it.operatorName to it }.toMap()
val ILT_SYMBOL: FirClassifierSymbol<*> = FirIntegerLiteralTypeClassifierSymbol
val SCOPE_SESSION_KEY = scopeSessionKey<FirClassifierSymbol<*>, FirIntegerLiteralTypeScope>()
val SCOPE_SESSION_KEY = scopeSessionKey<ILTKey, FirIntegerLiteralTypeScope>()
}
private val BINARY_OPERATOR_SYMBOLS = BINARY_OPERATOR_NAMES.map { name ->
@@ -80,7 +52,7 @@ class FirIntegerLiteralTypeScope(private val session: FirSession) : FirScope() {
valueParameters += buildValueParameter {
source = null
session = this@FirIntegerLiteralTypeScope.session
returnTypeRef = FirILTTypeRefPlaceHolder()
returnTypeRef = FirILTTypeRefPlaceHolder(isUnsigned)
this.name = valueParameterName
symbol = FirVariableSymbol(valueParameterName)
defaultValue = null
@@ -100,7 +72,7 @@ class FirIntegerLiteralTypeScope(private val session: FirSession) : FirScope() {
private fun createFirFunction(name: Name, symbol: FirNamedFunctionSymbol): FirSimpleFunctionImpl = FirIntegerOperator(
source = null,
session,
FirILTTypeRefPlaceHolder(),
FirILTTypeRefPlaceHolder(isUnsigned),
receiverTypeRef = null,
ALL_OPERATORS.getValue(name),
FirResolvedDeclarationStatusImpl(Visibilities.PUBLIC, FirEffectiveVisibility.Public, Modality.FINAL),
@@ -166,10 +138,10 @@ class FirIntegerOperator @FirImplementationDetail constructor(
}
}
class FirILTTypeRefPlaceHolder : FirResolvedTypeRef() {
class FirILTTypeRefPlaceHolder(isUnsigned: Boolean) : FirResolvedTypeRef() {
override val source: FirSourceElement? get() = null
override val annotations: List<FirAnnotationCall> get() = emptyList()
override var type: ConeIntegerLiteralType = ConeIntegerLiteralTypeImpl(0)
override var type: ConeIntegerLiteralType = ConeIntegerLiteralTypeImpl(0, isUnsigned)
override val delegatedTypeRef: FirTypeRef? get() = null
override fun <R, D> acceptChildren(visitor: FirVisitor<R, D>, data: D) {}
@@ -146,7 +146,7 @@ fun <T : ConeKotlinType> T.withNullability(nullability: ConeNullability, typeCon
ConeNullability.NULLABLE -> original.withNullability(nullability)
ConeNullability.UNKNOWN -> original.withNullability(nullability)
}
is ConeIntegerLiteralType -> ConeIntegerLiteralTypeImpl(value, nullability)
is ConeIntegerLiteralType -> ConeIntegerLiteralTypeImpl(value, isUnsigned, nullability)
else -> error("sealed: ${this::class}")
} as T
}
@@ -9,14 +9,23 @@ sealed class FirConstKind<T>(val asString: kotlin.String) {
object Null : FirConstKind<Nothing?>("Null")
object Boolean : FirConstKind<kotlin.Boolean>("Boolean")
object Char : FirConstKind<kotlin.Char>("Char")
object Byte : FirConstKind<kotlin.Byte>("Byte")
object UnsignedByte : FirConstKind<kotlin.Byte>("UByte")
object Short : FirConstKind<kotlin.Short>("Short")
object UnsignedShort : FirConstKind<kotlin.Short>("UShort")
object Int : FirConstKind<kotlin.Int>("Int")
object UnsignedInt : FirConstKind<kotlin.Int>("UInt")
object Long : FirConstKind<kotlin.Long>("Long")
object UnsignedLong : FirConstKind<kotlin.Long>("ULong")
object String : FirConstKind<kotlin.String>("String")
object Float : FirConstKind<kotlin.Float>("Float")
object Double : FirConstKind<kotlin.Double>("Double")
object IntegerLiteral : FirConstKind<kotlin.Long>("IntegerLiteral")
object UnsignedIntegerLiteral : FirConstKind<kotlin.Long>("UnsignedIntegerLiteral")
override fun toString() = asString
}
@@ -14,7 +14,7 @@ import org.jetbrains.kotlin.types.model.SimpleTypeMarker
class ConeIntegerLiteralTypeImpl : ConeIntegerLiteralType {
override val possibleTypes: Collection<ConeClassLikeType>
constructor(value: Long, nullability: ConeNullability = ConeNullability.NOT_NULL) : super(value, nullability) {
constructor(value: Long, isUnsigned: Boolean, nullability: ConeNullability = ConeNullability.NOT_NULL) : super(value, isUnsigned, nullability) {
possibleTypes = mutableListOf()
fun checkBoundsAndAddPossibleType(classId: ClassId, range: LongRange) {
@@ -30,15 +30,26 @@ class ConeIntegerLiteralTypeImpl : ConeIntegerLiteralType {
checkBoundsAndAddPossibleType(StandardClassIds.Short, SHORT_RANGE)
}
addSignedPossibleTypes()
// TODO: add support of unsigned types
fun addUnsignedPossibleType() {
checkBoundsAndAddPossibleType(StandardClassIds.UInt, UINT_RANGE)
possibleTypes += createType(StandardClassIds.ULong)
checkBoundsAndAddPossibleType(StandardClassIds.UByte, UBYTE_RANGE)
checkBoundsAndAddPossibleType(StandardClassIds.UShort, USHORT_RANGE)
}
if (isUnsigned) {
addUnsignedPossibleType()
} else {
addSignedPossibleTypes()
}
}
private constructor(
value: Long,
possibleTypes: Collection<ConeClassLikeType>,
isUnsigned: Boolean,
nullability: ConeNullability = ConeNullability.NOT_NULL
) : super(value, nullability) {
) : super(value, isUnsigned, nullability) {
this.possibleTypes = possibleTypes
}
@@ -68,6 +79,10 @@ class ConeIntegerLiteralTypeImpl : ConeIntegerLiteralType {
private val BYTE_RANGE = Byte.MIN_VALUE.toLong()..Byte.MAX_VALUE.toLong()
private val SHORT_RANGE = Short.MIN_VALUE.toLong()..Short.MAX_VALUE.toLong()
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? {
@@ -114,7 +129,7 @@ class ConeIntegerLiteralTypeImpl : ConeIntegerLiteralType {
Mode.COMMON_SUPER_TYPE -> left.possibleTypes intersect right.possibleTypes
Mode.INTERSECTION_TYPE -> left.possibleTypes union right.possibleTypes
}
return ConeIntegerLiteralTypeImpl(left.value, possibleTypes)
return ConeIntegerLiteralTypeImpl(left.value, possibleTypes, left.isUnsigned)
}
private fun fold(left: ConeIntegerLiteralType, right: SimpleTypeMarker): SimpleTypeMarker? =
@@ -59,5 +59,10 @@ fun ConeClassLikeType.toConstKind(): FirConstKind<*>? = when (lookupTag.classId)
StandardClassIds.Short -> FirConstKind.Short
StandardClassIds.Int -> FirConstKind.Int
StandardClassIds.Long -> FirConstKind.Long
StandardClassIds.UInt -> FirConstKind.UnsignedInt
StandardClassIds.ULong -> FirConstKind.UnsignedLong
StandardClassIds.UShort -> FirConstKind.UnsignedShort
StandardClassIds.UByte -> FirConstKind.UnsignedByte
else -> null
}