[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 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 { FlexibleTypeMarker {
init { init {
@@ -133,6 +133,25 @@ data class ConeFlexibleType(val lowerBound: ConeKotlinType, val upperBound: Cone
override val nullability: ConeNullability override val nullability: ConeNullability
get() = lowerBound.nullability.takeIf { it == upperBound.nullability } ?: ConeNullability.UNKNOWN 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 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 * Contract of the intersection type: it is flat. It means that
* intersection type can not contains another intersection types * 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.FirResolvedNamedReferenceImpl
import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference import org.jetbrains.kotlin.fir.references.impl.FirSimpleNamedReference
import org.jetbrains.kotlin.fir.resolve.* 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.ConeClassLikeLookupTag
import org.jetbrains.kotlin.fir.symbols.ConeClassifierLookupTag import org.jetbrains.kotlin.fir.symbols.ConeClassifierLookupTag
import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag import org.jetbrains.kotlin.fir.symbols.ConeTypeParameterLookupTag
@@ -88,7 +89,10 @@ private fun JavaType?.enhancePossiblyFlexible(
session, javaTypeParameterStack, annotations, arguments, TypeComponentPosition.FLEXIBLE_UPPER, qualifiers, index 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 -> { is JavaArrayType -> {
val baseEnhanced = type.toNotNullConeKotlinType(session, javaTypeParameterStack) 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( private fun JavaClassifierType.enhanceInflexibleType(
session: FirSession, session: FirSession,
javaTypeParameterStack: JavaTypeParameterStack, javaTypeParameterStack: JavaTypeParameterStack,
@@ -169,7 +244,8 @@ private fun JavaClassifierType.enhanceInflexibleType(
qualifiers: IndexedJavaTypeQualifiers, qualifiers: IndexedJavaTypeQualifiers,
index: Int index: Int
): ConeLookupTagBasedType { ): ConeLookupTagBasedType {
val originalTag = when (val classifier = classifier) { val classifier = classifier
val originalTag = when (classifier) {
is JavaClass -> { is JavaClass -> {
val classId = classifier.classId!! val classId = classifier.classId!!
var mappedId = JavaToKotlinClassMap.mapJavaToKotlin(classId.asSingleFqName()) var mappedId = JavaToKotlinClassMap.mapJavaToKotlin(classId.asSingleFqName())
@@ -188,20 +264,32 @@ private fun JavaClassifierType.enhanceInflexibleType(
val effectiveQualifiers = qualifiers(index) val effectiveQualifiers = qualifiers(index)
val enhancedTag = originalTag.enhanceMutability(effectiveQualifiers, position) val enhancedTag = originalTag.enhanceMutability(effectiveQualifiers, position)
var globalArgIndex = index + 1 val enhancedArguments = if (isRaw) {
val enhancedArguments = arguments.mapIndexed { localArgIndex, arg -> val firClassifier = originalTag.toSymbol(session)!!.firUnsafe<FirRegularClass>()
if (arg is JavaWildcardType) { firClassifier.typeParameters.map {
globalArgIndex++ val fir = it
arg.toConeProjection( val erasedUpperBound = fir.getErasedUpperBound {
session, firClassifier.defaultType().withArguments(firClassifier.typeParameters.map { ConeStarProjection }.toTypedArray())
javaTypeParameterStack, }
((originalTag as? FirBasedSymbol<*>)?.fir as? FirCallableMemberDeclaration<*>)?.typeParameters?.getOrNull(localArgIndex) computeProjection(session, fir, position, erasedUpperBound)
) }
} else { } else {
val argEnhancedTypeRef = arg.enhancePossiblyFlexible(session, javaTypeParameterStack, annotations, qualifiers, globalArgIndex) var globalArgIndex = index + 1
globalArgIndex += arg.subtreeSize() arguments.mapIndexed { localArgIndex, arg ->
// For arg == null (raw type) we take <out Any> to match everything if (arg is JavaWildcardType) {
argEnhancedTypeRef.type.type.toTypeProjection(if (arg == null) Variance.OUT_VARIANCE else Variance.INVARIANT) 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 ConeFlexibleType -> lowerBound.scope(useSiteSession, scopeSession)
is ConeIntersectionType -> FirCompositeScope( is ConeIntersectionType -> FirCompositeScope(
intersectedTypes.mapNotNullTo(mutableListOf()) { intersectedTypes.mapNotNullTo(mutableListOf()) {
@@ -91,8 +91,8 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty
} }
override fun FlexibleTypeMarker.asRawType(): RawTypeMarker? { override fun FlexibleTypeMarker.asRawType(): RawTypeMarker? {
assert(this is ConeKotlinType) require(this is ConeFlexibleType)
return null // TODO return this as? ConeRawType
} }
override fun FlexibleTypeMarker.upperBound(): SimpleTypeMarker { override fun FlexibleTypeMarker.upperBound(): SimpleTypeMarker {