Choosing a best fit when getting multiple exact bounds for a type variable

This commit is contained in:
Andrey Breslav
2014-10-08 13:17:36 +04:00
parent d74b27335e
commit 890ef7a8ae
14 changed files with 276 additions and 7 deletions
@@ -0,0 +1,12 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE
// !CHECK_TYPE
import java.util.ArrayList
fun foo(handlers: Array<MutableList<String>?>) {
val v = handlers[0] ?: ArrayList<String>()
handlers[0] = v
val js: MutableList<String> = v
v checkType { it : _<MutableList<String>>}
v checkType { it : _<MutableList<String?>>}
}
@@ -0,0 +1,11 @@
package
internal fun foo(/*0*/ handlers: kotlin.Array<kotlin.MutableList<kotlin.String>?>): kotlin.Unit
internal fun </*0*/ T> T.checkType(/*0*/ f: (_<T>) -> kotlin.Unit): (_<T>) -> kotlin.Unit
internal final class _</*0*/ T> {
public constructor _</*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
}
@@ -0,0 +1,20 @@
// !DIAGNOSTICS: -UNUSED_VARIABLE
// !CHECK_TYPE
// FILE: p/J.java
package p;
public class J<G> {
public static J<String> j() { return null; }
}
// FILE: k.kt
import p.*
fun foo(j: J<String>) {
val v = if (true) j else J.j()
val js: J<String> = v
v checkType { it : _<J<String>>}
v checkType { it : _<J<String?>>}
}
@@ -0,0 +1,11 @@
package
internal fun foo(/*0*/ j: p.J<kotlin.String>): kotlin.Unit
internal fun </*0*/ T> T.checkType(/*0*/ f: (_<T>) -> kotlin.Unit): (_<T>) -> kotlin.Unit
internal final class _</*0*/ T> {
public constructor _</*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
}
@@ -0,0 +1,24 @@
// !DIAGNOSTICS: -UNUSED_PARAMETER
// !CHECK_TYPE
// FILE: p/J.java
package p;
public class J {
public static J j() { return null; }
}
// FILE: k.kt
import p.*
trait Out<out T1>
fun <T> f(a: Out<T>, b: Out<T>, c: Out<T>): T = null!!
fun <T> out(t: T): Out<MutableList<T>> = null!!
fun test(a: Out<Nothing>, b: Out<MutableList<J>>) {
val v = f(a, b, out(J.j()))
v checkType { it : _<MutableList<J>>}
v checkType { it : _<MutableList<J?>>}
}
@@ -0,0 +1,19 @@
package
internal fun </*0*/ T> f(/*0*/ a: Out<T>, /*1*/ b: Out<T>, /*2*/ c: Out<T>): T
internal fun </*0*/ T> out(/*0*/ t: T): Out<kotlin.MutableList<T>>
internal fun test(/*0*/ a: Out<kotlin.Nothing>, /*1*/ b: Out<kotlin.MutableList<p.J>>): kotlin.Unit
internal fun </*0*/ T> T.checkType(/*0*/ f: (_<T>) -> kotlin.Unit): (_<T>) -> kotlin.Unit
internal trait Out</*0*/ out T1> {
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
}
internal final class _</*0*/ T> {
public constructor _</*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
}
@@ -0,0 +1,21 @@
import java.util.HashMap
trait ModuleDescriptorImpl
trait ModuleInfo
trait ResolverForModule
trait ResolverForProject<M1, R1>
class ResolverForProjectImpl<M : ModuleInfo, R : ResolverForModule>(
descriptorByModule: Map<M, ModuleDescriptorImpl>,
delegateResolver: ResolverForProject<M, R>
) : ResolverForProject<M, R>
fun <M2: ModuleInfo, A: ResolverForModule> foo(delegateResolver: ResolverForProject<M2, A>): ResolverForProject<M2, A> {
val descriptorByModule = HashMap<M2, ModuleDescriptorImpl>()
return ResolverForProjectImpl(descriptorByModule, delegateResolver)
}
// M = M2
// HashMap<M2, MDI> :< Map<M, MDI> => M = M2!
// R = A
// RFP<M2, A> :< RFP<M, R>
@@ -0,0 +1,34 @@
package
internal fun </*0*/ M2 : ModuleInfo, /*1*/ A : ResolverForModule> foo(/*0*/ delegateResolver: ResolverForProject<M2, A>): ResolverForProject<M2, A>
internal trait ModuleDescriptorImpl {
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
}
internal trait ModuleInfo {
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
}
internal trait ResolverForModule {
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
}
internal trait ResolverForProject</*0*/ M1, /*1*/ R1> {
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
}
internal final class ResolverForProjectImpl</*0*/ M : ModuleInfo, /*1*/ R : ResolverForModule> : ResolverForProject<M, R> {
public constructor ResolverForProjectImpl</*0*/ M : ModuleInfo, /*1*/ R : ResolverForModule>(/*0*/ descriptorByModule: kotlin.Map<M, ModuleDescriptorImpl>, /*1*/ delegateResolver: ResolverForProject<M, R>)
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,26 @@
import java.util.HashMap
trait ResolverForProject<M1> {
val exposeM: M1 get() = null!!
}
class ResolverForProjectImpl<M>(
descriptorByModule: Map<M, String>,
delegateResolver: ResolverForProject<M>
) : ResolverForProject<M>
trait WithFoo {
fun foo()
}
fun <M2: WithFoo> foo(delegateResolver: ResolverForProject<M2?>): ResolverForProject<M2> {
val descriptorByModule = HashMap<M2, String>()
val result = ResolverForProjectImpl(descriptorByModule, delegateResolver)
result.exposeM.foo() // M is not M2?
result.exposeM?.foo() // no warning, M is not M2, hense M is M2!
return ResolverForProjectImpl(descriptorByModule, delegateResolver) // another bound check
}
// HashMap<M2, String> :< Map<M, String> => M = M2!
// RFP<M2?> :< RFP<M> => M = M2?
@@ -0,0 +1,25 @@
package
internal fun </*0*/ M2 : WithFoo> foo(/*0*/ delegateResolver: ResolverForProject<M2?>): ResolverForProject<M2>
internal trait ResolverForProject</*0*/ M1> {
internal open val exposeM: M1
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
}
internal final class ResolverForProjectImpl</*0*/ M> : ResolverForProject<M> {
public constructor ResolverForProjectImpl</*0*/ M>(/*0*/ descriptorByModule: kotlin.Map<M, kotlin.String>, /*1*/ delegateResolver: ResolverForProject<M>)
internal open override /*1*/ /*fake_override*/ val exposeM: M
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
}
internal trait WithFoo {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
internal abstract fun foo(): kotlin.Unit
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -7793,6 +7793,18 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("mixedElvis.kt")
public void testMixedElvis() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/commonSupertype/mixedElvis.kt");
doTest(fileName);
}
@TestMetadata("mixedIf.kt")
public void testMixedIf() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/commonSupertype/mixedIf.kt");
doTest(fileName);
}
@TestMetadata("recursiveGeneric.kt")
public void testRecursiveGeneric() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/commonSupertype/recursiveGeneric.kt");
@@ -7811,6 +7823,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("withNothing.kt")
public void testWithNothing() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/commonSupertype/withNothing.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/diagnostics/tests/platformTypes/methodCall")
@@ -7869,6 +7887,18 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("multipleExactBounds.kt")
public void testMultipleExactBounds() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/methodCall/multipleExactBounds.kt");
doTest(fileName);
}
@TestMetadata("multipleExactBoundsNullable.kt")
public void testMultipleExactBoundsNullable() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/methodCall/multipleExactBoundsNullable.kt");
doTest(fileName);
}
@TestMetadata("objectArray.kt")
public void testObjectArray() throws Exception {
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/methodCall/objectArray.kt");
@@ -156,10 +156,10 @@ public class TypeBoundsImpl implements TypeBounds {
}
Set<JetType> exactBounds = filterBounds(bounds, BoundKind.EXACT_BOUND, values);
if (exactBounds.size() == 1) {
JetType exactBound = exactBounds.iterator().next();
if (tryPossibleAnswer(exactBound)) {
return Collections.singleton(exactBound);
JetType bestFit = TypesPackage.singleBestRepresentative(exactBounds);
if (bestFit != null) {
if (tryPossibleAnswer(bestFit)) {
return Collections.singleton(bestFit);
}
}
values.addAll(exactBounds);
@@ -103,7 +103,9 @@ public class CommonSupertypes {
private static JetType commonSuperTypeForInflexible(@NotNull Collection<JetType> types, int recursionDepth, int maxDepth) {
assert !types.isEmpty();
Collection<JetType> typeSet = new HashSet<JetType>(types);
if (typeSet.size() == 1) return typeSet.iterator().next();
JetType bestFit = TypesPackage.singleBestRepresentative(typeSet);
if (bestFit != null) return bestFit;
// If any of the types is nullable, the result must be nullable
// This also removed Nothing and Nothing? because they are subtypes of everything else
@@ -232,8 +234,9 @@ public class CommonSupertypes {
@NotNull Set<TypeProjection> typeProjections,
int recursionDepth, int maxDepth
) {
if (typeProjections.size() == 1) {
return typeProjections.iterator().next();
TypeProjection singleBestProjection = TypesPackage.singleBestRepresentative(typeProjections);
if (singleBestProjection != null) {
return singleBestProjection;
}
if (recursionDepth >= maxDepth) {
@@ -18,6 +18,7 @@ package org.jetbrains.jet.lang.types
import org.jetbrains.jet.lang.types.checker.JetTypeChecker
import org.jetbrains.jet.lang.types.Approximation.DataFlowExtras
import org.jetbrains.kotlin.util.printAndReturn
public trait Flexibility : TypeCapability {
// lowerBound is a subtype of upperBound
@@ -29,6 +30,38 @@ public trait Flexibility : TypeCapability {
public fun JetType.isFlexible(): Boolean = this.getCapability(javaClass<Flexibility>()) != null
public fun JetType.flexibility(): Flexibility = this.getCapability(javaClass<Flexibility>())!!
// This function is intended primarily for sets: since JetType.equals() represents _syntactical_ equality of types,
// whereas JetTypeChecker.DEFAULT.equalsTypes() represents semantical equality
// A set of types (e.g. exact bounds etc) may contain, for example, X, X? and X!
// These are not equal syntactically (by JetType.equals()), but X! is _compatible_ with others as exact bounds,
// moreover, X! is a better fit.
//
// So, we are looking for a type among this set such that it is equal to all others semantically
// (by JetTypeChecker.DEFAULT.equalsTypes()), and fits at least as well as they do.
fun Collection<JetType>.singleBestRepresentative(): JetType? {
if (this.size() == 1) return this.first()
return this.firstOrNull {
candidate ->
this.all {
other ->
candidate == other || JetTypeChecker.DEFAULT.equalTypes(candidate, other)
}
}
}
fun Collection<TypeProjection>.singleBestRepresentative(): TypeProjection? {
if (this.size() == 1) return this.first()
val projectionKinds = this.map { it.getProjectionKind() }.toSet()
if (projectionKinds.size() != 1) return null
val bestType = this.map { it.getType() }.singleBestRepresentative()
if (bestType == null) return null
return TypeProjectionImpl(projectionKinds.single(), bestType)
}
public fun JetType.lowerIfFlexible(): JetType = if (this.isFlexible()) this.flexibility().getLowerBound() else this
public fun JetType.upperIfFlexible(): JetType = if (this.isFlexible()) this.flexibility().getUpperBound() else this