[NI] Remove Nothing result type restriction in most cases
Make Nothing as result type not suitable only for if, when, try and ?: special functions.
This commit is contained in:
+5
-1
@@ -12,12 +12,12 @@ import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.components.ConstraintInjector
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.NewTypeVariable
|
||||
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||
import org.jetbrains.kotlin.resolve.calls.results.SimpleConstraintSystem
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.ImplicitScopeTower
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValueWithSmartCastInfo
|
||||
import org.jetbrains.kotlin.types.StubType
|
||||
import org.jetbrains.kotlin.types.UnwrappedType
|
||||
|
||||
@@ -40,6 +40,10 @@ interface KotlinResolutionStatelessCallbacks {
|
||||
fun createConstraintSystemForOverloadResolution(
|
||||
constraintInjector: ConstraintInjector, builtIns: KotlinBuiltIns
|
||||
): SimpleConstraintSystem
|
||||
|
||||
fun isSpecialFunctionTypeParameterName(name: Name): Boolean
|
||||
|
||||
fun isExclExclTypeParameterName(name: Name): Boolean
|
||||
}
|
||||
|
||||
// This components hold state (trace). Work with this carefully.
|
||||
|
||||
+3
-1
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionStatelessCallbacks
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.NotEnoughInformationForTypeParameter
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.TypeVariableFromCallableDescriptor
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
|
||||
@@ -21,7 +22,8 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
class KotlinConstraintSystemCompleter(
|
||||
private val resultTypeResolver: ResultTypeResolver,
|
||||
private val variableFixationFinder: VariableFixationFinder
|
||||
private val variableFixationFinder: VariableFixationFinder,
|
||||
private val statelessCallbacks: KotlinResolutionStatelessCallbacks
|
||||
) {
|
||||
enum class ConstraintSystemCompletionMode {
|
||||
FULL,
|
||||
|
||||
+36
-24
@@ -17,17 +17,21 @@
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator
|
||||
import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionStatelessCallbacks
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.components.TypeVariableDirectionCalculator.ResolveDirection
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.*
|
||||
import org.jetbrains.kotlin.types.AbstractTypeApproximator
|
||||
import org.jetbrains.kotlin.types.TypeApproximatorConfiguration
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeVariableMarker
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeSystemInferenceExtensionContext
|
||||
|
||||
class ResultTypeResolver(
|
||||
val typeApproximator: AbstractTypeApproximator,
|
||||
val trivialConstraintTypeInferenceOracle: TrivialConstraintTypeInferenceOracle
|
||||
val trivialConstraintTypeInferenceOracle: TrivialConstraintTypeInferenceOracle,
|
||||
private val statelessCallbacks: KotlinResolutionStatelessCallbacks? // TODO injection in FIR
|
||||
) {
|
||||
interface Context : TypeSystemInferenceExtensionContext {
|
||||
fun isProperType(type: KotlinTypeMarker): Boolean
|
||||
@@ -43,18 +47,20 @@ class ResultTypeResolver(
|
||||
}
|
||||
}
|
||||
|
||||
fun findResultTypeOrNull(c: Context, variableWithConstraints: VariableWithConstraints, direction: ResolveDirection): KotlinTypeMarker? {
|
||||
private fun findResultTypeOrNull(
|
||||
c: Context,
|
||||
variableWithConstraints: VariableWithConstraints,
|
||||
direction: ResolveDirection
|
||||
): KotlinTypeMarker? {
|
||||
findResultIfThereIsEqualsConstraint(c, variableWithConstraints)?.let { return it }
|
||||
|
||||
val subType = c.findSubType(variableWithConstraints)
|
||||
val superType = c.findSuperType(variableWithConstraints)
|
||||
val result = if (direction == ResolveDirection.TO_SUBTYPE || direction == ResolveDirection.UNKNOWN) {
|
||||
return if (direction == ResolveDirection.TO_SUBTYPE || direction == ResolveDirection.UNKNOWN) {
|
||||
c.resultType(subType, superType, variableWithConstraints)
|
||||
} else {
|
||||
c.resultType(superType, subType, variableWithConstraints)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
private fun Context.resultType(
|
||||
@@ -66,10 +72,10 @@ class ResultTypeResolver(
|
||||
|
||||
if (isSuitableType(firstCandidate, variableWithConstraints)) return firstCandidate
|
||||
|
||||
if (isSuitableType(secondCandidate, variableWithConstraints)) {
|
||||
return secondCandidate
|
||||
return if (isSuitableType(secondCandidate, variableWithConstraints)) {
|
||||
secondCandidate
|
||||
} else {
|
||||
return firstCandidate
|
||||
firstCandidate
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,20 +84,21 @@ class ResultTypeResolver(
|
||||
if (!isProperType(constraint.type)) continue
|
||||
if (!checkConstraint(this, constraint.type, constraint.kind, resultType)) return false
|
||||
}
|
||||
|
||||
/*
|
||||
* If all upper constraints came from declared upper bounds we should accept Nothing
|
||||
* as suitable type as OI does
|
||||
*/
|
||||
if (
|
||||
variableWithConstraints.constraints.any {
|
||||
it.kind == ConstraintKind.UPPER && it.position.from !is DeclaredUpperBoundConstraintPosition
|
||||
} && !trivialConstraintTypeInferenceOracle.isSuitableResultedType(resultType)
|
||||
!trivialConstraintTypeInferenceOracle.isSuitableResultedType(resultType)
|
||||
&& isNothingNotSuitableFor(variableWithConstraints.typeVariable)
|
||||
) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun isNothingNotSuitableFor(variable: TypeVariableMarker): Boolean {
|
||||
val parameterName = variable.safeAs<TypeVariableFromCallableDescriptor>()?.originalTypeParameter?.name ?: return false
|
||||
val isSpecialFunctionParameter = statelessCallbacks?.isSpecialFunctionTypeParameterName(parameterName) ?: false
|
||||
val isBangBangParameter = isSpecialFunctionParameter && statelessCallbacks?.isExclExclTypeParameterName(parameterName) ?: false
|
||||
return isSpecialFunctionParameter && !isBangBangParameter
|
||||
}
|
||||
|
||||
private fun Context.findSubType(variableWithConstraints: VariableWithConstraints): KotlinTypeMarker? {
|
||||
val lowerConstraintTypes = prepareLowerConstraints(variableWithConstraints.constraints)
|
||||
|
||||
@@ -163,22 +170,27 @@ class ResultTypeResolver(
|
||||
}
|
||||
|
||||
private fun Context.findSuperType(variableWithConstraints: VariableWithConstraints): KotlinTypeMarker? {
|
||||
val upperConstraints = variableWithConstraints.constraints.filter { it.kind == ConstraintKind.UPPER && this@findSuperType.isProperType(it.type) }
|
||||
val upperConstraints =
|
||||
variableWithConstraints.constraints.filter { it.kind == ConstraintKind.UPPER && this@findSuperType.isProperType(it.type) }
|
||||
if (upperConstraints.isNotEmpty()) {
|
||||
val upperType = intersectTypes(upperConstraints.map { it.type })
|
||||
|
||||
return typeApproximator.approximateToSubType(upperType, TypeApproximatorConfiguration.CapturedAndIntegerLiteralsTypesApproximation) ?: upperType
|
||||
return typeApproximator.approximateToSubType(
|
||||
upperType,
|
||||
TypeApproximatorConfiguration.CapturedAndIntegerLiteralsTypesApproximation
|
||||
) ?: upperType
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun findResultIfThereIsEqualsConstraint(c: Context, variableWithConstraints: VariableWithConstraints): KotlinTypeMarker? = with(c) {
|
||||
val properEqualityConstraints = variableWithConstraints.constraints.filter {
|
||||
it.kind == ConstraintKind.EQUALITY && c.isProperType(it.type)
|
||||
}
|
||||
private fun findResultIfThereIsEqualsConstraint(c: Context, variableWithConstraints: VariableWithConstraints): KotlinTypeMarker? =
|
||||
with(c) {
|
||||
val properEqualityConstraints = variableWithConstraints.constraints.filter {
|
||||
it.kind == ConstraintKind.EQUALITY && c.isProperType(it.type)
|
||||
}
|
||||
|
||||
return c.representativeFromEqualityConstraints(properEqualityConstraints)
|
||||
}
|
||||
return c.representativeFromEqualityConstraints(properEqualityConstraints)
|
||||
}
|
||||
|
||||
// Discriminate integer literal types as they are less specific than separate integer types (Int, Short...)
|
||||
private fun Context.representativeFromEqualityConstraints(constraints: List<Constraint>): KotlinTypeMarker? {
|
||||
|
||||
+9
@@ -23,6 +23,15 @@ class TrivialConstraintTypeInferenceOracle private constructor(context: TypeSyst
|
||||
return constraint.kind == ConstraintKind.LOWER && constraint.type.typeConstructor().isNothingConstructor()
|
||||
}
|
||||
|
||||
// This function controls the choice between sub and super result type
|
||||
// Even that Nothing(?) is the most specific type for subtype, it doesn't bring valuable information to the user,
|
||||
// therefore it is discriminated in favor of supertype
|
||||
fun isSuitableResultedType(
|
||||
resultType: KotlinTypeMarker
|
||||
): Boolean {
|
||||
return !resultType.typeConstructor().isNothingConstructor()
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
Reference in New Issue
Block a user