[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");
}
@TestMetadata("equalityConstraintUpstairs.kt")
public void testEqualityConstraintUpstairs() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/equalityConstraintUpstairs.kt");
}
@TestMetadata("kt33166.kt")
public void testKt33166() throws Exception {
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");
}
@TestMetadata("transitiveConstraint.kt")
public void testTransitiveConstraint() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/transitiveConstraint.kt");
}
@TestMetadata("withExact.kt")
public void testWithExact() throws Exception {
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.UnwrappedType
import org.jetbrains.kotlin.types.model.*
import org.jetbrains.kotlin.utils.newLinkedHashMapWithExpectedSize
import java.util.*
typealias CsCompleterContext = KotlinConstraintSystemCompleter.Context
@@ -45,10 +46,11 @@ class CompletionModeCalculator {
private val csCompleterContext: CsCompleterContext
) {
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 typesToProcess: Queue<KotlinTypeMarker> = ArrayDeque()
@@ -68,14 +70,14 @@ class CompletionModeCalculator {
while (typesToProcess.isNotEmpty()) {
val type = typesToProcess.poll() ?: break
fixationRequirementForTopLevel(type)?.let { directionForVariable ->
fixationDirectionForTopLevel(type)?.let { directionForVariable ->
updateDirection(directionForVariable)
enqueueTypesFromConstraints(directionForVariable.variable)
}
// find all variables in type and make requirements for them
type.contains { fromReturnType ->
for (directionForVariable in directionsForVariablesInTypeArguments(fromReturnType)) {
type.contains { typePart ->
for (directionForVariable in directionsForVariablesInTypeArguments(typePart)) {
updateDirection(directionForVariable)
enqueueTypesFromConstraints(directionForVariable.variable)
}
@@ -106,16 +108,16 @@ class CompletionModeCalculator {
private fun updateDirection(directionForVariable: FixationDirectionForVariable) {
val (variable, newDirection) = directionForVariable
fixationDirectionsForVariables[variable]?.let { oldDirection ->
// To sub and to super are merged into equality, old equality stays
if (oldDirection != FixationDirection.EQUALITY && oldDirection != newDirection)
fixationDirectionsForVariables[variable] = FixationDirection.EQUALITY
} ?: run {
fixationDirectionsForVariables[variable] = newDirection
}
fixationDirectionsForVariables[variable] = newDirection
}
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 {
FixationDirectionForVariable(it, FixationDirection.TO_SUBTYPE)
}
@@ -138,10 +140,10 @@ class CompletionModeCalculator {
val parameter = type.typeConstructor().getParameter(position)
val effectiveVariance = AbstractTypeChecker.effectiveVariance(parameter.getVariance(), argument.getVariance())
?: TypeVariance.OUT // Discuss
?: TypeVariance.INV
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.INV -> FixationDirection.EQUALITY
}
@@ -169,7 +171,6 @@ class CompletionModeCalculator {
private fun Constraint.hasRequiredKind(direction: FixationDirection) = when (direction) {
FixationDirection.TO_SUBTYPE -> kind.isLower() || kind.isEqual()
FixationDirection.TO_SUPERTYPE -> kind.isUpper() || kind.isEqual()
FixationDirection.EQUALITY -> kind.isEqual()
}
}
@@ -57,7 +57,6 @@ fun test7(cls: Cls) {
}
fun test8(cls: Cls) {
// TODO
id(
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<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(int), any)
@@ -10386,6 +10386,11 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
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")
public void testKt33166() throws Exception {
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");
}
@TestMetadata("transitiveConstraint.kt")
public void testTransitiveConstraint() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/transitiveConstraint.kt");
}
@TestMetadata("withExact.kt")
public void testWithExact() throws Exception {
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");
}
@TestMetadata("equalityConstraintUpstairs.kt")
public void testEqualityConstraintUpstairs() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/equalityConstraintUpstairs.kt");
}
@TestMetadata("kt33166.kt")
public void testKt33166() throws Exception {
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");
}
@TestMetadata("transitiveConstraint.kt")
public void testTransitiveConstraint() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/transitiveConstraint.kt");
}
@TestMetadata("withExact.kt")
public void testWithExact() throws Exception {
runTest("compiler/testData/diagnostics/tests/inference/completion/withExact.kt");