diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorUtil.java b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorUtil.java index bd1a900c88d..62fa4b93c81 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorUtil.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/FunctionDescriptorUtil.java @@ -44,6 +44,11 @@ public class FunctionDescriptorUtil { public boolean isEmpty() { return false; } + + @Override + public String toString() { + return "FunctionDescriptorUtil.MAKE_TYPE_PARAMETERS_FRESH"; + } }); public static Map createSubstitutionContext(@NotNull FunctionDescriptor functionDescriptor, List typeArguments) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemWithPriorities.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemWithPriorities.java index edccd282990..075bc5371ef 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemWithPriorities.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/calls/inference/ConstraintSystemWithPriorities.java @@ -490,6 +490,11 @@ public class ConstraintSystemWithPriorities implements ConstraintSystem { public boolean isEmpty() { return false; } + + @Override + public String toString() { + return unknownTypes.toString(); + } }); private SolutionStatus status; diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/CompositeTypeSubstitution.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/CompositeTypeSubstitution.java index 3ed6708cb2c..32b2cfd5b52 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/CompositeTypeSubstitution.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/CompositeTypeSubstitution.java @@ -44,4 +44,13 @@ public class CompositeTypeSubstitution implements TypeSubstitutor.TypeSubstituti } return true; } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + for (TypeSubstitutor.TypeSubstitution substitution : inner) { + builder.append(substitution).append(" * "); + } + return builder.toString(); + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/DescriptorSubstitutor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/DescriptorSubstitutor.java index 9aaa0144094..305739640a2 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/DescriptorSubstitutor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/DescriptorSubstitutor.java @@ -50,6 +50,11 @@ public class DescriptorSubstitutor { public boolean isEmpty() { return originalSubstitutor.isEmpty() && mutableSubstitution.isEmpty(); } + + @Override + public String toString() { + return "DescriptorSubstitutor.substituteTypeParameters(" + mutableSubstitution + " / " + originalSubstitutor.getSubstitution() + ")"; + } }); for (TypeParameterDescriptor descriptor : typeParameters) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java index bfc35932638..e2487877d0d 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java @@ -17,6 +17,7 @@ package org.jetbrains.jet.lang.types; import com.google.common.collect.Sets; +import com.intellij.openapi.progress.ProcessCanceledException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; @@ -29,6 +30,8 @@ import java.util.*; */ public class TypeSubstitutor { + private static final int MAX_RECURSION_DEPTH = 100; + public static TypeSubstitutor makeConstantSubstitutor(Collection typeParameterDescriptors, JetType type) { final Set constructors = Sets.newHashSet(); for (TypeParameterDescriptor typeParameterDescriptor : typeParameterDescriptors) { @@ -49,6 +52,11 @@ public class TypeSubstitutor { public boolean isEmpty() { return false; } + + @Override + public String toString() { + return "TypeConstructor.makeConstantSubstitutor(" + constructors + " -> " + projection + ")"; + } }); } @@ -63,6 +71,11 @@ public class TypeSubstitutor { public boolean isEmpty() { return true; } + + @Override + public String toString() { + return "Empty TypeSubstitution"; + } }; @Nullable @@ -86,6 +99,11 @@ public class TypeSubstitutor { public boolean isEmpty() { return substitutionContext.isEmpty(); } + + @Override + public String toString() { + return substitutionContext.toString(); + } } public static final TypeSubstitutor EMPTY = create(TypeSubstitution.EMPTY); @@ -170,12 +188,12 @@ public class TypeSubstitutor { TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) constructor.getDeclarationDescriptor(); - TypeProjection result = substitutionResult(typeParameterDescriptor, howThisTypeIsUsed, Variance.INVARIANT, value); + TypeProjection result = substitutionResult(typeParameterDescriptor, howThisTypeIsUsed, Variance.INVARIANT, value, 0); return TypeUtils.makeNullableIfNeeded(result.getType(), type.isNullable()); } - return specializeType(type, howThisTypeIsUsed); + return specializeType(type, howThisTypeIsUsed, 0); } private TypeProjection getValueWithCorrectNullability(TypeSubstitution substitution, JetType type) { @@ -190,7 +208,8 @@ public class TypeSubstitutor { return new TypeProjection(value.getProjectionKind(), TypeUtils.makeNullable(value.getType())); } - private JetType specializeType(JetType subjectType, Variance callSiteVariance) throws SubstitutionException { + private JetType specializeType(JetType subjectType, Variance callSiteVariance, int recursionDepth) throws SubstitutionException { + assertRecursionDepth(recursionDepth, subjectType, substitution); if (ErrorUtils.isErrorType(subjectType)) return subjectType; List newArguments = new ArrayList(); @@ -202,7 +221,7 @@ public class TypeSubstitutor { substitution, argument, parameterDescriptor, - callSiteVariance)); + callSiteVariance, recursionDepth + 1)); } return new JetTypeImpl( subjectType.getAnnotations(), @@ -217,7 +236,10 @@ public class TypeSubstitutor { @NotNull TypeSubstitution substitutionContext, @NotNull TypeProjection passedProjection, @NotNull TypeParameterDescriptor correspondingTypeParameter, - @NotNull Variance contextCallSiteVariance) throws SubstitutionException { + @NotNull Variance contextCallSiteVariance, + int recursionDepth) throws SubstitutionException { + assertRecursionDepth(recursionDepth, correspondingTypeParameter, passedProjection, substitution); + JetType typeToSubstituteIn = passedProjection.getType(); if (ErrorUtils.isErrorType(typeToSubstituteIn)) return passedProjection; @@ -235,20 +257,23 @@ public class TypeSubstitutor { return TypeUtils.makeStarProjection(correspondingTypeParameter); } - return substitutionResult(correspondingTypeParameter, effectiveContextVariance, passedProjectionKind, projectionValue); + return substitutionResult(correspondingTypeParameter, effectiveContextVariance, passedProjectionKind, projectionValue, recursionDepth + 1); } return new TypeProjection( passedProjectionKind, specializeType( typeToSubstituteIn, - effectiveContextVariance)); + effectiveContextVariance, recursionDepth + 1)); } private TypeProjection substitutionResult( TypeParameterDescriptor correspondingTypeParameter, Variance effectiveContextVariance, Variance passedProjectionKind, - TypeProjection value) throws SubstitutionException { + TypeProjection value, + int recursionDepth) throws SubstitutionException { + assertRecursionDepth(recursionDepth, correspondingTypeParameter, value, substitution); + Variance projectionKindValue = value.getProjectionKind(); JetType typeValue = value.getType(); Variance effectiveProjectionKindValue = asymmetricOr(passedProjectionKind, projectionKindValue); @@ -285,7 +310,7 @@ public class TypeSubstitutor { // throw new SubstitutionException(""); // TODO : error message // } // - return new TypeProjection(effectiveProjectionKindValue, specializeType(effectiveTypeValue, effectiveContextVariance)); + return new TypeProjection(effectiveProjectionKindValue, specializeType(effectiveTypeValue, effectiveContextVariance, recursionDepth + 1)); } private static Variance asymmetricOr(Variance a, Variance b) { @@ -300,4 +325,28 @@ public class TypeSubstitutor { } throw new IllegalStateException(declarationSiteVariance.toString()); } + + private static void assertRecursionDepth(int recursionDepth, TypeParameterDescriptor parameter, TypeProjection value, TypeSubstitution substitution) { + if (recursionDepth > MAX_RECURSION_DEPTH) { + throw new IllegalStateException("Recursion too deep. Most likely infinite loop while substituting " + safeToString(value) + " for " + safeToString(parameter) + "; substitution: " + safeToString(substitution)); + } + } + + private static void assertRecursionDepth(int recursionDepth, JetType type, TypeSubstitution substitution) { + if (recursionDepth > MAX_RECURSION_DEPTH) { + throw new IllegalStateException("Recursion too deep. Most likely infinite loop while substituting " + safeToString(type) + "; substitution: " + safeToString(substitution)); + } + } + + private static String safeToString(Object o) { + try { + return o.toString(); + } + catch (ProcessCanceledException e) { + throw e; + } + catch (Throwable e) { + return "[Exception while computing toString(): " + e + "]"; + } + } }