diff --git a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt index 19134a565d2..00c7ba762bd 100644 --- a/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt +++ b/compiler/resolution/src/org/jetbrains/kotlin/resolve/calls/inference/components/ConstraintIncorporator.kt @@ -157,6 +157,11 @@ class ConstraintIncorporator( isSubtype: Boolean ) { if (targetVariable in getNestedTypeVariables(newConstraint)) return + + val isUsefulForNullabilityConstraint = + isPotentialUsefulNullabilityConstraint(newConstraint, otherConstraint.type, otherConstraint.kind) + + if (!isUsefulForNullabilityConstraint && !containsConstrainingTypeWithoutProjection(newConstraint, otherConstraint)) return if (trivialConstraintTypeInferenceOracle.isGeneratedConstraintTrivial( baseConstraint, otherConstraint, newConstraint, isSubtype ) @@ -172,6 +177,28 @@ class ConstraintIncorporator( addNewIncorporatedConstraint(targetVariable, newConstraint, ConstraintContext(kind, derivedFrom)) } + fun Context.containsConstrainingTypeWithoutProjection( + newConstraint: KotlinTypeMarker, + otherConstraint: Constraint + ): Boolean { + return getNestedArguments(newConstraint).any { + it.getType().typeConstructor() == otherConstraint.type.typeConstructor() && it.getVariance() == TypeVariance.INV + } + } + + private fun Context.isPotentialUsefulNullabilityConstraint( + newConstraint: KotlinTypeMarker, + otherConstraint: KotlinTypeMarker, + kind: ConstraintKind + ): Boolean { + val otherConstraintCanAddNullabilityToNewOne = + !newConstraint.isNullableType() && otherConstraint.isNullableType() && kind == ConstraintKind.LOWER + val newConstraintCanAddNullabilityToOtherOne = + newConstraint.isNullableType() && !otherConstraint.isNullableType() && kind == ConstraintKind.UPPER + + return otherConstraintCanAddNullabilityToNewOne || newConstraintCanAddNullabilityToOtherOne + } + fun Context.getNestedTypeVariables(type: KotlinTypeMarker): List = getNestedArguments(type).mapNotNull { getTypeVariable(it.getType().typeConstructor()) } @@ -189,19 +216,36 @@ class ConstraintIncorporator( private fun TypeSystemInferenceExtensionContext.getNestedArguments(type: KotlinTypeMarker): List { val result = ArrayList() - val stack = ArrayDeque() + + when (type) { + is FlexibleType -> { + stack.push(createTypeArgument(type.lowerBound, TypeVariance.INV)) + stack.push(createTypeArgument(type.upperBound, TypeVariance.INV)) + } + else -> stack.push(createTypeArgument(type, TypeVariance.INV)) + } + stack.push(createTypeArgument(type, TypeVariance.INV)) + val addArgumentsToStack = { projectedType: KotlinTypeMarker -> + for (argumentIndex in 0 until projectedType.argumentsCount()) { + stack.add(projectedType.getArgument(argumentIndex)) + } + } + while (!stack.isEmpty()) { val typeProjection = stack.pop() if (typeProjection.isStarProjection()) continue result.add(typeProjection) - val projectedType = typeProjection.getType() - for (argumentIndex in 0 until projectedType.argumentsCount()) { - stack.add(projectedType.getArgument(argumentIndex)) + when (val projectedType = typeProjection.getType()) { + is FlexibleType -> { + addArgumentsToStack(projectedType.lowerBound) + addArgumentsToStack(projectedType.upperBound) + } + else -> addArgumentsToStack(projectedType) } } return result diff --git a/compiler/testData/diagnostics/tests/inference/commonSystem/kt32818.kt b/compiler/testData/diagnostics/tests/inference/commonSystem/kt32818.kt index 5ab9d19b96b..9ac95c8770a 100644 --- a/compiler/testData/diagnostics/tests/inference/commonSystem/kt32818.kt +++ b/compiler/testData/diagnostics/tests/inference/commonSystem/kt32818.kt @@ -2,4 +2,4 @@ fun nullable(): T? = null -val value = nullable() ?: nullable() \ No newline at end of file +val value = nullable() ?: nullable() \ No newline at end of file