Choosing a best fit when getting multiple exact bounds for a type variable
This commit is contained in:
@@ -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
|
||||||
|
}
|
||||||
+26
@@ -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?
|
||||||
+25
@@ -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);
|
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")
|
@TestMetadata("recursiveGeneric.kt")
|
||||||
public void testRecursiveGeneric() throws Exception {
|
public void testRecursiveGeneric() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/commonSupertype/recursiveGeneric.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/commonSupertype/recursiveGeneric.kt");
|
||||||
@@ -7811,6 +7823,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("compiler/testData/diagnostics/tests/platformTypes/methodCall")
|
||||||
@@ -7869,6 +7887,18 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("objectArray.kt")
|
||||||
public void testObjectArray() throws Exception {
|
public void testObjectArray() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/methodCall/objectArray.kt");
|
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/platformTypes/methodCall/objectArray.kt");
|
||||||
|
|||||||
+4
-4
@@ -156,10 +156,10 @@ public class TypeBoundsImpl implements TypeBounds {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Set<JetType> exactBounds = filterBounds(bounds, BoundKind.EXACT_BOUND, values);
|
Set<JetType> exactBounds = filterBounds(bounds, BoundKind.EXACT_BOUND, values);
|
||||||
if (exactBounds.size() == 1) {
|
JetType bestFit = TypesPackage.singleBestRepresentative(exactBounds);
|
||||||
JetType exactBound = exactBounds.iterator().next();
|
if (bestFit != null) {
|
||||||
if (tryPossibleAnswer(exactBound)) {
|
if (tryPossibleAnswer(bestFit)) {
|
||||||
return Collections.singleton(exactBound);
|
return Collections.singleton(bestFit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
values.addAll(exactBounds);
|
values.addAll(exactBounds);
|
||||||
|
|||||||
@@ -103,7 +103,9 @@ public class CommonSupertypes {
|
|||||||
private static JetType commonSuperTypeForInflexible(@NotNull Collection<JetType> types, int recursionDepth, int maxDepth) {
|
private static JetType commonSuperTypeForInflexible(@NotNull Collection<JetType> types, int recursionDepth, int maxDepth) {
|
||||||
assert !types.isEmpty();
|
assert !types.isEmpty();
|
||||||
Collection<JetType> typeSet = new HashSet<JetType>(types);
|
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
|
// 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
|
// This also removed Nothing and Nothing? because they are subtypes of everything else
|
||||||
@@ -232,8 +234,9 @@ public class CommonSupertypes {
|
|||||||
@NotNull Set<TypeProjection> typeProjections,
|
@NotNull Set<TypeProjection> typeProjections,
|
||||||
int recursionDepth, int maxDepth
|
int recursionDepth, int maxDepth
|
||||||
) {
|
) {
|
||||||
if (typeProjections.size() == 1) {
|
TypeProjection singleBestProjection = TypesPackage.singleBestRepresentative(typeProjections);
|
||||||
return typeProjections.iterator().next();
|
if (singleBestProjection != null) {
|
||||||
|
return singleBestProjection;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recursionDepth >= maxDepth) {
|
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.checker.JetTypeChecker
|
||||||
import org.jetbrains.jet.lang.types.Approximation.DataFlowExtras
|
import org.jetbrains.jet.lang.types.Approximation.DataFlowExtras
|
||||||
|
import org.jetbrains.kotlin.util.printAndReturn
|
||||||
|
|
||||||
public trait Flexibility : TypeCapability {
|
public trait Flexibility : TypeCapability {
|
||||||
// lowerBound is a subtype of upperBound
|
// 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.isFlexible(): Boolean = this.getCapability(javaClass<Flexibility>()) != null
|
||||||
public fun JetType.flexibility(): Flexibility = this.getCapability(javaClass<Flexibility>())!!
|
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.lowerIfFlexible(): JetType = if (this.isFlexible()) this.flexibility().getLowerBound() else this
|
||||||
public fun JetType.upperIfFlexible(): JetType = if (this.isFlexible()) this.flexibility().getUpperBound() else this
|
public fun JetType.upperIfFlexible(): JetType = if (this.isFlexible()) this.flexibility().getUpperBound() else this
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user