Recursion depth in CommonSupertypes is bounded by maxDepth + 3
This commit is contained in:
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user