[FE] Check if type variable is fixed into an empty intersection type and report resolution warnings/errors if needed (completion stage)

^KT-51221 Fixed
This commit is contained in:
Victor Petukhov
2022-02-11 11:37:48 +03:00
committed by teamcity
parent 8e834fc7bb
commit 9e9e0211eb
17 changed files with 93 additions and 19 deletions
@@ -272,6 +272,11 @@ interface ConeInferenceContext : TypeSystemInferenceExtensionContext, ConeTypeCo
return ConeStubTypeForTypeVariableInSubtyping(typeVariable, ConeNullability.create(typeVariable.defaultType().isMarkedNullable())) return ConeStubTypeForTypeVariableInSubtyping(typeVariable, ConeNullability.create(typeVariable.defaultType().isMarkedNullable()))
} }
override fun KotlinTypeMarker.isFinal(): Boolean {
require(this is ConeKotlinType)
return !this.canHaveSubtypes(session)
}
override fun KotlinTypeMarker.removeAnnotations(): KotlinTypeMarker { override fun KotlinTypeMarker.removeAnnotations(): KotlinTypeMarker {
require(this is ConeKotlinType) require(this is ConeKotlinType)
return withAttributes(ConeAttributes.Empty) return withAttributes(ConeAttributes.Empty)
@@ -37,7 +37,7 @@ class InferenceComponents(val session: FirSession) : FirSessionComponent {
val constraintSystemFactory = ConstraintSystemFactory() val constraintSystemFactory = ConstraintSystemFactory()
fun createConstraintSystem(): NewConstraintSystemImpl { fun createConstraintSystem(): NewConstraintSystemImpl {
return NewConstraintSystemImpl(injector, typeContext) return NewConstraintSystemImpl(injector, typeContext, session.languageVersionSettings)
} }
inner class ConstraintSystemFactory { inner class ConstraintSystemFactory {
@@ -61,7 +61,9 @@ class BuilderInferenceSession(
psiCallResolver, postponedArgumentsAnalyzer, kotlinConstraintSystemCompleter, callComponents, builtIns psiCallResolver, postponedArgumentsAnalyzer, kotlinConstraintSystemCompleter, callComponents, builtIns
) { ) {
private lateinit var lambda: ResolvedLambdaAtom private lateinit var lambda: ResolvedLambdaAtom
private val commonSystem = NewConstraintSystemImpl(callComponents.constraintInjector, builtIns, callComponents.kotlinTypeRefiner) private val commonSystem = NewConstraintSystemImpl(
callComponents.constraintInjector, builtIns, callComponents.kotlinTypeRefiner, topLevelCallContext.languageVersionSettings
)
init { init {
if (topLevelCallContext.inferenceSession is StubTypesBasedInferenceSession<*>) { if (topLevelCallContext.inferenceSession is StubTypesBasedInferenceSession<*>) {
@@ -103,8 +103,9 @@ class KotlinResolutionCallbacksImpl(
) as KotlinType ) as KotlinType
} }
override fun createEmptyConstraintSystem(): NewConstraintSystem = override fun createEmptyConstraintSystem(): NewConstraintSystem = NewConstraintSystemImpl(
NewConstraintSystemImpl(callComponents.constraintInjector, callComponents.builtIns, callComponents.kotlinTypeRefiner) callComponents.constraintInjector, callComponents.builtIns, callComponents.kotlinTypeRefiner, callComponents.languageVersionSettings
)
override fun resolveCallableReferenceArgument( override fun resolveCallableReferenceArgument(
argument: CallableReferenceKotlinCallArgument, argument: CallableReferenceKotlinCallArgument,
@@ -113,7 +113,7 @@ class KotlinResolutionStatelessCallbacksImpl(
constraintInjector: ConstraintInjector, builtIns: KotlinBuiltIns constraintInjector: ConstraintInjector, builtIns: KotlinBuiltIns
): SimpleConstraintSystem { ): SimpleConstraintSystem {
return if (languageVersionSettings.getFlag(AnalysisFlags.constraintSystemForOverloadResolution).forNewInference()) return if (languageVersionSettings.getFlag(AnalysisFlags.constraintSystemForOverloadResolution).forNewInference())
SimpleConstraintSystemImpl(constraintInjector, builtIns, kotlinTypeRefiner) SimpleConstraintSystemImpl(constraintInjector, builtIns, kotlinTypeRefiner, languageVersionSettings)
else else
ConstraintSystemBuilderImpl.forSpecificity() ConstraintSystemBuilderImpl.forSpecificity()
} }
@@ -120,7 +120,9 @@ abstract class StubTypesBasedInferenceSession<D : CallableDescriptor>(
for (callInfo in listOf(goodCandidate, badCandidate)) { for (callInfo in listOf(goodCandidate, badCandidate)) {
val atomsToAnalyze = mutableListOf<ResolvedAtom>(callInfo.callResolutionResult) val atomsToAnalyze = mutableListOf<ResolvedAtom>(callInfo.callResolutionResult)
val system = NewConstraintSystemImpl(callComponents.constraintInjector, builtIns, callComponents.kotlinTypeRefiner).apply { val system = NewConstraintSystemImpl(
callComponents.constraintInjector, builtIns, callComponents.kotlinTypeRefiner, callComponents.languageVersionSettings
).apply {
addOtherSystem(callInfo.callResolutionResult.constraintSystem.getBuilder().currentStorage()) addOtherSystem(callInfo.callResolutionResult.constraintSystem.getBuilder().currentStorage())
/* /*
* This is needed for very stupid case, when we have some delegate with good `getValue` and bad `setValue` that * This is needed for very stupid case, when we have some delegate with good `getValue` and bad `setValue` that
@@ -159,7 +161,8 @@ abstract class StubTypesBasedInferenceSession<D : CallableDescriptor>(
val commonSystem = NewConstraintSystemImpl( val commonSystem = NewConstraintSystemImpl(
callComponents.constraintInjector, callComponents.constraintInjector,
builtIns, builtIns,
callComponents.kotlinTypeRefiner callComponents.kotlinTypeRefiner,
callComponents.languageVersionSettings
).apply { ).apply {
addOtherSystem(currentConstraintSystem()) addOtherSystem(currentConstraintSystem())
} }
@@ -8,9 +8,7 @@ package org.jetbrains.kotlin.resolve.calls.inference.components
import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintSystemError import org.jetbrains.kotlin.resolve.calls.inference.model.*
import org.jetbrains.kotlin.resolve.calls.inference.model.FixVariableConstraintPosition
import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
import org.jetbrains.kotlin.resolve.calls.model.PostponedAtomWithRevisableExpectedType import org.jetbrains.kotlin.resolve.calls.model.PostponedAtomWithRevisableExpectedType
import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtomMarker
import org.jetbrains.kotlin.types.model.KotlinTypeMarker import org.jetbrains.kotlin.types.model.KotlinTypeMarker
@@ -110,4 +108,9 @@ abstract class ConstraintSystemCompletionContext : VariableFixationFinder.Contex
private fun <T : PostponedResolvedAtomMarker> findPostponedArgumentWithFixedInputTypes( private fun <T : PostponedResolvedAtomMarker> findPostponedArgumentWithFixedInputTypes(
postponedArguments: List<T> postponedArguments: List<T>
) = postponedArguments.firstOrNull { argument -> argument.inputTypes.all { containsOnlyFixedVariables(it) } } ) = postponedArguments.firstOrNull { argument -> argument.inputTypes.all { containsOnlyFixedVariables(it) } }
fun List<Constraint>.extractUpperTypes(): List<KotlinTypeMarker> =
filter { constraint ->
constraint.kind == ConstraintKind.UPPER && !constraint.type.contains { !it.typeConstructor().isClassTypeConstructor() }
}.map { it.type }
} }
@@ -113,7 +113,7 @@ class NewConstraintWarning(
override val lowerType: KotlinTypeMarker, override val lowerType: KotlinTypeMarker,
override val upperType: KotlinTypeMarker, override val upperType: KotlinTypeMarker,
override val position: IncorporationConstraintPosition, override val position: IncorporationConstraintPosition,
) : ConstraintSystemError(RESOLVED), NewConstraintMismatch ) : ConstraintSystemError(RESOLVED_WITH_WARNING), NewConstraintMismatch
class CapturedTypeFromSubtyping( class CapturedTypeFromSubtyping(
val typeVariable: TypeVariableMarker, val typeVariable: TypeVariableMarker,
@@ -135,6 +135,21 @@ class ConstrainingTypeIsError(
class NoSuccessfulFork(val position: IncorporationConstraintPosition) : ConstraintSystemError(INAPPLICABLE) class NoSuccessfulFork(val position: IncorporationConstraintPosition) : ConstraintSystemError(INAPPLICABLE)
sealed interface InferredEmptyIntersection {
val incompatibleTypes: Collection<KotlinTypeMarker>
val typeVariable: TypeVariableMarker
}
class InferredEmptyIntersectionWarning(
override val incompatibleTypes: Collection<KotlinTypeMarker>,
override val typeVariable: TypeVariableMarker
) : ConstraintSystemError(RESOLVED_WITH_WARNING), InferredEmptyIntersection
class InferredEmptyIntersectionError(
override val incompatibleTypes: Collection<KotlinTypeMarker>,
override val typeVariable: TypeVariableMarker
) : ConstraintSystemError(INAPPLICABLE), InferredEmptyIntersection
class OnlyInputTypesDiagnostic(val typeVariable: TypeVariableMarker) : ConstraintSystemError(INAPPLICABLE) class OnlyInputTypesDiagnostic(val typeVariable: TypeVariableMarker) : ConstraintSystemError(INAPPLICABLE)
object LowerPriorityToPreserveCompatibility : ConstraintSystemError(RESOLVED_NEED_PRESERVE_COMPATIBILITY) object LowerPriorityToPreserveCompatibility : ConstraintSystemError(RESOLVED_NEED_PRESERVE_COMPATIBILITY)
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.resolve.calls.inference.model package org.jetbrains.kotlin.resolve.calls.inference.model
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageVersionSettings
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.*
@@ -19,7 +21,8 @@ import kotlin.math.max
class NewConstraintSystemImpl( class NewConstraintSystemImpl(
private val constraintInjector: ConstraintInjector, private val constraintInjector: ConstraintInjector,
val typeSystemContext: TypeSystemInferenceExtensionContext val typeSystemContext: TypeSystemInferenceExtensionContext,
private val languageVersionSettings: LanguageVersionSettings,
) : ConstraintSystemCompletionContext(), ) : ConstraintSystemCompletionContext(),
TypeSystemInferenceExtensionContext by typeSystemContext, TypeSystemInferenceExtensionContext by typeSystemContext,
NewConstraintSystem, NewConstraintSystem,
@@ -408,6 +411,8 @@ class NewConstraintSystemImpl(
) = with(utilContext) { ) = with(utilContext) {
checkState(State.BUILDING, State.COMPLETION) checkState(State.BUILDING, State.COMPLETION)
checkInferredEmptyIntersection(variable, resultType)
constraintInjector.addInitialEqualityConstraint(this@NewConstraintSystemImpl, variable.defaultType(), resultType, position) constraintInjector.addInitialEqualityConstraint(this@NewConstraintSystemImpl, variable.defaultType(), resultType, position)
/* /*
@@ -434,7 +439,19 @@ class NewConstraintSystemImpl(
doPostponedComputationsIfAllVariablesAreFixed() doPostponedComputationsIfAllVariablesAreFixed()
} }
@OptIn(ExperimentalStdlibApi::class) private fun checkInferredEmptyIntersection(variable: TypeVariableMarker, resultType: KotlinTypeMarker) {
val intersectionTypeConstructor = resultType.typeConstructor().takeIf { it is IntersectionTypeConstructorMarker } ?: return
val isInferredEmptyIntersectionForbidden =
languageVersionSettings.supportsFeature(LanguageFeature.ForbidInferringTypeVariablesIntoEmptyIntersection)
val intersectionComponents = intersectionTypeConstructor.supertypes()
if (intersectionComponents.isEmptyIntersection()) {
val errorFactory =
if (isInferredEmptyIntersectionForbidden) ::InferredEmptyIntersectionError else ::InferredEmptyIntersectionWarning
addError(errorFactory(intersectionComponents, variable))
}
}
private fun checkMissedConstraints() { private fun checkMissedConstraints() {
val constraintSystem = this@NewConstraintSystemImpl val constraintSystem = this@NewConstraintSystemImpl
val errorsByMissedConstraints = buildList { val errorsByMissedConstraints = buildList {
@@ -29,6 +29,7 @@ enum class CandidateApplicability {
PROPERTY_AS_OPERATOR, // using property of functional type as an operator. From resolution perspective, this is considered successful. PROPERTY_AS_OPERATOR, // using property of functional type as an operator. From resolution perspective, this is considered successful.
RESOLVED_NEED_PRESERVE_COMPATIBILITY, // call resolved successfully, but using new features that changes resolve RESOLVED_NEED_PRESERVE_COMPATIBILITY, // call resolved successfully, but using new features that changes resolve
RESOLVED_WITH_ERROR, // call has error, but it is still successful from resolution perspective RESOLVED_WITH_ERROR, // call has error, but it is still successful from resolution perspective
RESOLVED_WITH_WARNING, // generally call is successful, but there are additional resolution warnings (e.g. for deprecation something)
RESOLVED, // call success or has uncompleted inference or in other words possible successful candidate RESOLVED, // call success or has uncompleted inference or in other words possible successful candidate
} }
@@ -120,7 +120,8 @@ private inline fun Any?.errorMessage(): String {
fun NewConstraintSystemImpl( fun NewConstraintSystemImpl(
constraintInjector: ConstraintInjector, constraintInjector: ConstraintInjector,
builtIns: KotlinBuiltIns, builtIns: KotlinBuiltIns,
kotlinTypeRefiner: KotlinTypeRefiner kotlinTypeRefiner: KotlinTypeRefiner,
languageVersionSettings: LanguageVersionSettings
): NewConstraintSystemImpl { ): NewConstraintSystemImpl {
return NewConstraintSystemImpl(constraintInjector, ClassicTypeSystemContextForCS(builtIns, kotlinTypeRefiner)) return NewConstraintSystemImpl(constraintInjector, ClassicTypeSystemContextForCS(builtIns, kotlinTypeRefiner), languageVersionSettings)
} }
@@ -79,7 +79,8 @@ sealed class ResolutionCandidate : Candidate, KotlinDiagnosticsHolder {
fun getSystem(): NewConstraintSystem { fun getSystem(): NewConstraintSystem {
if (newSystem == null) { if (newSystem == null) {
newSystem = NewConstraintSystemImpl( newSystem = NewConstraintSystemImpl(
callComponents.constraintInjector, callComponents.builtIns, callComponents.kotlinTypeRefiner callComponents.constraintInjector, callComponents.builtIns,
callComponents.kotlinTypeRefiner, callComponents.languageVersionSettings
) )
if (baseSystem != null) { if (baseSystem != null) {
newSystem!!.addOtherSystem(baseSystem!!) newSystem!!.addOtherSystem(baseSystem!!)
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.resolve.calls.inference.components package org.jetbrains.kotlin.resolve.calls.inference.components
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.resolve.calls.components.ClassicTypeSystemContextForCS import org.jetbrains.kotlin.resolve.calls.components.ClassicTypeSystemContextForCS
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
@@ -33,9 +34,12 @@ import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
class SimpleConstraintSystemImpl( class SimpleConstraintSystemImpl(
constraintInjector: ConstraintInjector, constraintInjector: ConstraintInjector,
builtIns: KotlinBuiltIns, builtIns: KotlinBuiltIns,
kotlinTypeRefiner: KotlinTypeRefiner kotlinTypeRefiner: KotlinTypeRefiner,
languageVersionSettings: LanguageVersionSettings
) : SimpleConstraintSystem { ) : SimpleConstraintSystem {
val system = NewConstraintSystemImpl(constraintInjector, ClassicTypeSystemContextForCS(builtIns, kotlinTypeRefiner)) val system = NewConstraintSystemImpl(
constraintInjector, ClassicTypeSystemContextForCS(builtIns, kotlinTypeRefiner), languageVersionSettings
)
val csBuilder: ConstraintSystemBuilder = val csBuilder: ConstraintSystemBuilder =
system.getBuilder() system.getBuilder()
@@ -33,7 +33,10 @@ class SimpleCandidateFactory(
val baseSystem: ConstraintStorage val baseSystem: ConstraintStorage
init { init {
val baseSystem = NewConstraintSystemImpl(callComponents.constraintInjector, callComponents.builtIns, callComponents.kotlinTypeRefiner) val baseSystem = NewConstraintSystemImpl(
callComponents.constraintInjector, callComponents.builtIns,
callComponents.kotlinTypeRefiner, callComponents.languageVersionSettings
)
if (!inferenceSession.resolveReceiverIndependently()) { if (!inferenceSession.resolveReceiverIndependently()) {
baseSystem.addSubsystemFromArgument(kotlinCall.explicitReceiver) baseSystem.addSubsystemFromArgument(kotlinCall.explicitReceiver)
baseSystem.addSubsystemFromArgument(kotlinCall.dispatchReceiverForInvokeExtension) baseSystem.addSubsystemFromArgument(kotlinCall.dispatchReceiverForInvokeExtension)
@@ -261,6 +261,7 @@ enum class LanguageFeature(
ForbidUsingExtensionPropertyTypeParameterInDelegate(KOTLIN_1_8, kind = BUG_FIX), ForbidUsingExtensionPropertyTypeParameterInDelegate(KOTLIN_1_8, kind = BUG_FIX),
ModifierNonBuiltinSuspendFunError(KOTLIN_1_8), ModifierNonBuiltinSuspendFunError(KOTLIN_1_8),
SynchronizedSuspendError(KOTLIN_1_8), SynchronizedSuspendError(KOTLIN_1_8),
ForbidInferringTypeVariablesIntoEmptyIntersection(KOTLIN_1_8, kind = BUG_FIX), // KT-51221
// 1.9 // 1.9
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.types.model package org.jetbrains.kotlin.types.model
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.TypeCheckerState import org.jetbrains.kotlin.types.TypeCheckerState
import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.types.Variance
import kotlin.contracts.ExperimentalContracts import kotlin.contracts.ExperimentalContracts
@@ -175,6 +176,18 @@ interface TypeSystemInferenceExtensionContext : TypeSystemContext, TypeSystemBui
fun createStubTypeForBuilderInference(typeVariable: TypeVariableMarker): StubTypeMarker fun createStubTypeForBuilderInference(typeVariable: TypeVariableMarker): StubTypeMarker
fun createStubTypeForTypeVariablesInSubtyping(typeVariable: TypeVariableMarker): StubTypeMarker fun createStubTypeForTypeVariablesInSubtyping(typeVariable: TypeVariableMarker): StubTypeMarker
fun KotlinTypeMarker.isFinal(): Boolean
fun Collection<KotlinTypeMarker>.isEmptyIntersection(): Boolean =
any { first ->
any { second ->
first !== second &&
first.isFinal() &&
second.typeConstructor().isClassTypeConstructor() &&
!AbstractTypeChecker.isSubtypeOf(this@TypeSystemInferenceExtensionContext, first, second)
}
}
fun KotlinTypeMarker.removeAnnotations(): KotlinTypeMarker fun KotlinTypeMarker.removeAnnotations(): KotlinTypeMarker
fun KotlinTypeMarker.removeExactAnnotation(): KotlinTypeMarker fun KotlinTypeMarker.removeExactAnnotation(): KotlinTypeMarker
@@ -460,6 +460,10 @@ interface ClassicTypeSystemContext : TypeSystemInferenceExtensionContext, TypeSy
return makeSimpleTypeDefinitelyNotNullOrNotNullInternal(this) return makeSimpleTypeDefinitelyNotNullOrNotNullInternal(this)
} }
override fun KotlinTypeMarker.isFinal(): Boolean {
require(this is KotlinType, this::errorMessage)
return !TypeUtils.canHaveSubtypes(KotlinTypeChecker.DEFAULT, this)
}
override fun KotlinTypeMarker.removeAnnotations(): KotlinTypeMarker { override fun KotlinTypeMarker.removeAnnotations(): KotlinTypeMarker {
require(this is UnwrappedType, this::errorMessage) require(this is UnwrappedType, this::errorMessage)