[NI] Improvements for "not enough information" diagnostic
#KT-30590 Fixed
This commit is contained in:
+5
@@ -166,6 +166,11 @@ class ConstraintInjector(val constraintIncorporator: ConstraintIncorporator, val
|
||||
?: error("Should by type variableConstructor: $typeVariableConstructor. ${c.allTypeVariables.values}")
|
||||
|
||||
var targetType = type
|
||||
if (targetType.isUninferredParameter()) {
|
||||
// there already should be an error, so there is no point in reporting one more
|
||||
return
|
||||
}
|
||||
|
||||
if (targetType.isError()) {
|
||||
c.addError(ConstrainingTypeIsError(typeVariable, targetType, position))
|
||||
return
|
||||
|
||||
+53
-4
@@ -6,8 +6,10 @@
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||
|
||||
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
|
||||
import org.jetbrains.kotlin.resolve.calls.model.*
|
||||
import org.jetbrains.kotlin.types.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.TypeConstructor
|
||||
import org.jetbrains.kotlin.types.UnwrappedType
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
@@ -84,11 +86,11 @@ class KotlinConstraintSystemCompleter(
|
||||
if (variableForFixation.hasProperConstraint || completionMode == ConstraintSystemCompletionMode.FULL) {
|
||||
val variableWithConstraints = c.notFixedTypeVariables.getValue(variableForFixation.variable)
|
||||
|
||||
fixVariable(c, topLevelType, variableWithConstraints, postponedKtPrimitives)
|
||||
if (variableForFixation.hasProperConstraint)
|
||||
fixVariable(c, topLevelType, variableWithConstraints, postponedKtPrimitives)
|
||||
else
|
||||
processVariableWhenNotEnoughInformation(c, variableWithConstraints, topLevelAtoms)
|
||||
|
||||
if (!variableForFixation.hasProperConstraint) {
|
||||
c.addError(NotEnoughInformationForTypeParameter(variableWithConstraints.typeVariable, topLevelAtoms.first()))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -218,4 +220,51 @@ class KotlinConstraintSystemCompleter(
|
||||
val resultType = resultTypeResolver.findResultType(c, variableWithConstraints, direction)
|
||||
c.fixVariable(variableWithConstraints.typeVariable, resultType)
|
||||
}
|
||||
|
||||
private fun processVariableWhenNotEnoughInformation(
|
||||
c: Context,
|
||||
variableWithConstraints: VariableWithConstraints,
|
||||
topLevelAtoms: List<ResolvedAtom>
|
||||
) {
|
||||
val typeVariable = variableWithConstraints.typeVariable
|
||||
|
||||
val resolvedAtom = findResolvedAtomBy(typeVariable, topLevelAtoms) ?: topLevelAtoms.firstOrNull()
|
||||
if (resolvedAtom != null) {
|
||||
c.addError(NotEnoughInformationForTypeParameter(typeVariable, resolvedAtom))
|
||||
}
|
||||
|
||||
val resultErrorType = if (typeVariable is TypeVariableFromCallableDescriptor)
|
||||
ErrorUtils.createUninferredParameterType(typeVariable.originalTypeParameter)
|
||||
else
|
||||
ErrorUtils.createErrorType("Cannot infer type variable $typeVariable")
|
||||
|
||||
c.fixVariable(typeVariable, resultErrorType)
|
||||
}
|
||||
|
||||
private fun findResolvedAtomBy(typeVariable: TypeVariableMarker, topLevelAtoms: List<ResolvedAtom>): ResolvedAtom? {
|
||||
fun ResolvedAtom.check(): ResolvedAtom? {
|
||||
val suitableCall = when (this) {
|
||||
is ResolvedCallAtom -> typeVariable in substitutor.freshVariables
|
||||
is ResolvedCallableReferenceAtom -> candidate?.freshSubstitutor?.freshVariables?.let { typeVariable in it } ?: false
|
||||
is ResolvedLambdaAtom -> typeVariable == typeVariableForLambdaReturnType
|
||||
else -> false
|
||||
}
|
||||
|
||||
if (suitableCall) {
|
||||
return this
|
||||
}
|
||||
|
||||
subResolvedAtoms.forEach { subResolvedAtom ->
|
||||
subResolvedAtom.check()?.let { result -> return@check result }
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
for (topLevelAtom in topLevelAtoms) {
|
||||
topLevelAtom.check()?.let { return it }
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
+1
-4
@@ -18,10 +18,7 @@ package org.jetbrains.kotlin.resolve.calls.inference.components
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.components.TypeVariableDirectionCalculator.ResolveDirection
|
||||
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.VariableWithConstraints
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.checkConstraint
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.model.*
|
||||
import org.jetbrains.kotlin.resolve.constants.IntegerLiteralTypeConstructor
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
|
||||
+46
-14
@@ -5,12 +5,9 @@
|
||||
|
||||
package org.jetbrains.kotlin.resolve.calls.inference.model
|
||||
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.components.NewTypeSubstitutor
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.trimToSize
|
||||
import org.jetbrains.kotlin.resolve.calls.model.KotlinCallDiagnostic
|
||||
import org.jetbrains.kotlin.resolve.calls.tower.isSuccess
|
||||
import org.jetbrains.kotlin.types.TypeConstructor
|
||||
import org.jetbrains.kotlin.types.UnwrappedType
|
||||
import org.jetbrains.kotlin.types.model.KotlinTypeMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeConstructorMarker
|
||||
import org.jetbrains.kotlin.types.model.TypeVariableMarker
|
||||
@@ -38,16 +35,22 @@ class MutableVariableWithConstraints(
|
||||
fun addConstraint(constraint: Constraint): Constraint? {
|
||||
val previousConstraintWithSameType = constraints.filter { it.typeHashCode == constraint.typeHashCode && it.type == constraint.type }
|
||||
|
||||
if (previousConstraintWithSameType.any { newConstraintIsUseless(it.kind, constraint.kind) }) {
|
||||
if (previousConstraintWithSameType.any { previous -> newConstraintIsUseless(previous, constraint) })
|
||||
return null
|
||||
|
||||
val addAsEqualityConstraint = previousConstraintWithSameType.any { previous ->
|
||||
when (previous.kind) {
|
||||
ConstraintKind.LOWER -> constraint.kind.isUpper()
|
||||
ConstraintKind.UPPER -> constraint.kind.isLower()
|
||||
ConstraintKind.EQUALITY -> true
|
||||
}
|
||||
}
|
||||
|
||||
val actualConstraint = if (previousConstraintWithSameType.isNotEmpty()) {
|
||||
// i.e. previous is LOWER and new is UPPER or opposite situation
|
||||
val actualConstraint = if (addAsEqualityConstraint)
|
||||
Constraint(ConstraintKind.EQUALITY, constraint.type, constraint.position, constraint.typeHashCode)
|
||||
} else {
|
||||
else
|
||||
constraint
|
||||
}
|
||||
|
||||
mutableConstraints.add(actualConstraint)
|
||||
simplifiedConstraints = null
|
||||
return actualConstraint
|
||||
@@ -66,18 +69,47 @@ class MutableVariableWithConstraints(
|
||||
simplifiedConstraints = null
|
||||
}
|
||||
|
||||
private fun newConstraintIsUseless(oldKind: ConstraintKind, newKind: ConstraintKind) =
|
||||
when (oldKind) {
|
||||
private fun newConstraintIsUseless(old: Constraint, new: Constraint): Boolean {
|
||||
// Constraints from declared upper bound are quite special -- they aren't considered as a proper ones
|
||||
// In other words, user-defined constraints have "higher" priority and here we're trying not to loose them
|
||||
if (old.position.from is DeclaredUpperBoundConstraintPosition && new.position.from !is DeclaredUpperBoundConstraintPosition)
|
||||
return false
|
||||
|
||||
return when (old.kind) {
|
||||
ConstraintKind.EQUALITY -> true
|
||||
ConstraintKind.LOWER -> newKind == ConstraintKind.LOWER
|
||||
ConstraintKind.UPPER -> newKind == ConstraintKind.UPPER
|
||||
ConstraintKind.LOWER -> new.kind.isLower()
|
||||
ConstraintKind.UPPER -> new.kind.isUpper()
|
||||
}
|
||||
}
|
||||
|
||||
private fun simplifyConstraints(): List<Constraint> {
|
||||
val equalityConstraints = mutableConstraints
|
||||
val distinctConstraints = removeDuplicatesFromDeclaredUpperBoundConstraints(mutableConstraints)
|
||||
|
||||
val equalityConstraints = distinctConstraints
|
||||
.filter { it.kind == ConstraintKind.EQUALITY }
|
||||
.groupBy { it.typeHashCode }
|
||||
return mutableConstraints.filter { isUsefulConstraint(it, equalityConstraints) }
|
||||
return distinctConstraints.filter { isUsefulConstraint(it, equalityConstraints) }
|
||||
}
|
||||
|
||||
private fun removeDuplicatesFromDeclaredUpperBoundConstraints(constraints: List<Constraint>): MutableList<Constraint> {
|
||||
val currentConstraints = constraints.toMutableList()
|
||||
val iterator = currentConstraints.iterator()
|
||||
while (iterator.hasNext()) {
|
||||
val potentialDuplicate = iterator.next()
|
||||
|
||||
if (potentialDuplicate.position.from !is DeclaredUpperBoundConstraintPosition) continue
|
||||
val hasDuplicate = currentConstraints.any { other ->
|
||||
potentialDuplicate !== other &&
|
||||
potentialDuplicate.typeHashCode == other.typeHashCode &&
|
||||
potentialDuplicate.type == other.type &&
|
||||
potentialDuplicate.kind == other.kind
|
||||
}
|
||||
|
||||
if (hasDuplicate)
|
||||
iterator.remove()
|
||||
}
|
||||
|
||||
return currentConstraints
|
||||
}
|
||||
|
||||
private fun isUsefulConstraint(constraint: Constraint, equalityConstraints: Map<Int, List<Constraint>>): Boolean {
|
||||
|
||||
Reference in New Issue
Block a user