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);
|
||||
}
|
||||
|
||||
@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");
|
||||
|
||||
+4
-4
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user