[FIR] Partially support raw types

This commit is contained in:
Simon Ogorodnik
2019-11-12 23:56:36 +03:00
parent 119a3f1306
commit 6e8f8f9a65
4 changed files with 129 additions and 19 deletions
@@ -119,7 +119,7 @@ abstract class ConeAbbreviatedType : ConeClassLikeType() {
abstract val abbreviationLookupTag: ConeClassLikeLookupTag
}
data class ConeFlexibleType(val lowerBound: ConeKotlinType, val upperBound: ConeKotlinType) : ConeKotlinType(),
open class ConeFlexibleType(val lowerBound: ConeKotlinType, val upperBound: ConeKotlinType) : ConeKotlinType(),
FlexibleTypeMarker {
init {
@@ -133,6 +133,25 @@ data class ConeFlexibleType(val lowerBound: ConeKotlinType, val upperBound: Cone
override val nullability: ConeNullability
get() = lowerBound.nullability.takeIf { it == upperBound.nullability } ?: ConeNullability.UNKNOWN
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ConeFlexibleType
if (lowerBound != other.lowerBound) return false
if (upperBound != other.upperBound) return false
return true
}
override fun hashCode(): Int {
var result = lowerBound.hashCode()
result = 31 * result + upperBound.hashCode()
return result
}
}
fun ConeKotlinType.upperBoundIfFlexible() = (this as? ConeFlexibleType)?.upperBound ?: this
@@ -182,6 +201,8 @@ class ConeDefinitelyNotNullType private constructor(val original: ConeKotlinType
}
}
class ConeRawType(lowerBound: ConeKotlinType, upperBound: ConeKotlinType) : ConeFlexibleType(lowerBound, upperBound), RawTypeMarker
/*
* Contract of the intersection type: it is flat. It means that
* intersection type can not contains another intersection types
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.fir.java.types.FirJavaTypeRef
import org.jetbrains.kotlin.fir.references.impl.FirResolvedNamedReferenceImpl
import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference
import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.firUnsafe
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.ConeClassifierLookupTag
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
@@ -88,7 +89,10 @@ private fun JavaType?.enhancePossiblyFlexible(
session, javaTypeParameterStack, annotations, arguments, TypeComponentPosition.FLEXIBLE_UPPER, qualifiers, index
)
coneFlexibleOrSimpleType(session, lowerResult, upperResult)
when {
type.isRaw -> ConeRawType(lowerResult, upperResult)
else -> coneFlexibleOrSimpleType(session, lowerResult, upperResult)
}
}
is JavaArrayType -> {
val baseEnhanced = type.toNotNullConeKotlinType(session, javaTypeParameterStack)
@@ -160,6 +164,77 @@ private fun ClassId.mutableToReadOnly(): ClassId? {
}
}
// Definition:
// ErasedUpperBound(T : G<t>) = G<*> // UpperBound(T) is a type G<t> with arguments
// ErasedUpperBound(T : A) = A // UpperBound(T) is a type A without arguments
// ErasedUpperBound(T : F) = UpperBound(F) // UB(T) is another type parameter F
private fun FirTypeParameter.getErasedUpperBound(
// Calculation of `potentiallyRecursiveTypeParameter.upperBounds` may recursively depend on `this.getErasedUpperBound`
// E.g. `class A<T extends A, F extends A>`
// To prevent recursive calls return defaultValue() instead
potentiallyRecursiveTypeParameter: FirTypeParameter? = null,
defaultValue: (() -> ConeKotlinType) = { ConeKotlinErrorType("Can't compute erased upper bound of type parameter `$this`") }
): ConeKotlinType {
if (this === potentiallyRecursiveTypeParameter) return defaultValue()
val firstUpperBound = this.bounds.first().coneTypeUnsafe<ConeKotlinType>()
val firstUpperBoundClassifier = firstUpperBound
if (firstUpperBoundClassifier is ConeClassLikeType) {
return firstUpperBound.withArguments(firstUpperBound.typeArguments.map { ConeStarProjection }.toTypedArray())
}
val stopAt = potentiallyRecursiveTypeParameter ?: this
var current = (firstUpperBound as ConeTypeParameterType).lookupTag.typeParameterSymbol.fir
while (current != stopAt) {
val nextUpperBound = current.bounds.first().coneTypeUnsafe<ConeKotlinType>()
if (nextUpperBound is ConeClassLikeType) {
return nextUpperBound.withArguments(nextUpperBound.typeArguments.map { ConeStarProjection }.toTypedArray())
}
current = (nextUpperBound as ConeTypeParameterType).lookupTag.typeParameterSymbol.fir
}
return defaultValue()
}
fun computeProjection(
session: FirSession,
parameter: FirTypeParameter,
attr: TypeComponentPosition,
erasedUpperBound: ConeKotlinType = parameter.getErasedUpperBound()
) = when (attr) {
// Raw(List<T>) => (List<Any?>..List<*>)
// Raw(Enum<T>) => (Enum<Enum<*>>..Enum<out Enum<*>>)
// In the last case upper bound is equal to star projection `Enum<*>`,
// but we want to keep matching tree structure of flexible bounds (at least they should have the same size)
TypeComponentPosition.FLEXIBLE_LOWER -> {
// T : String -> String
// in T : String -> String
// T : Enum<T> -> Enum<*>
erasedUpperBound
}
TypeComponentPosition.FLEXIBLE_UPPER, TypeComponentPosition.INFLEXIBLE -> {
if (!parameter.variance.allowsOutPosition)
// in T -> Comparable<Nothing>
session.builtinTypes.nothingType.type
else if (erasedUpperBound is ConeClassLikeType &&
erasedUpperBound.lookupTag.toSymbol(session)!!.firUnsafe<FirRegularClass>().typeParameters.isNotEmpty())
// T : Enum<E> -> out Enum<*>
ConeKotlinTypeProjectionOut(erasedUpperBound)
else
// T : String -> *
ConeStarProjection
}
}
private fun JavaClassifierType.enhanceInflexibleType(
session: FirSession,
javaTypeParameterStack: JavaTypeParameterStack,
@@ -169,7 +244,8 @@ private fun JavaClassifierType.enhanceInflexibleType(
qualifiers: IndexedJavaTypeQualifiers,
index: Int
): ConeLookupTagBasedType {
val originalTag = when (val classifier = classifier) {
val classifier = classifier
val originalTag = when (classifier) {
is JavaClass -> {
val classId = classifier.classId!!
var mappedId = JavaToKotlinClassMap.mapJavaToKotlin(classId.asSingleFqName())
@@ -188,20 +264,32 @@ private fun JavaClassifierType.enhanceInflexibleType(
val effectiveQualifiers = qualifiers(index)
val enhancedTag = originalTag.enhanceMutability(effectiveQualifiers, position)
var globalArgIndex = index + 1
val enhancedArguments = arguments.mapIndexed { localArgIndex, arg ->
if (arg is JavaWildcardType) {
globalArgIndex++
arg.toConeProjection(
session,
javaTypeParameterStack,
((originalTag as? FirBasedSymbol<*>)?.fir as? FirCallableMemberDeclaration<*>)?.typeParameters?.getOrNull(localArgIndex)
)
} else {
val argEnhancedTypeRef = arg.enhancePossiblyFlexible(session, javaTypeParameterStack, annotations, qualifiers, globalArgIndex)
globalArgIndex += arg.subtreeSize()
// For arg == null (raw type) we take <out Any> to match everything
argEnhancedTypeRef.type.type.toTypeProjection(if (arg == null) Variance.OUT_VARIANCE else Variance.INVARIANT)
val enhancedArguments = if (isRaw) {
val firClassifier = originalTag.toSymbol(session)!!.firUnsafe<FirRegularClass>()
firClassifier.typeParameters.map {
val fir = it
val erasedUpperBound = fir.getErasedUpperBound {
firClassifier.defaultType().withArguments(firClassifier.typeParameters.map { ConeStarProjection }.toTypedArray())
}
computeProjection(session, fir, position, erasedUpperBound)
}
} else {
var globalArgIndex = index + 1
arguments.mapIndexed { localArgIndex, arg ->
if (arg is JavaWildcardType) {
globalArgIndex++
arg.toConeProjection(
session,
javaTypeParameterStack,
((originalTag as? FirBasedSymbol<*>)?.fir as? FirCallableMemberDeclaration<*>)?.typeParameters?.getOrNull(localArgIndex)
)
} else {
val argEnhancedTypeRef =
arg.enhancePossiblyFlexible(session, javaTypeParameterStack, annotations, qualifiers, globalArgIndex)
globalArgIndex += arg.subtreeSize()
argEnhancedTypeRef.type.type.toTypeProjection(Variance.INVARIANT)
}
}
}
@@ -33,6 +33,7 @@ fun ConeKotlinType.scope(useSiteSession: FirSession, scopeSession: ScopeSession)
}
)
}
is ConeRawType -> lowerBound.scope(useSiteSession, scopeSession)
is ConeFlexibleType -> lowerBound.scope(useSiteSession, scopeSession)
is ConeIntersectionType -> FirCompositeScope(
intersectedTypes.mapNotNullTo(mutableListOf()) {
@@ -91,8 +91,8 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty
}
override fun FlexibleTypeMarker.asRawType(): RawTypeMarker? {
assert(this is ConeKotlinType)
return null // TODO
require(this is ConeFlexibleType)
return this as? ConeRawType
}
override fun FlexibleTypeMarker.upperBound(): SimpleTypeMarker {