Recursion depth in CommonSupertypes is bounded by maxDepth + 3

This commit is contained in:
Andrey Breslav
2014-08-27 16:13:47 +04:00
parent 364a7a6574
commit 12d18533e8
8 changed files with 195 additions and 11 deletions
@@ -16,6 +16,8 @@
package org.jetbrains.jet.lang.types;
import kotlin.Function1;
import kotlin.KotlinPackage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
@@ -47,6 +49,34 @@ public class CommonSupertypes {
@NotNull
public static JetType commonSupertype(@NotNull Collection<JetType> types) {
// Recursion should not be significantly deeper than the deepest type in question
// It can be slightly deeper, though: e.g. when initial types are simple, but their supertypes are complex
return findCommonSupertype(types, 0, maxDepth(types) + 3);
}
private static int maxDepth(@NotNull Collection<JetType> types) {
int max = 0;
for (JetType type : types) {
int depth = depth(type);
if (max < depth) {
max = depth;
}
}
return max;
}
private static int depth(@NotNull JetType type) {
return 1 + maxDepth(KotlinPackage.map(type.getArguments(), new Function1<TypeProjection, JetType>() {
@Override
public JetType invoke(TypeProjection projection) {
return projection.getType();
}
}));
}
@NotNull
private static JetType findCommonSupertype(@NotNull Collection<JetType> types, int recursionDepth, int maxDepth) {
assert recursionDepth <= maxDepth : "Recursion depth exceeded: " + recursionDepth + " > " + maxDepth + " for types " + types;
boolean hasFlexible = false;
List<JetType> upper = new ArrayList<JetType>(types.size());
List<JetType> lower = new ArrayList<JetType>(types.size());
@@ -63,12 +93,15 @@ public class CommonSupertypes {
}
}
if (!hasFlexible) return commonSuperTypeForInflexible(types);
return DelegatingFlexibleType.OBJECT$.create(commonSuperTypeForInflexible(lower), commonSuperTypeForInflexible(upper));
if (!hasFlexible) return commonSuperTypeForInflexible(types, recursionDepth, maxDepth);
return DelegatingFlexibleType.OBJECT$.create(
commonSuperTypeForInflexible(lower, recursionDepth, maxDepth),
commonSuperTypeForInflexible(upper, recursionDepth, maxDepth)
);
}
@NotNull
private static JetType commonSuperTypeForInflexible(@NotNull Collection<JetType> types) {
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();
@@ -114,7 +147,7 @@ public class CommonSupertypes {
Map.Entry<TypeConstructor, Set<JetType>> entry = commonSupertypes.entrySet().iterator().next();
// Reconstructing type arguments if possible
JetType result = computeSupertypeProjections(entry.getKey(), entry.getValue());
JetType result = computeSupertypeProjections(entry.getKey(), entry.getValue(), recursionDepth, maxDepth);
return TypeUtils.makeNullableIfNeeded(result, nullable);
}
@@ -160,7 +193,7 @@ public class CommonSupertypes {
// constructor - type constructor of a supertype to be instantiated
// types - instantiations of constructor occurring as supertypes of classes we are trying to intersect
@NotNull
private static JetType computeSupertypeProjections(@NotNull TypeConstructor constructor, @NotNull Set<JetType> types) {
private static JetType computeSupertypeProjections(@NotNull TypeConstructor constructor, @NotNull Set<JetType> types, int recursionDepth, int maxDepth) {
// we assume that all the given types are applications of the same type constructor
assert !types.isEmpty();
@@ -177,7 +210,7 @@ public class CommonSupertypes {
for (JetType type : types) {
typeProjections.add(type.getArguments().get(i));
}
newProjections.add(computeSupertypeProjection(parameterDescriptor, typeProjections));
newProjections.add(computeSupertypeProjection(parameterDescriptor, typeProjections, recursionDepth, maxDepth));
}
boolean nullable = false;
@@ -195,11 +228,21 @@ public class CommonSupertypes {
}
@NotNull
private static TypeProjection computeSupertypeProjection(@NotNull TypeParameterDescriptor parameterDescriptor, @NotNull Set<TypeProjection> typeProjections) {
private static TypeProjection computeSupertypeProjection(
@NotNull TypeParameterDescriptor parameterDescriptor,
@NotNull Set<TypeProjection> typeProjections,
int recursionDepth, int maxDepth
) {
if (typeProjections.size() == 1) {
return typeProjections.iterator().next();
}
if (recursionDepth >= maxDepth) {
// If recursion is too deep, we cut it by taking <out Any?> as an ultimate supertype argument
// Example: class A : Base<A>; class B : Base<B>, commonSuperType(A, B) = Base<out Any?>
return new TypeProjectionImpl(OUT_VARIANCE, KotlinBuiltIns.getInstance().getNullableAnyType());
}
Set<JetType> ins = new HashSet<JetType>();
Set<JetType> outs = new HashSet<JetType>();
@@ -239,19 +282,19 @@ public class CommonSupertypes {
if (outs != null) {
Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE;
return new TypeProjectionImpl(projectionKind, commonSupertype(outs));
return new TypeProjectionImpl(projectionKind, findCommonSupertype(outs, recursionDepth + 1, maxDepth));
}
if (ins != null) {
JetType intersection = TypeUtils.intersect(JetTypeChecker.DEFAULT, ins);
if (intersection == null) {
return new TypeProjectionImpl(OUT_VARIANCE, commonSupertype(parameterDescriptor.getUpperBounds()));
return new TypeProjectionImpl(OUT_VARIANCE, findCommonSupertype(parameterDescriptor.getUpperBounds(), recursionDepth + 1, maxDepth));
}
Variance projectionKind = variance == IN_VARIANCE ? Variance.INVARIANT : IN_VARIANCE;
return new TypeProjectionImpl(projectionKind, intersection);
}
else {
Variance projectionKind = variance == OUT_VARIANCE ? Variance.INVARIANT : OUT_VARIANCE;
return new TypeProjectionImpl(projectionKind, commonSupertype(parameterDescriptor.getUpperBounds()));
return new TypeProjectionImpl(projectionKind, findCommonSupertype(parameterDescriptor.getUpperBounds(), recursionDepth + 1, maxDepth));
}
}