[NI] Support fixation to subtype in completion mode calculator

Allow full compltion mode for return type depending on type parameters in
contravariant position only if they have proper equality constraint.
This commit is contained in:
Pavel Kirpichenkov
2019-12-19 13:56:53 +03:00
parent e69e45e2a6
commit 384bd858e9
12 changed files with 178 additions and 13 deletions
@@ -10379,6 +10379,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte
runTest("compiler/testData/diagnostics/tests/inference/completion/basic.kt"); runTest("compiler/testData/diagnostics/tests/inference/completion/basic.kt");
} }
@TestMetadata("equalityConstraintUpstairs.kt")
public void testEqualityConstraintUpstairs() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/equalityConstraintUpstairs.kt");
}
@TestMetadata("kt33166.kt") @TestMetadata("kt33166.kt")
public void testKt33166() throws Exception { public void testKt33166() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/kt33166.kt"); runTest("compiler/testData/diagnostics/tests/inference/completion/kt33166.kt");
@@ -10389,6 +10394,11 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirOldFronte
runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt"); runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt");
} }
@TestMetadata("transitiveConstraint.kt")
public void testTransitiveConstraint() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/transitiveConstraint.kt");
}
@TestMetadata("withExact.kt") @TestMetadata("withExact.kt")
public void testWithExact() throws Exception { public void testWithExact() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/withExact.kt"); runTest("compiler/testData/diagnostics/tests/inference/completion/withExact.kt");
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.resolve.calls.model.KotlinResolutionCandidate
import org.jetbrains.kotlin.types.AbstractTypeChecker import org.jetbrains.kotlin.types.AbstractTypeChecker
import org.jetbrains.kotlin.types.UnwrappedType import org.jetbrains.kotlin.types.UnwrappedType
import org.jetbrains.kotlin.types.model.* import org.jetbrains.kotlin.types.model.*
import org.jetbrains.kotlin.utils.newLinkedHashMapWithExpectedSize
import java.util.* import java.util.*
typealias CsCompleterContext = KotlinConstraintSystemCompleter.Context typealias CsCompleterContext = KotlinConstraintSystemCompleter.Context
@@ -45,10 +46,11 @@ class CompletionModeCalculator {
private val csCompleterContext: CsCompleterContext private val csCompleterContext: CsCompleterContext
) { ) {
private enum class FixationDirection { private enum class FixationDirection {
TO_SUBTYPE, TO_SUPERTYPE, EQUALITY TO_SUBTYPE, EQUALITY
} }
private val fixationDirectionsForVariables = mutableMapOf<VariableWithConstraints, FixationDirection>() private val fixationDirectionsForVariables: MutableMap<VariableWithConstraints, FixationDirection> =
newLinkedHashMapWithExpectedSize(csCompleterContext.notFixedTypeVariables.size)
private val variablesWithQueuedConstraints = mutableSetOf<TypeVariableMarker>() private val variablesWithQueuedConstraints = mutableSetOf<TypeVariableMarker>()
private val typesToProcess: Queue<KotlinTypeMarker> = ArrayDeque() private val typesToProcess: Queue<KotlinTypeMarker> = ArrayDeque()
@@ -68,14 +70,14 @@ class CompletionModeCalculator {
while (typesToProcess.isNotEmpty()) { while (typesToProcess.isNotEmpty()) {
val type = typesToProcess.poll() ?: break val type = typesToProcess.poll() ?: break
fixationRequirementForTopLevel(type)?.let { directionForVariable -> fixationDirectionForTopLevel(type)?.let { directionForVariable ->
updateDirection(directionForVariable) updateDirection(directionForVariable)
enqueueTypesFromConstraints(directionForVariable.variable) enqueueTypesFromConstraints(directionForVariable.variable)
} }
// find all variables in type and make requirements for them // find all variables in type and make requirements for them
type.contains { fromReturnType -> type.contains { typePart ->
for (directionForVariable in directionsForVariablesInTypeArguments(fromReturnType)) { for (directionForVariable in directionsForVariablesInTypeArguments(typePart)) {
updateDirection(directionForVariable) updateDirection(directionForVariable)
enqueueTypesFromConstraints(directionForVariable.variable) enqueueTypesFromConstraints(directionForVariable.variable)
} }
@@ -106,16 +108,16 @@ class CompletionModeCalculator {
private fun updateDirection(directionForVariable: FixationDirectionForVariable) { private fun updateDirection(directionForVariable: FixationDirectionForVariable) {
val (variable, newDirection) = directionForVariable val (variable, newDirection) = directionForVariable
fixationDirectionsForVariables[variable]?.let { oldDirection -> fixationDirectionsForVariables[variable]?.let { oldDirection ->
// To sub and to super are merged into equality, old equality stays
if (oldDirection != FixationDirection.EQUALITY && oldDirection != newDirection) if (oldDirection != FixationDirection.EQUALITY && oldDirection != newDirection)
fixationDirectionsForVariables[variable] = FixationDirection.EQUALITY fixationDirectionsForVariables[variable] = FixationDirection.EQUALITY
} ?: run {
fixationDirectionsForVariables[variable] = newDirection
} }
fixationDirectionsForVariables[variable] = newDirection
} }
private data class FixationDirectionForVariable(val variable: VariableWithConstraints, val direction: FixationDirection) private data class FixationDirectionForVariable(val variable: VariableWithConstraints, val direction: FixationDirection)
private fun CsCompleterContext.fixationRequirementForTopLevel(type: KotlinTypeMarker): FixationDirectionForVariable? { private fun CsCompleterContext.fixationDirectionForTopLevel(type: KotlinTypeMarker): FixationDirectionForVariable? {
return notFixedTypeVariables[type.typeConstructor()]?.let { return notFixedTypeVariables[type.typeConstructor()]?.let {
FixationDirectionForVariable(it, FixationDirection.TO_SUBTYPE) FixationDirectionForVariable(it, FixationDirection.TO_SUBTYPE)
} }
@@ -138,10 +140,10 @@ class CompletionModeCalculator {
val parameter = type.typeConstructor().getParameter(position) val parameter = type.typeConstructor().getParameter(position)
val effectiveVariance = AbstractTypeChecker.effectiveVariance(parameter.getVariance(), argument.getVariance()) val effectiveVariance = AbstractTypeChecker.effectiveVariance(parameter.getVariance(), argument.getVariance())
?: TypeVariance.OUT // Discuss ?: TypeVariance.INV
val direction = when (effectiveVariance) { val direction = when (effectiveVariance) {
TypeVariance.IN -> FixationDirection.TO_SUPERTYPE TypeVariance.IN -> FixationDirection.EQUALITY // Assuming that variables in contravariant positions are fixed to subtype
TypeVariance.OUT -> FixationDirection.TO_SUBTYPE TypeVariance.OUT -> FixationDirection.TO_SUBTYPE
TypeVariance.INV -> FixationDirection.EQUALITY TypeVariance.INV -> FixationDirection.EQUALITY
} }
@@ -169,7 +171,6 @@ class CompletionModeCalculator {
private fun Constraint.hasRequiredKind(direction: FixationDirection) = when (direction) { private fun Constraint.hasRequiredKind(direction: FixationDirection) = when (direction) {
FixationDirection.TO_SUBTYPE -> kind.isLower() || kind.isEqual() FixationDirection.TO_SUBTYPE -> kind.isLower() || kind.isEqual()
FixationDirection.TO_SUPERTYPE -> kind.isUpper() || kind.isEqual()
FixationDirection.EQUALITY -> kind.isEqual() FixationDirection.EQUALITY -> kind.isEqual()
} }
} }
@@ -57,7 +57,6 @@ fun test7(cls: Cls) {
} }
fun test8(cls: Cls) { fun test8(cls: Cls) {
// TODO
id( id(
wrapIn(cls) wrapIn(cls)
) )
@@ -0,0 +1,17 @@
// !LANGUAGE: +NewInference
// !DIAGNOSTICS: -UNUSED_PARAMETER
class In<in I>(arg: I)
class Out<out O>(val prop: O)
class Inv<T>(val prop: T)
interface Upper
class Lower : Upper
fun <K> id(arg: K): K = arg
fun test(lower: Lower) {
id<Inv<Upper>>(Inv(lower))
id<In<Upper>>(In(lower))
id<Out<Upper>>(Out(lower))
}
@@ -0,0 +1,17 @@
// !LANGUAGE: +NewInference
// !DIAGNOSTICS: -UNUSED_PARAMETER
class In<in I>(arg: I)
class Out<out O>(val prop: O)
class Inv<T>(val prop: T)
interface Upper
class Lower : Upper
fun <K> id(arg: K): K = arg
fun test(lower: Lower) {
id<Inv<Upper>>(Inv(lower))
id<In<Upper>>(In(lower))
id<Out<Upper>>(Out(lower))
}
@@ -0,0 +1,40 @@
package
public fun </*0*/ K> id(/*0*/ arg: K): K
public fun test(/*0*/ lower: Lower): kotlin.Unit
public final class In</*0*/ in I> {
public constructor In</*0*/ in I>(/*0*/ arg: I)
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class Inv</*0*/ T> {
public constructor Inv</*0*/ T>(/*0*/ prop: T)
public final val prop: T
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class Lower : Upper {
public constructor Lower()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class Out</*0*/ out O> {
public constructor Out</*0*/ out O>(/*0*/ prop: O)
public final val prop: O
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface Upper {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,15 @@
// !LANGUAGE: +NewInference
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE
interface Bound
interface Upper : Bound
class Lower : Upper
class Inv<T>
fun <T : Bound, U : T> makeInv(v: U): Inv<T> = TODO()
fun <K> id(arg: K): K = arg
fun test(lower: Lower) {
id<Inv<Upper>>(makeInv(lower))
}
@@ -0,0 +1,15 @@
// !LANGUAGE: +NewInference
// !DIAGNOSTICS: -UNUSED_PARAMETER -UNUSED_VARIABLE
interface Bound
interface Upper : Bound
class Lower : Upper
class Inv<T>
fun <T : Bound, U : T> makeInv(v: U): Inv<T> = TODO()
fun <K> id(arg: K): K = arg
fun test(lower: Lower) {
id<Inv<Upper>>(makeInv(lower))
}
@@ -0,0 +1,31 @@
package
public fun </*0*/ K> id(/*0*/ arg: K): K
public fun </*0*/ T : Bound, /*1*/ U : T> makeInv(/*0*/ v: U): Inv<T>
public fun test(/*0*/ lower: Lower): kotlin.Unit
public interface Bound {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class Inv</*0*/ T> {
public constructor Inv</*0*/ T>()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final class Lower : Upper {
public constructor Lower()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public interface Upper : Bound {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -45,7 +45,7 @@ fun test1(int: Int, any: Any) {
readFromMyList(getMyList(any), int) readFromMyList(getMyList(any), int)
readFromMyList<Int>(<!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, OI;TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>getMyList(any)<!>, int) readFromMyList<Int>(<!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, OI;TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>getMyList(any)<!>, int)
readFromMyList<Int>(<!NI;TYPE_MISMATCH, NI;TYPE_MISMATCH, OI;TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>getMyListToReadFrom(any)<!>, int) readFromMyList<Int>(<!NI;TYPE_MISMATCH, OI;TYPE_INFERENCE_EXPECTED_TYPE_MISMATCH!>getMyListToReadFrom(any)<!>, int)
readFromMyList(getMyListToReadFrom(any), int) readFromMyList(getMyListToReadFrom(any), int)
readFromMyList(getMyListToReadFrom(int), any) readFromMyList(getMyListToReadFrom(int), any)
@@ -10386,6 +10386,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
runTest("compiler/testData/diagnostics/tests/inference/completion/basic.kt"); runTest("compiler/testData/diagnostics/tests/inference/completion/basic.kt");
} }
@TestMetadata("equalityConstraintUpstairs.kt")
public void testEqualityConstraintUpstairs() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/equalityConstraintUpstairs.kt");
}
@TestMetadata("kt33166.kt") @TestMetadata("kt33166.kt")
public void testKt33166() throws Exception { public void testKt33166() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/kt33166.kt"); runTest("compiler/testData/diagnostics/tests/inference/completion/kt33166.kt");
@@ -10396,6 +10401,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt"); runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt");
} }
@TestMetadata("transitiveConstraint.kt")
public void testTransitiveConstraint() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/transitiveConstraint.kt");
}
@TestMetadata("withExact.kt") @TestMetadata("withExact.kt")
public void testWithExact() throws Exception { public void testWithExact() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/withExact.kt"); runTest("compiler/testData/diagnostics/tests/inference/completion/withExact.kt");
@@ -10381,6 +10381,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
runTest("compiler/testData/diagnostics/tests/inference/completion/basic.kt"); runTest("compiler/testData/diagnostics/tests/inference/completion/basic.kt");
} }
@TestMetadata("equalityConstraintUpstairs.kt")
public void testEqualityConstraintUpstairs() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/equalityConstraintUpstairs.kt");
}
@TestMetadata("kt33166.kt") @TestMetadata("kt33166.kt")
public void testKt33166() throws Exception { public void testKt33166() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/kt33166.kt"); runTest("compiler/testData/diagnostics/tests/inference/completion/kt33166.kt");
@@ -10391,6 +10396,11 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt"); runTest("compiler/testData/diagnostics/tests/inference/completion/partialForIlt.kt");
} }
@TestMetadata("transitiveConstraint.kt")
public void testTransitiveConstraint() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/transitiveConstraint.kt");
}
@TestMetadata("withExact.kt") @TestMetadata("withExact.kt")
public void testWithExact() throws Exception { public void testWithExact() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/withExact.kt"); runTest("compiler/testData/diagnostics/tests/inference/completion/withExact.kt");