[NI] Soften restictions on using Nothing as proper constraint for full call completion

Consider lower `Nothing` constraint non-proper only if there is a dependant not analyzed postponed atom.
Early completion to `Nothing` provides data flow info for smart casts.

KT-35668 Fixed
This commit is contained in:
Pavel Kirpichenkov
2020-01-22 15:46:33 +03:00
parent f1d9177112
commit 78c9bbcc0d
13 changed files with 234 additions and 24 deletions
@@ -11,6 +11,7 @@ import org.jetbrains.kotlin.resolve.calls.inference.components.TrivialConstraint
import org.jetbrains.kotlin.resolve.calls.inference.model.Constraint
import org.jetbrains.kotlin.resolve.calls.inference.model.VariableWithConstraints
import org.jetbrains.kotlin.resolve.calls.model.KotlinResolutionCandidate
import org.jetbrains.kotlin.resolve.calls.model.PostponedResolvedAtom
import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.model.*
@@ -39,11 +40,14 @@ class CompletionModeCalculator {
if (csBuilder.isProperType(returnType)) return ConstraintSystemCompletionMode.FULL
// For nested call with variables in return type check possibility of full completion
return CalculatorForNestedCall(returnType, csCompleterContext, trivialConstraintTypeInferenceOracle).computeCompletionMode()
return CalculatorForNestedCall(
candidate, returnType, csCompleterContext, trivialConstraintTypeInferenceOracle
).computeCompletionMode()
}
}
private class CalculatorForNestedCall(
private val candidate: KotlinResolutionCandidate,
private val returnType: UnwrappedType?,
private val csCompleterContext: CsCompleterContext,
private val trivialConstraintTypeInferenceOracle: TrivialConstraintTypeInferenceOracle
@@ -57,6 +61,10 @@ class CompletionModeCalculator {
private val variablesWithQueuedConstraints = mutableSetOf<TypeVariableMarker>()
private val typesToProcess: Queue<KotlinTypeMarker> = ArrayDeque()
private val postponedAtoms: List<PostponedResolvedAtom> by lazy {
KotlinConstraintSystemCompleter.getOrderedNotAnalyzedPostponedArguments(listOf(candidate.resolvedCall))
}
fun computeCompletionMode(): ConstraintSystemCompletionMode = with(csCompleterContext) {
// Add fixation directions for variables based on effective variance in type
typesToProcess.add(returnType)
@@ -163,13 +171,14 @@ class CompletionModeCalculator {
direction: FixationDirection
): Boolean {
val constraints = variableWithConstraints.constraints
val variable = variableWithConstraints.typeVariable
// todo check correctness for @Exact
return constraints.isNotEmpty() && constraints.any { constraint ->
constraint.hasRequiredKind(direction)
&& !constraint.type.typeConstructor().isIntegerLiteralTypeConstructor()
&& isProperType(constraint.type)
&& trivialConstraintTypeInferenceOracle.isSuitableResultedType(constraint.type)
&& !constraint.type.typeConstructor().isIntegerLiteralTypeConstructor()
&& !isNothingConstraintForPartiallyAnalyzedVariable(constraint, variable)
}
}
@@ -177,5 +186,16 @@ class CompletionModeCalculator {
FixationDirection.TO_SUBTYPE -> kind.isLower() || kind.isEqual()
FixationDirection.EQUALITY -> kind.isEqual()
}
private fun CsCompleterContext.isNothingConstraintForPartiallyAnalyzedVariable(
constraint: Constraint,
variable: TypeVariableMarker
): Boolean {
if (trivialConstraintTypeInferenceOracle.isSuitableResultedType(constraint.type) || !constraint.kind.isLower())
return false
return postponedAtoms.any { atom ->
atom.expectedType?.contains { type -> variable.defaultType() == type } ?: false
}
}
}
}
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.resolve.calls.inference.components
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalType
import org.jetbrains.kotlin.builtins.isBuiltinFunctionalTypeOrSubtype
import org.jetbrains.kotlin.resolve.calls.components.KotlinResolutionStatelessCallbacks
import org.jetbrains.kotlin.resolve.calls.components.transformToResolvedLambda
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
import org.jetbrains.kotlin.resolve.calls.inference.model.*
@@ -23,7 +22,6 @@ import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class KotlinConstraintSystemCompleter(
private val resultTypeResolver: ResultTypeResolver,
val variableFixationFinder: VariableFixationFinder,
private val statelessCallbacks: KotlinResolutionStatelessCallbacks
) {
enum class ConstraintSystemCompletionMode {
FULL,
@@ -214,23 +212,6 @@ class KotlinConstraintSystemCompleter(
return false
}
private fun getOrderedNotAnalyzedPostponedArguments(topLevelAtoms: List<ResolvedAtom>): List<PostponedResolvedAtom> {
fun ResolvedAtom.process(to: MutableList<PostponedResolvedAtom>) {
to.addIfNotNull(this.safeAs<PostponedResolvedAtom>()?.takeUnless { it.analyzed })
if (analyzed) {
subResolvedAtoms?.forEach { it.process(to) }
}
}
val notAnalyzedArguments = arrayListOf<PostponedResolvedAtom>()
for (primitive in topLevelAtoms) {
primitive.process(notAnalyzedArguments)
}
return notAnalyzedArguments
}
private fun getOrderedAllTypeVariables(
c: Context,
collectVariablesFromContext: Boolean,
@@ -352,4 +333,23 @@ class KotlinConstraintSystemCompleter(
return null
}
companion object {
fun getOrderedNotAnalyzedPostponedArguments(topLevelAtoms: List<ResolvedAtom>): List<PostponedResolvedAtom> {
fun ResolvedAtom.process(to: MutableList<PostponedResolvedAtom>) {
to.addIfNotNull(this.safeAs<PostponedResolvedAtom>()?.takeUnless { it.analyzed })
if (analyzed) {
subResolvedAtoms?.forEach { it.process(to) }
}
}
val notAnalyzedArguments = arrayListOf<PostponedResolvedAtom>()
for (primitive in topLevelAtoms) {
primitive.process(notAnalyzedArguments)
}
return notAnalyzedArguments
}
}
}