[FIR] Unify code of type unification, fix intersection types processing in FirCastDiagnosticsHelpers.kt

This commit is contained in:
Ivan Kochurkin
2021-11-10 14:52:07 +03:00
committed by TeamCityServer
parent 98cce8e05b
commit 5fda933c33
7 changed files with 214 additions and 330 deletions
@@ -8,15 +8,14 @@ package org.jetbrains.kotlin.fir.resolve.transformers.body.resolve
import org.jetbrains.kotlin.fir.declarations.FirRegularClass
import org.jetbrains.kotlin.fir.declarations.FirTypeAlias
import org.jetbrains.kotlin.fir.declarations.FirTypeParameter
import org.jetbrains.kotlin.fir.declarations.FirTypeParameterRef
import org.jetbrains.kotlin.fir.declarations.utils.expandedConeType
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
import org.jetbrains.kotlin.fir.resolve.defaultType
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
import org.jetbrains.kotlin.fir.resolve.toSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
fun BodyResolveComponents.computeRepresentativeTypeForBareType(type: ConeClassLikeType, originalType: ConeKotlinType): ConeKotlinType? {
originalType.lowerBoundIfFlexible().fullyExpandedType(session).let {
@@ -49,11 +48,11 @@ fun BodyResolveComponents.computeRepresentativeTypeForBareType(type: ConeClassLi
correspondingSupertype
}
val substitution = mutableMapOf<FirTypeParameterRef, ConeTypeProjection>()
val typeParameters = castClass.typeParameters.mapTo(mutableSetOf()) { it.symbol.fir }
if (!doUnify(originalType, superTypeWithParameters, typeParameters, substitution)) return null
val substitution = mutableMapOf<FirTypeParameterSymbol, ConeTypeProjection>()
val typeParameters = castClass.typeParameters.mapTo(mutableSetOf()) { it.symbol }
if (!session.doUnify(originalType, superTypeWithParameters, typeParameters, substitution)) return null
val newArguments = castClass.typeParameters.map { substitution[it.symbol.fir] ?: return@computeRepresentativeTypeForBareType null }
val newArguments = castClass.typeParameters.map { substitution[it.symbol] ?: return@computeRepresentativeTypeForBareType null }
return expandedCastType.withArguments(newArguments.toTypedArray())
}
@@ -76,123 +75,3 @@ private fun canBeUsedAsBareType(firTypeAlias: FirTypeAlias): Boolean {
return true
}
/**
* @return false does only mean that there were conflicted values for some type parameter. In all other cases, it returns true.
* "fail" result in the comments below means that we can't infer anything meaningful in that branch of unification.
* See more at org.jetbrains.kotlin.types.TypeUnifier.doUnify.
* NB: "Failed@ result of UnificationResultImpl is effectively unused in production.
*/
private fun BodyResolveComponents.doUnify(
originalTypeProjection: ConeTypeProjection,
typeWithParametersProjection: ConeTypeProjection,
targetTypeParameters: Set<FirTypeParameterRef>,
result: MutableMap<FirTypeParameterRef, ConeTypeProjection>,
): Boolean {
val originalType = originalTypeProjection.type
val typeWithParameters = typeWithParametersProjection.type
if (originalType is ConeIntersectionType) {
val intersectionResult = mutableMapOf<FirTypeParameterRef, ConeTypeProjection>()
for (intersectedType in originalType.intersectedTypes) {
val localResult = mutableMapOf<FirTypeParameterRef, ConeTypeProjection>()
if (!doUnify(intersectedType, typeWithParametersProjection, targetTypeParameters, localResult)) return false
for ((typeParameter, typeProjection) in localResult) {
val existingTypeProjection = intersectionResult[typeParameter]
if (existingTypeProjection == null
|| (typeProjection is KotlinTypeMarker &&
existingTypeProjection is KotlinTypeMarker &&
AbstractTypeChecker.isSubtypeOf(session.typeContext, typeProjection, existingTypeProjection))
) {
intersectionResult[typeParameter] = typeProjection
}
}
}
for ((key, value) in intersectionResult) {
result[key] = value
}
return true
}
// in Foo ~ in X => Foo ~ X
if (originalTypeProjection.kind == typeWithParametersProjection.kind &&
originalTypeProjection.kind != ProjectionKind.INVARIANT && originalTypeProjection.kind != ProjectionKind.STAR) {
return doUnify(originalType!!, typeWithParameters!!, targetTypeParameters, result)
}
// Foo? ~ X? => Foo ~ X
if (originalType?.nullability == ConeNullability.NULLABLE && typeWithParameters?.nullability == ConeNullability.NULLABLE) {
return doUnify(
originalTypeProjection.removeQuestionMark(session.typeContext),
typeWithParametersProjection.removeQuestionMark(session.typeContext),
targetTypeParameters, result,
)
}
// in Foo ~ out X => fail
// in Foo ~ X => may be OK
if (originalTypeProjection.kind != typeWithParametersProjection.kind && typeWithParametersProjection.kind != ProjectionKind.INVARIANT) {
return true
}
if (typeWithParameters is ConeFlexibleType) {
return doUnify(
originalTypeProjection,
typeWithParametersProjection.replaceType(typeWithParameters.lowerBound),
targetTypeParameters, result,
)
}
// Foo ~ X? => fail
if (originalType?.nullability != ConeNullability.NULLABLE && typeWithParameters?.nullability == ConeNullability.NULLABLE) {
return true
}
// Foo ~ X => x |-> Foo
// * ~ X => x |-> *
val typeParameter = (typeWithParameters as? ConeTypeParameterType)?.lookupTag?.typeParameterSymbol?.fir
if (typeParameter != null && typeParameter in targetTypeParameters) {
if (typeParameter in result && result[typeParameter] != originalTypeProjection) return false
result[typeParameter] = originalTypeProjection
return true
}
// Foo? ~ Foo || in Foo ~ Foo || Foo ~ Bar
if (originalType?.nullability?.isNullable != typeWithParameters?.nullability?.isNullable) return true
if (originalTypeProjection.kind != typeWithParametersProjection.kind) return true
if ((originalType as? ConeLookupTagBasedType)?.lookupTag != (typeWithParameters as? ConeLookupTagBasedType)?.lookupTag) return true
if (originalType == null || typeWithParameters == null) return true
// Foo<A> ~ Foo<B, C>
if (originalType.typeArguments.size != typeWithParameters.typeArguments.size) {
return true
}
// Foo ~ Foo
if (originalType.typeArguments.isEmpty()) {
return true
}
// Foo<...> ~ Foo<...>
for ((originalTypeArgument, typeWithParametersArgument) in originalType.typeArguments.zip(typeWithParameters.typeArguments)) {
if (!doUnify(originalTypeArgument, typeWithParametersArgument, targetTypeParameters, result)) return false
}
return true
}
private fun ConeTypeProjection.removeQuestionMark(typeContext: ConeTypeContext): ConeTypeProjection {
val type = type
require(type != null && type.nullability.isNullable) {
"Expected nullable type, got $type"
}
return replaceType(type.withNullability(ConeNullability.NOT_NULL, typeContext))
}
private fun ConeTypeProjection.replaceType(newType: ConeKotlinType): ConeTypeProjection =
when (kind) {
ProjectionKind.INVARIANT -> newType
ProjectionKind.IN -> ConeKotlinTypeProjectionIn(newType)
ProjectionKind.OUT -> ConeKotlinTypeProjectionOut(newType)
ProjectionKind.STAR -> error("Should not be a star projection")
}