Approximate types when only input types check is done. Otherwise given types are inconsistent with types obtained by ResultTypeResolver

^KT-45714 Fixed
This commit is contained in:
Victor Petukhov
2021-03-26 15:28:54 +03:00
parent cc1cb463b2
commit d06031ece3
2 changed files with 30 additions and 11 deletions
@@ -32,12 +32,12 @@ class MutableVariableWithConstraints private constructor(
}
// see @OnlyInputTypes annotation
fun getProjectedInputCallTypes(utilContext: ConstraintSystemUtilContext): Collection<KotlinTypeMarker> {
fun getProjectedInputCallTypes(utilContext: ConstraintSystemUtilContext): Collection<Pair<KotlinTypeMarker, ConstraintKind>> {
return with(utilContext) {
mutableConstraints
.mapNotNullTo(SmartList()) {
if (it.position.from is OnlyInputTypeConstraintPosition || it.inputTypePositionBeforeIncorporation != null)
it.type.unCapture()
it.type.unCapture() to it.kind
else null
}
}
@@ -8,7 +8,9 @@ package org.jetbrains.kotlin.resolve.calls.inference.model
import org.jetbrains.kotlin.resolve.calls.components.PostponedArgumentsAnalyzerContext
import org.jetbrains.kotlin.resolve.calls.inference.*
import org.jetbrains.kotlin.resolve.calls.inference.components.*
import org.jetbrains.kotlin.types.AbstractTypeApproximator
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
import org.jetbrains.kotlin.types.model.*
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.SmartSet
@@ -361,24 +363,41 @@ class NewConstraintSystemImpl(
}
}
private fun KotlinTypeMarker.substituteIfNecessary(substitutor: TypeSubstitutorMarker): KotlinTypeMarker {
private fun KotlinTypeMarker.substituteAndApproximateIfNecessary(
substitutor: TypeSubstitutorMarker,
approximator: AbstractTypeApproximator,
constraintKind: ConstraintKind
): KotlinTypeMarker {
val doesInputTypeContainsOtherVariables = this.contains { it.typeConstructor() is TypeVariableTypeConstructorMarker }
return if (doesInputTypeContainsOtherVariables) substitutor.safeSubstitute(this) else this
val substitutedType = if (doesInputTypeContainsOtherVariables) substitutor.safeSubstitute(this) else this
// Appoximation here is the same as ResultTypeResolver do
val approximatedType = when (constraintKind) {
ConstraintKind.LOWER ->
approximator.approximateToSuperType(substitutedType, TypeApproximatorConfiguration.InternalTypesApproximation)
ConstraintKind.UPPER ->
approximator.approximateToSubType(substitutedType, TypeApproximatorConfiguration.InternalTypesApproximation)
ConstraintKind.EQUALITY -> substitutedType
} ?: substitutedType
return approximatedType
}
private fun checkOnlyInputTypesAnnotation(variableWithConstraints: MutableVariableWithConstraints, resultType: KotlinTypeMarker) {
val substitutor = buildCurrentSubstitutor()
val isResultTypeEqualSomeInputType = variableWithConstraints.getProjectedInputCallTypes(utilContext).any { inputType ->
val inputTypeConstructor = inputType.typeConstructor()
val approximator = constraintInjector.typeApproximator
val isResultTypeEqualSomeInputType =
variableWithConstraints.getProjectedInputCallTypes(utilContext).any { (inputType, constraintKind) ->
val inputTypeConstructor = inputType.typeConstructor()
val otherResultType = inputType.substituteAndApproximateIfNecessary(substitutor, approximator, constraintKind)
if (AbstractTypeChecker.equalTypes(this, resultType, otherResultType)) return@any true
if (!inputTypeConstructor.isIntersection()) return@any false
if (inputTypeConstructor.isIntersection()) {
inputTypeConstructor.supertypes().any {
AbstractTypeChecker.equalTypes(this, resultType, it.substituteIfNecessary(substitutor))
val intersectionComponentResultType = it.substituteAndApproximateIfNecessary(substitutor, approximator, constraintKind)
AbstractTypeChecker.equalTypes(this, resultType, intersectionComponentResultType)
}
} else {
AbstractTypeChecker.equalTypes(this, resultType, inputType.substituteIfNecessary(substitutor))
}
}
if (!isResultTypeEqualSomeInputType) {
addError(OnlyInputTypesDiagnostic(variableWithConstraints.typeVariable))
}