FIR: Refine inference constraints when type variable in flexible position

That issue might be fixed via changing
TypeVariableMarker.shouldBeFlexible at ConeConstraintSystemUtilContext
but this and some other tricks have been added because of incorrect
handling of constraints where type variable has a flexible bound

^KT-51168 Fixed
This commit is contained in:
Denis.Zharkov
2022-04-12 16:30:55 +03:00
committed by teamcity
parent 853b7ec078
commit f70ae2df3a
54 changed files with 411 additions and 154 deletions
@@ -103,10 +103,12 @@ abstract class AbstractConeSubstitutor(protected val typeContext: ConeTypeContex
}
private fun ConeDefinitelyNotNullType.substituteOriginal(): ConeKotlinType? {
val substituted = substituteOrNull(original)
?.withNullability(ConeNullability.NOT_NULL, typeContext)
?.withAttributes(original.attributes)
?: return null
val substitutedOriginal = substituteOrNull(original) ?: return null
val substituted = substitutedOriginal.withNullability(
ConeNullability.NOT_NULL,
typeContext,
substitutedOriginal.attributes.add(original.attributes)
)
return ConeDefinitelyNotNullType.create(substituted, typeContext) ?: substituted
}
@@ -562,8 +562,7 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
return intersectionType.withAlternative(secondCandidate)
}
override fun SimpleTypeMarker.createConstraintPartForLowerBoundAndFlexibleTypeVariable(): KotlinTypeMarker =
createFlexibleType(this.makeSimpleTypeDefinitelyNotNullOrNotNull(), this.withNullability(true))
override fun useRefinedBoundsForTypeVariableInFlexiblePosition(): Boolean = true
override fun createSubstitutorForSuperTypes(baseType: KotlinTypeMarker): TypeSubstitutorMarker? =
if (baseType is ConeLookupTagBasedType) createSubstitutionForSupertype(baseType, session) else null
@@ -13,7 +13,10 @@ import org.jetbrains.kotlin.descriptors.ValueClassKind
import org.jetbrains.kotlin.descriptors.valueClassLoweringKind
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.*
import org.jetbrains.kotlin.fir.declarations.utils.expandedConeType
import org.jetbrains.kotlin.fir.declarations.utils.isInner
import org.jetbrains.kotlin.fir.declarations.utils.modality
import org.jetbrains.kotlin.fir.declarations.utils.superConeTypes
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.resolve.directExpansionType
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
@@ -289,7 +292,7 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty
else -> listOf(session.builtinTypes.anyType.type)
}
}
is ConeCapturedTypeConstructor -> supertypes!!
is ConeCapturedTypeConstructor -> supertypes.orEmpty()
is ConeIntersectionType -> intersectedTypes
is ConeIntegerLiteralType -> supertypes
else -> unknownConstructorError()
@@ -475,13 +478,13 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty
return when (this) {
is ConeFlexibleType -> this.upperBound.isNullableType()
is ConeTypeParameterType -> lookupTag.symbol.allBoundsAreNullable()
is ConeTypeParameterType -> lookupTag.symbol.allBoundsAreNullableOrUnresolved()
is ConeTypeVariableType -> {
val symbol = lookupTag.toSymbol(session) ?: return false
when (symbol) {
is FirClassSymbol -> false
is FirTypeAliasSymbol -> symbol.fir.expandedConeType?.isNullableType() ?: false
is FirTypeParameterSymbol -> symbol.allBoundsAreNullable()
is FirTypeParameterSymbol -> symbol.allBoundsAreNullableOrUnresolved()
}
}
is ConeIntersectionType -> intersectedTypes.all { it.isNullableType() }
@@ -490,8 +493,13 @@ interface ConeTypeContext : TypeSystemContext, TypeSystemOptimizationContext, Ty
}
}
private fun FirTypeParameterSymbol.allBoundsAreNullable(): Boolean {
return resolvedBounds.all { it.coneType.isNullableType() }
private fun FirTypeParameterSymbol.allBoundsAreNullableOrUnresolved(): Boolean {
for (bound in fir.bounds) {
if (bound !is FirResolvedTypeRef) return true
if (!bound.type.isNullableType()) return false
}
return true
}
private fun TypeConstructorMarker.toFirRegularClass(): FirRegularClass? {
@@ -60,7 +60,7 @@ fun TypeCheckerProviderContext.equalTypes(a: ConeKotlinType, b: ConeKotlinType):
private fun ConeTypeContext.makesSenseToBeDefinitelyNotNull(originalType: ConeKotlinType): Boolean {
return when (val type = originalType.lowerBoundIfFlexible()) {
is ConeTypeParameterType -> type.isNullableType()
is ConeTypeParameterType -> !type.lookupTag.symbol.isInitializedFir || type.isNullableType()
// Actually, this branch should work for type parameters as well, but it breaks some cases. See KT-40114.
// Basically, if we have `T : X..X?`, then `T <: Any` but we still have `T` != `T & Any`.
is ConeTypeVariableType, is ConeCapturedType -> {
@@ -75,12 +75,13 @@ private fun ConeTypeContext.makesSenseToBeDefinitelyNotNull(originalType: ConeKo
fun ConeDefinitelyNotNullType.Companion.create(
original: ConeKotlinType,
typeContext: ConeTypeContext
typeContext: ConeTypeContext,
forceWithoutCheck: Boolean = false,
): ConeDefinitelyNotNullType? {
return when (original) {
is ConeDefinitelyNotNullType -> original
is ConeFlexibleType -> create(original.lowerBound, typeContext)
is ConeSimpleKotlinType -> runIf(typeContext.makesSenseToBeDefinitelyNotNull(original)) {
is ConeSimpleKotlinType -> runIf(forceWithoutCheck || typeContext.makesSenseToBeDefinitelyNotNull(original)) {
ConeDefinitelyNotNullType(original.coneLowerBoundIfFlexible())
}
}