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
@@ -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