[NI] Dont' add trivial constraints with Nothing from incorporation
#KT-24490 Fixed #KT-26816 Fixed
This commit is contained in:
+16
-11
@@ -19,7 +19,10 @@ import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import java.util.*
|
||||
|
||||
// todo problem: intersection types in constrains: A <: Number, B <: Inv<A & Any> =>? B <: Inv<out Number & Any>
|
||||
class ConstraintIncorporator(val typeApproximator: TypeApproximator) {
|
||||
class ConstraintIncorporator(
|
||||
val typeApproximator: TypeApproximator,
|
||||
val trivialConstraintTypeInferenceOracle: TrivialConstraintTypeInferenceOracle
|
||||
) {
|
||||
|
||||
interface Context {
|
||||
val allTypeVariablesWithConstraints: Collection<VariableWithConstraints>
|
||||
@@ -99,9 +102,10 @@ class ConstraintIncorporator(val typeApproximator: TypeApproximator) {
|
||||
otherVariable: NewTypeVariable,
|
||||
otherConstraint: Constraint
|
||||
) {
|
||||
val baseConstraintType = baseConstraint.type
|
||||
val typeForApproximation = when (otherConstraint.kind) {
|
||||
ConstraintKind.EQUALITY -> {
|
||||
baseConstraint.type.substitute(otherVariable, otherConstraint.type)
|
||||
baseConstraintType.substituteTypeVariable(otherVariable, otherConstraint.type)
|
||||
}
|
||||
ConstraintKind.UPPER -> {
|
||||
val newCapturedTypeConstructor = NewCapturedTypeConstructor(
|
||||
@@ -113,7 +117,7 @@ class ConstraintIncorporator(val typeApproximator: TypeApproximator) {
|
||||
newCapturedTypeConstructor,
|
||||
lowerType = null
|
||||
)
|
||||
baseConstraint.type.substitute(otherVariable, temporaryCapturedType)
|
||||
baseConstraintType.substituteTypeVariable(otherVariable, temporaryCapturedType)
|
||||
}
|
||||
ConstraintKind.LOWER -> {
|
||||
val newCapturedTypeConstructor = NewCapturedTypeConstructor(
|
||||
@@ -125,23 +129,24 @@ class ConstraintIncorporator(val typeApproximator: TypeApproximator) {
|
||||
newCapturedTypeConstructor,
|
||||
lowerType = otherConstraint.type
|
||||
)
|
||||
baseConstraint.type.substitute(otherVariable, temporaryCapturedType)
|
||||
baseConstraintType.substituteTypeVariable(otherVariable, temporaryCapturedType)
|
||||
}
|
||||
}
|
||||
|
||||
if (baseConstraint.kind != ConstraintKind.UPPER) {
|
||||
c.addNewIncorporatedConstraint(approximateCapturedTypes(typeForApproximation, toSuper = false), targetVariable.defaultType)
|
||||
val generatedConstraintType = approximateCapturedTypes(typeForApproximation, toSuper = false)
|
||||
if (!trivialConstraintTypeInferenceOracle.isGeneratedConstraintTrivial(otherConstraint, generatedConstraintType)) {
|
||||
c.addNewIncorporatedConstraint(generatedConstraintType, targetVariable.defaultType)
|
||||
}
|
||||
}
|
||||
if (baseConstraint.kind != ConstraintKind.LOWER) {
|
||||
c.addNewIncorporatedConstraint(targetVariable.defaultType, approximateCapturedTypes(typeForApproximation, toSuper = true))
|
||||
val generatedConstraintType = approximateCapturedTypes(typeForApproximation, toSuper = true)
|
||||
if (!trivialConstraintTypeInferenceOracle.isGeneratedConstraintTrivial(otherConstraint, generatedConstraintType)) {
|
||||
c.addNewIncorporatedConstraint(targetVariable.defaultType, generatedConstraintType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun UnwrappedType.substitute(typeVariable: NewTypeVariable, value: UnwrappedType): UnwrappedType {
|
||||
val substitutor = NewTypeSubstitutorByConstructorMap(mapOf(typeVariable.freshTypeConstructor to value))
|
||||
return substitutor.safeSubstitute(this)
|
||||
}
|
||||
|
||||
private fun approximateCapturedTypes(type: UnwrappedType, toSuper: Boolean): UnwrappedType =
|
||||
if (toSuper) typeApproximator.approximateToSuperType(type, TypeApproximatorConfiguration.IncorporationConfiguration) ?: type
|
||||
else typeApproximator.approximateToSubType(type, TypeApproximatorConfiguration.IncorporationConfiguration) ?: type
|
||||
|
||||
+6
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.NewTypeVariable
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableFromCallableDescriptor
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.NewCapturedType
|
||||
@@ -164,4 +165,9 @@ class FreshVariableNewTypeSubstitutor(val freshVariables: List<TypeVariableFromC
|
||||
companion object {
|
||||
val Empty = FreshVariableNewTypeSubstitutor(emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
fun UnwrappedType.substituteTypeVariable(typeVariable: NewTypeVariable, value: UnwrappedType): UnwrappedType {
|
||||
val substitutor = NewTypeSubstitutorByConstructorMap(mapOf(typeVariable.freshTypeConstructor to value))
|
||||
return substitutor.safeSubstitute(this)
|
||||
}
|
||||
+27
-1
@@ -7,7 +7,9 @@ package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.ConstraintKind
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.NewTypeVariable
|
||||
import org.jetbrains.kotlin.types.UnwrappedType
|
||||
import org.jetbrains.kotlin.types.typeUtil.contains
|
||||
import org.jetbrains.kotlin.types.typeUtil.isNothing
|
||||
import org.jetbrains.kotlin.types.typeUtil.isNullableNothing
|
||||
|
||||
@@ -26,7 +28,31 @@ class TrivialConstraintTypeInferenceOracle {
|
||||
fun isSuitableResultedType(resultType: UnwrappedType): Boolean {
|
||||
return !resultType.isNothingOrNullableNothing()
|
||||
}
|
||||
|
||||
// It's possible to generate Nothing-like constraints inside incorporation mechanism:
|
||||
// For instance, when two type variables are in subtyping relation `T <: K`, after incorporation
|
||||
// there will be constraint `approximation(out K) <: K` => `Nothing <: K`, which is innocent
|
||||
// but can change result of the constraint system.
|
||||
// Therefore, here we avoid adding such trivial constraints to have stable constraint system
|
||||
fun isGeneratedConstraintTrivial(
|
||||
otherConstraint: Constraint,
|
||||
generatedConstraintType: UnwrappedType
|
||||
): Boolean {
|
||||
if (generatedConstraintType.isNothing()) return true
|
||||
|
||||
// If type that will be used to generate new constraint already contains `Nothing(?)`,
|
||||
// then we can't decide that resulting constraint will be useless
|
||||
if (otherConstraint.type.contains { it.isNothingOrNullableNothing() }) return false
|
||||
|
||||
// It's important to preserve constraints with nullable Nothing: `Nothing? <: T` (see implicitNothingConstraintFromReturn.kt test)
|
||||
if (generatedConstraintType.containsOnlyNonNullableNothing()) return true
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun UnwrappedType.isNothingOrNullableNothing(): Boolean =
|
||||
isNothing() || isNullableNothing()
|
||||
isNothing() || isNullableNothing()
|
||||
|
||||
private fun UnwrappedType.containsOnlyNonNullableNothing(): Boolean =
|
||||
contains { it.isNothing() } && !contains { it.isNullableNothing() }
|
||||
|
||||
Reference in New Issue
Block a user