[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:
Mikhail Zarechenskiy
2019-08-28 01:23:18 +03:00
parent ba6648d535
commit ca8da22569
41 changed files with 605 additions and 81 deletions
@@ -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
}
}
}
@@ -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
@@ -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
}
@@ -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
@@ -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)
@@ -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,
@@ -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 ->
@@ -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