[NI] Improve CST algorithm to handle non-fixed variables
#KT-32456 Fixed #KT-32423 Fixed #KT-32818 Fixed #KT-33197 Fixed
This commit is contained in:
+46
-12
@@ -68,7 +68,8 @@ object NewCommonSuperTypeCalculator {
|
||||
depth: Int
|
||||
): SimpleTypeMarker {
|
||||
// i.e. result type also should be marked nullable
|
||||
val notAllNotNull = types.any { !AbstractNullabilityChecker.isSubtypeOfAny(this, it) }
|
||||
val notAllNotNull =
|
||||
types.any { !isStubRelatedType(it) && !AbstractNullabilityChecker.isSubtypeOfAny(contextStubTypesEqualToAnything, it) }
|
||||
val notNullTypes = if (notAllNotNull) types.map { it.withNullability(false) } else types
|
||||
|
||||
val commonSuperType = commonSuperTypeForNotNullTypes(notNullTypes, depth)
|
||||
@@ -94,7 +95,8 @@ object NewCommonSuperTypeCalculator {
|
||||
val uniqueTypes = arrayListOf<SimpleTypeMarker>()
|
||||
for (type in types) {
|
||||
val isNewUniqueType = uniqueTypes.all {
|
||||
!AbstractTypeChecker.equalTypes(this, it, type) || it.typeConstructor().isIntegerLiteralTypeConstructor()
|
||||
!AbstractTypeChecker.equalTypes(this, it, type, stubTypesEqualToAnything = false) ||
|
||||
it.typeConstructor().isIntegerLiteralTypeConstructor()
|
||||
}
|
||||
if (isNewUniqueType) {
|
||||
uniqueTypes += type
|
||||
@@ -111,7 +113,9 @@ object NewCommonSuperTypeCalculator {
|
||||
while (iterator.hasNext()) {
|
||||
val potentialSubtype = iterator.next()
|
||||
val isSubtype = supertypes.any { supertype ->
|
||||
supertype !== potentialSubtype && AbstractTypeChecker.isSubtypeOf(this, potentialSubtype, supertype)
|
||||
supertype !== potentialSubtype && AbstractTypeChecker.isSubtypeOf(
|
||||
this, potentialSubtype, supertype, stubTypesEqualToAnything = false
|
||||
)
|
||||
}
|
||||
|
||||
if (isSubtype) iterator.remove()
|
||||
@@ -120,13 +124,26 @@ object NewCommonSuperTypeCalculator {
|
||||
return supertypes
|
||||
}
|
||||
|
||||
/*
|
||||
* Common Supertype calculator works with proper types and stub types (which is a replacement for non-proper types)
|
||||
* Also, there are two invariant related to stub types:
|
||||
* - resulting type should be only proper type
|
||||
* - one of the input types is definitely proper type
|
||||
* */
|
||||
private fun TypeSystemCommonSuperTypesContext.commonSuperTypeForNotNullTypes(
|
||||
types: List<SimpleTypeMarker>,
|
||||
depth: Int
|
||||
): SimpleTypeMarker {
|
||||
if (types.size == 1) return types.single()
|
||||
|
||||
val uniqueTypes = uniquify(types)
|
||||
val nonStubTypes = types.filter { !isStubRelatedType(it) }
|
||||
if (nonStubTypes.size == 1) return nonStubTypes.single()
|
||||
|
||||
assert(nonStubTypes.isNotEmpty()) {
|
||||
"There should be at least one non-stub type to compute common supertype but there are: $types"
|
||||
}
|
||||
|
||||
val uniqueTypes = uniquify(nonStubTypes)
|
||||
if (uniqueTypes.size == 1) return uniqueTypes.single()
|
||||
|
||||
val explicitSupertypes = filterSupertypes(uniqueTypes)
|
||||
@@ -134,12 +151,23 @@ object NewCommonSuperTypeCalculator {
|
||||
findErrorTypeInSupertypesIfItIsNeeded(explicitSupertypes)?.let { return it }
|
||||
|
||||
findCommonIntegerLiteralTypesSuperType(explicitSupertypes)?.let { return it }
|
||||
// IntegerLiteralTypeConstructor.findCommonSuperType(explicitSupertypes)?.let { return it }
|
||||
|
||||
return findSuperTypeConstructorsAndIntersectResult(explicitSupertypes, depth)
|
||||
}
|
||||
|
||||
private fun TypeSystemCommonSuperTypesContext.findErrorTypeInSupertypesIfItIsNeeded(types: List<SimpleTypeMarker>): SimpleTypeMarker? {
|
||||
private fun TypeSystemCommonSuperTypesContext.isStubRelatedType(type: SimpleTypeMarker): Boolean {
|
||||
return type.isStubType() || isCapturedStubType(type)
|
||||
}
|
||||
|
||||
private fun TypeSystemCommonSuperTypesContext.isCapturedStubType(type: SimpleTypeMarker): Boolean {
|
||||
val projectedType = type.asCapturedType()?.typeConstructor()?.projection()?.getType() ?: return false
|
||||
return projectedType.asSimpleType()?.isStubType() == true
|
||||
}
|
||||
|
||||
private fun TypeSystemCommonSuperTypesContext.findErrorTypeInSupertypesIfItIsNeeded(
|
||||
types: List<SimpleTypeMarker>,
|
||||
contextStubTypesEqualToAnything: AbstractTypeCheckerContext
|
||||
): SimpleTypeMarker? {
|
||||
if (isErrorTypeAllowed) return null
|
||||
for (type in types) {
|
||||
collectAllSupertypes(type).firstOrNull { it.isError() }?.let { return it.toErrorType() }
|
||||
@@ -187,7 +215,7 @@ object NewCommonSuperTypeCalculator {
|
||||
nullable = false
|
||||
)
|
||||
|
||||
val typeCheckerContext = newBaseTypeCheckerContext(false)
|
||||
val typeCheckerContext = newBaseTypeCheckerContext(errorTypesEqualToAnything = false, stubTypesEqualToAnything = true)
|
||||
|
||||
/**
|
||||
* Sometimes one type can have several supertypes with given type constructor, suppose A <: List<Int> and A <: List<Double>.
|
||||
@@ -206,11 +234,17 @@ object NewCommonSuperTypeCalculator {
|
||||
val parameter = constructor.getParameter(index)
|
||||
var thereIsStar = false
|
||||
val typeProjections = correspondingSuperTypes.mapNotNull {
|
||||
it.getArgumentOrNull(index)?.let {
|
||||
if (it.isStarProjection()) {
|
||||
thereIsStar = true
|
||||
null
|
||||
} else it
|
||||
it.getArgumentOrNull(index)?.let { typeArgument ->
|
||||
when {
|
||||
typeArgument.isStarProjection() -> {
|
||||
thereIsStar = true
|
||||
null
|
||||
}
|
||||
|
||||
typeArgument.getType().lowerBoundIfFlexible().isStubType() -> null
|
||||
|
||||
else -> typeArgument
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -23,6 +23,7 @@ class PostponedArgumentsAnalyzer(
|
||||
) {
|
||||
interface Context : TypeSystemInferenceExtensionContext {
|
||||
fun buildCurrentSubstitutor(additionalBindings: Map<TypeConstructorMarker, StubTypeMarker>): TypeSubstitutorMarker
|
||||
fun buildNotFixedVariablesToStubTypesSubstitutor(): TypeSubstitutorMarker
|
||||
fun bindingStubsForPostponedVariables(): Map<TypeVariableMarker, StubTypeMarker>
|
||||
|
||||
// type can be proper if it not contains not fixed type variables
|
||||
|
||||
+6
-4
@@ -21,10 +21,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstituto
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintStorage
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.model.StubTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeSystemInferenceExtensionContext
|
||||
import org.jetbrains.kotlin.types.model.*
|
||||
|
||||
fun ConstraintStorage.buildCurrentSubstitutor(
|
||||
context: TypeSystemInferenceExtensionContext,
|
||||
@@ -50,6 +47,11 @@ fun ConstraintStorage.buildAbstractResultingSubstitutor(context: TypeSystemInfer
|
||||
return context.typeSubstitutorByTypeConstructor(currentSubstitutorMap + uninferredSubstitutorMap)
|
||||
}
|
||||
|
||||
fun ConstraintStorage.buildNotFixedVariablesToNonSubtypableTypesSubstitutor(context: TypeSystemInferenceExtensionContext) =
|
||||
context.typeSubstitutorByTypeConstructor(
|
||||
notFixedTypeVariables.mapValues { context.createStubType(it.value.typeVariable) }
|
||||
)
|
||||
|
||||
fun ConstraintStorage.buildResultingSubstitutor(context: TypeSystemInferenceExtensionContext): NewTypeSubstitutor {
|
||||
return buildAbstractResultingSubstitutor(context) as NewTypeSubstitutor
|
||||
}
|
||||
|
||||
+3
@@ -18,6 +18,9 @@ abstract class AbstractTypeCheckerContextForConstraintSystem : AbstractTypeCheck
|
||||
override val isErrorTypeEqualsToAnything: Boolean
|
||||
get() = true
|
||||
|
||||
override val isStubTypeEqualsToAnything: Boolean
|
||||
get() = true
|
||||
|
||||
abstract fun isMyTypeVariable(type: SimpleTypeMarker): Boolean
|
||||
|
||||
// super and sub type isSingleClassifierType
|
||||
|
||||
+1
-2
@@ -65,7 +65,6 @@ class ConstraintInjector(
|
||||
addSubTypeConstraintAndIncorporateIt(c, b, a, incorporationPosition)
|
||||
}
|
||||
|
||||
|
||||
private fun addSubTypeConstraintAndIncorporateIt(
|
||||
c: Context,
|
||||
lowerType: KotlinTypeMarker,
|
||||
@@ -126,7 +125,7 @@ class ConstraintInjector(
|
||||
val possibleNewConstraints: MutableList<Pair<TypeVariableMarker, Constraint>>
|
||||
) : AbstractTypeCheckerContextForConstraintSystem(), ConstraintIncorporator.Context, TypeSystemInferenceExtensionContext by c {
|
||||
|
||||
val baseContext: AbstractTypeCheckerContext = newBaseTypeCheckerContext(isErrorTypeEqualsToAnything)
|
||||
val baseContext: AbstractTypeCheckerContext = newBaseTypeCheckerContext(isErrorTypeEqualsToAnything, isStubTypeEqualsToAnything)
|
||||
|
||||
override fun substitutionSupertypePolicy(type: SimpleTypeMarker): SupertypesPolicy {
|
||||
return baseContext.substitutionSupertypePolicy(type)
|
||||
|
||||
+27
-18
@@ -73,29 +73,26 @@ interface NewTypeSubstitutor: TypeSubstitutorMarker {
|
||||
"and class: ${type::class.java.canonicalName}. type.toString() = $type"
|
||||
}
|
||||
val capturedType = if (type is DefinitelyNotNullType) type.original as NewCapturedType else type as NewCapturedType
|
||||
val lower = capturedType.lowerType?.let { substitute(it, keepAnnotation, runCapturedChecks = false) }
|
||||
if (lower != null && capturedType.lowerType is StubType) {
|
||||
return NewCapturedType(
|
||||
capturedType.captureStatus,
|
||||
NewCapturedTypeConstructor(TypeProjectionImpl(typeConstructor.projection.projectionKind, lower)),
|
||||
lower
|
||||
)
|
||||
}
|
||||
|
||||
if (lower != null) throw IllegalStateException(
|
||||
"Illegal type substitutor: $this, " +
|
||||
"because for captured type '$type' lower type approximation should be null, but it is: '$lower'," +
|
||||
"original lower type: '${capturedType.lowerType}"
|
||||
)
|
||||
val innerType = capturedType.lowerType ?: capturedType.constructor.projection.type.unwrap()
|
||||
val substitutedInnerType = substitute(innerType, keepAnnotation, runCapturedChecks = false)
|
||||
|
||||
if (substitutedInnerType != null) {
|
||||
if (innerType is StubType || substitutedInnerType is StubType) {
|
||||
return NewCapturedType(
|
||||
capturedType.captureStatus,
|
||||
NewCapturedTypeConstructor(TypeProjectionImpl(typeConstructor.projection.projectionKind, substitutedInnerType)),
|
||||
lowerType = if (capturedType.lowerType != null) substitutedInnerType else null
|
||||
)
|
||||
} else {
|
||||
throwExceptionAboutInvalidCapturedSubstitution(capturedType, innerType, substitutedInnerType)
|
||||
}
|
||||
}
|
||||
|
||||
if (AbstractTypeChecker.RUN_SLOW_ASSERTIONS) {
|
||||
typeConstructor.supertypes.forEach { supertype ->
|
||||
substitute(supertype, keepAnnotation, runCapturedChecks = false)?.let {
|
||||
throw IllegalStateException(
|
||||
"Illegal type substitutor: $this, " +
|
||||
"because for captured type '$type' supertype approximation should be null, but it is: '$supertype'," +
|
||||
"original supertype: '$supertype'"
|
||||
)
|
||||
throwExceptionAboutInvalidCapturedSubstitution(capturedType, supertype, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,6 +127,18 @@ interface NewTypeSubstitutor: TypeSubstitutorMarker {
|
||||
return replacement
|
||||
}
|
||||
|
||||
private fun throwExceptionAboutInvalidCapturedSubstitution(
|
||||
capturedType: SimpleType,
|
||||
innerType: UnwrappedType,
|
||||
substitutedInnerType: UnwrappedType
|
||||
): Nothing =
|
||||
throw IllegalStateException(
|
||||
"Illegal type substitutor: $this, " +
|
||||
"because for captured type '$capturedType' supertype approximation should be null, but it is: '$innerType'," +
|
||||
"original supertype: '$substitutedInnerType'"
|
||||
)
|
||||
|
||||
|
||||
private fun substituteParametrizedType(
|
||||
type: SimpleType,
|
||||
keepAnnotation: Boolean,
|
||||
|
||||
+33
-3
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.model.*
|
||||
import org.jetbrains.kotlin.resolve.constants.IntegerLiteralTypeConstructor
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeSystemInferenceExtensionContext
|
||||
|
||||
class ResultTypeResolver(
|
||||
@@ -30,6 +31,7 @@ class ResultTypeResolver(
|
||||
) {
|
||||
interface Context : TypeSystemInferenceExtensionContext {
|
||||
fun isProperType(type: KotlinTypeMarker): Boolean
|
||||
fun buildNotFixedVariablesToStubTypesSubstitutor(): TypeSubstitutorMarker
|
||||
}
|
||||
|
||||
fun findResultType(c: Context, variableWithConstraints: VariableWithConstraints, direction: ResolveDirection): KotlinTypeMarker {
|
||||
@@ -83,9 +85,10 @@ class ResultTypeResolver(
|
||||
}
|
||||
|
||||
private fun Context.findSubType(variableWithConstraints: VariableWithConstraints): KotlinTypeMarker? {
|
||||
val lowerConstraints = variableWithConstraints.constraints.filter { it.kind == ConstraintKind.LOWER && isProperType(it.type) }
|
||||
if (lowerConstraints.isNotEmpty()) {
|
||||
val types = sinkIntegerLiteralTypes(lowerConstraints.map { it.type })
|
||||
val lowerConstraintTypes = prepareLowerConstraints(variableWithConstraints.constraints)
|
||||
|
||||
if (lowerConstraintTypes.isNotEmpty()) {
|
||||
val types = sinkIntegerLiteralTypes(lowerConstraintTypes)
|
||||
val commonSuperType = with(NewCommonSuperTypeCalculator) {
|
||||
this@findSubType.commonSuperType(types)
|
||||
}
|
||||
@@ -116,6 +119,33 @@ class ResultTypeResolver(
|
||||
return null
|
||||
}
|
||||
|
||||
private fun Context.prepareLowerConstraints(constraints: List<Constraint>): List<KotlinTypeMarker> {
|
||||
var atLeastOneProper = false
|
||||
var atLeastOneNonProper = false
|
||||
|
||||
val lowerConstraintTypes = mutableListOf<KotlinTypeMarker>()
|
||||
|
||||
for (constraint in constraints) {
|
||||
if (constraint.kind != ConstraintKind.LOWER) continue
|
||||
|
||||
val type = constraint.type
|
||||
lowerConstraintTypes.add(type)
|
||||
|
||||
if (isProperType(type)) {
|
||||
atLeastOneProper = true
|
||||
} else {
|
||||
atLeastOneNonProper = true
|
||||
}
|
||||
}
|
||||
|
||||
if (!atLeastOneProper) return emptyList()
|
||||
if (!atLeastOneNonProper) return lowerConstraintTypes
|
||||
|
||||
val notFixedToStubTypesSubstitutor = buildNotFixedVariablesToStubTypesSubstitutor()
|
||||
|
||||
return lowerConstraintTypes.map { if (isProperType(it)) it else notFixedToStubTypesSubstitutor.safeSubstitute(it) }
|
||||
}
|
||||
|
||||
private fun Context.sinkIntegerLiteralTypes(types: List<KotlinTypeMarker>): List<KotlinTypeMarker> {
|
||||
return types.sortedBy { type ->
|
||||
|
||||
|
||||
+5
@@ -307,6 +307,11 @@ class NewConstraintSystemImpl(
|
||||
return storage.buildCurrentSubstitutor(this, additionalBindings)
|
||||
}
|
||||
|
||||
override fun buildNotFixedVariablesToStubTypesSubstitutor(): TypeSubstitutorMarker {
|
||||
checkState(State.BUILDING, State.COMPLETION)
|
||||
return storage.buildNotFixedVariablesToNonSubtypableTypesSubstitutor(this)
|
||||
}
|
||||
|
||||
override fun bindingStubsForPostponedVariables(): Map<TypeVariableMarker, StubTypeMarker> {
|
||||
checkState(State.BUILDING, State.COMPLETION)
|
||||
// TODO: SUB
|
||||
|
||||
Reference in New Issue
Block a user