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 // see @OnlyInputTypes annotation
fun getProjectedInputCallTypes(utilContext: ConstraintSystemUtilContext): Collection<KotlinTypeMarker> { fun getProjectedInputCallTypes(utilContext: ConstraintSystemUtilContext): Collection<Pair<KotlinTypeMarker, ConstraintKind>> {
return with(utilContext) { return with(utilContext) {
mutableConstraints mutableConstraints
.mapNotNullTo(SmartList()) { .mapNotNullTo(SmartList()) {
if (it.position.from is OnlyInputTypeConstraintPosition || it.inputTypePositionBeforeIncorporation != null) if (it.position.from is OnlyInputTypeConstraintPosition || it.inputTypePositionBeforeIncorporation != null)
it.type.unCapture() it.type.unCapture() to it.kind
else null 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.components.PostponedArgumentsAnalyzerContext
import org.jetbrains.kotlin.resolve.calls.inference.* import org.jetbrains.kotlin.resolve.calls.inference.*
import org.jetbrains.kotlin.resolve.calls.inference.components.* 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.AbstractTypeChecker
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
import org.jetbrains.kotlin.types.model.* import org.jetbrains.kotlin.types.model.*
import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.kotlin.utils.SmartSet 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 } 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) { private fun checkOnlyInputTypesAnnotation(variableWithConstraints: MutableVariableWithConstraints, resultType: KotlinTypeMarker) {
val substitutor = buildCurrentSubstitutor() val substitutor = buildCurrentSubstitutor()
val isResultTypeEqualSomeInputType = variableWithConstraints.getProjectedInputCallTypes(utilContext).any { inputType -> val approximator = constraintInjector.typeApproximator
val inputTypeConstructor = inputType.typeConstructor() 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 { 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) { if (!isResultTypeEqualSomeInputType) {
addError(OnlyInputTypesDiagnostic(variableWithConstraints.typeVariable)) addError(OnlyInputTypesDiagnostic(variableWithConstraints.typeVariable))
} }