K2: Avoid using Nothing? as inference result in the majority of cases
Namely, do not choose `Nothing?` result type when fixing a variable that has other constraints besides the ones that came from the relevant type parameter's upper bounds. See more details in KT-55691. In K1, the case from specialCallWithMaterializeAndExpectedType.kt was working (inferred to String?) just because the branches were analyzed independently with `String?` expected type. This change became necessary after the previous commit when we united inference subsystems for if/when branches (see motivation there). NB: For K1, the behavior is left the same, but the code was refactored a bit. ^KT-55691 Fixed ^KT-56448 Fixed
This commit is contained in:
committed by
Space Team
parent
f12a4e08cf
commit
2bafcddf7a
+30
-6
@@ -146,15 +146,39 @@ class ResultTypeResolver(
|
||||
for (constraint in filteredConstraints) {
|
||||
if (!checkConstraint(this, constraint.type, constraint.kind, resultType)) return false
|
||||
}
|
||||
if (!trivialConstraintTypeInferenceOracle.isSuitableResultedType(resultType)) {
|
||||
if (resultType.isNullableType() && checkSingleLowerNullabilityConstraint(filteredConstraints)) return false
|
||||
if (isReified(variableWithConstraints.typeVariable)) return false
|
||||
}
|
||||
|
||||
return true
|
||||
// if resultType is not Nothing
|
||||
if (trivialConstraintTypeInferenceOracle.isSuitableResultedType(resultType)) return true
|
||||
|
||||
// Nothing and Nothing? is not allowed for reified parameters
|
||||
if (isReified(variableWithConstraints.typeVariable)) return false
|
||||
|
||||
// It's ok to fix result to non-nullable Nothing and parameter is not reified
|
||||
if (!resultType.isNullableType()) return true
|
||||
|
||||
return isNullableNothingMayBeConsideredAsSuitableResultType(filteredConstraints)
|
||||
}
|
||||
|
||||
private fun checkSingleLowerNullabilityConstraint(constraints: List<Constraint>): Boolean {
|
||||
private fun Context.isNullableNothingMayBeConsideredAsSuitableResultType(constraints: List<Constraint>): Boolean = when {
|
||||
isK2 ->
|
||||
// There might be an assertion for green code that if `allUpperConstraintsAreFromBounds(constraints) == true` then
|
||||
// the single `Nothing?` lower bound constraint has Constraint::isNullabilityConstraint is set to false
|
||||
// because otherwise we would not start fixing the variable since it has no proper constraints.
|
||||
allUpperConstraintsAreFromBounds(constraints)
|
||||
else -> !isThereSingleLowerNullabilityConstraint(constraints)
|
||||
}
|
||||
|
||||
private fun allUpperConstraintsAreFromBounds(constraints: List<Constraint>): Boolean =
|
||||
constraints.all {
|
||||
// Actually, at least for green code that should be an assertion that lower constraints (!isUpper) has `Nothing?` type
|
||||
// Because otherwise if we had `Nothing? <: T` and `SomethingElse <: T` than it would end with `SomethingElse? <: T`
|
||||
!it.kind.isUpper() || isFromTypeParameterUpperBound(it)
|
||||
}
|
||||
|
||||
private fun isFromTypeParameterUpperBound(constraint: Constraint): Boolean =
|
||||
constraint.position.isFromDeclaredUpperBound || constraint.position.from is DeclaredUpperBoundConstraintPosition<*>
|
||||
|
||||
private fun isThereSingleLowerNullabilityConstraint(constraints: List<Constraint>): Boolean {
|
||||
return constraints.singleOrNull { it.kind.isLower() }?.isNullabilityConstraint ?: false
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -86,6 +86,10 @@ class Constraint(
|
||||
val position: IncorporationConstraintPosition,
|
||||
val typeHashCode: Int = type.hashCode(),
|
||||
val derivedFrom: Set<TypeVariableMarker>,
|
||||
// This value is true for constraints of the form `Nothing? <: Tv`
|
||||
// that have been created during incorporation phase of the constraint of the form `Kv? <: Tv` (where `Kv` another type variable).
|
||||
// The main idea behind that parameter is that we don't consider such constraints as proper (signifying that variable is ready for completion).
|
||||
// And also, there is additional logic in K1 that doesn't allow to fix variable into `Nothing?` if we had only that kind of lower constraints
|
||||
val isNullabilityConstraint: Boolean,
|
||||
val inputTypePositionBeforeIncorporation: OnlyInputTypeConstraintPosition? = null
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user