From d06031ece32fc42fe9327d05174eb443b5c9764c Mon Sep 17 00:00:00 2001 From: Victor Petukhov Date: Fri, 26 Mar 2021 15:28:54 +0300 Subject: [PATCH] Approximate types when only input types check is done. Otherwise given types are inconsistent with types obtained by ResultTypeResolver ^KT-45714 Fixed --- .../model/MutableConstraintStorage.kt | 4 +- .../model/NewConstraintSystemImpl.kt | 37 ++++++++++++++----- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt index b2c0ccbdf0a..63c95717622 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/MutableConstraintStorage.kt @@ -32,12 +32,12 @@ class MutableVariableWithConstraints private constructor( } // see @OnlyInputTypes annotation - fun getProjectedInputCallTypes(utilContext: ConstraintSystemUtilContext): Collection { + fun getProjectedInputCallTypes(utilContext: ConstraintSystemUtilContext): Collection> { 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 } } diff --git a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt index ba05158e1ff..d45d2bf3e2e 100644 --- a/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt +++ b/compiler/resolution.common/src/org/jetbrains/kotlin/resolve/calls/inference/model/NewConstraintSystemImpl.kt @@ -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)) }