diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/MutableClassDescriptorLite.java b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/MutableClassDescriptorLite.java index ef24b557129..bd5bf229f73 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/MutableClassDescriptorLite.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/descriptors/MutableClassDescriptorLite.java @@ -140,7 +140,12 @@ public class MutableClassDescriptorLite extends MutableDeclarationDescriptor imp List typeParameters = getTypeConstructor().getParameters(); Map substitutionContext = SubstitutionUtils.buildSubstitutionContext(typeParameters, typeArguments); - return new SubstitutingScope(scopeForMemberLookup, TypeSubstitutor.create(substitutionContext)); + + // Unsafe substitutor is OK, because no recursion can hurt us upon a trivial substitution: + // all the types are written explicitly in the code already, they can not get infinite. + // One exception is *-projections, but they need to be handled separately anyways. + TypeSubstitutor substitutor = TypeSubstitutor.createUnsafe(substitutionContext); + return new SubstitutingScope(scopeForMemberLookup, substitutor); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/types/SubstitutionUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/types/SubstitutionUtils.java index c50f40dfb81..90f5e04380b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/SubstitutionUtils.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/SubstitutionUtils.java @@ -25,6 +25,7 @@ import org.jetbrains.jet.lang.types.lang.JetStandardClasses; import org.jetbrains.jet.util.CommonSuppliers; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; @@ -120,6 +121,21 @@ public class SubstitutionUtils { return false; } + public static Map removeTrivialSubstitutions(Map context) { + Map clean = Maps.newHashMap(context); + boolean changed = false; + for (Iterator> iterator = clean.entrySet().iterator(); iterator.hasNext(); ) { + Map.Entry entry = iterator.next(); + TypeConstructor key = entry.getKey(); + TypeProjection value = entry.getValue(); + if (key == value.getType().getConstructor() && value.getProjectionKind() == Variance.INVARIANT) { + iterator.remove(); + changed = true; + } + } + return changed ? clean : context; + } + public static void assertNotImmediatelyRecursive(Map context) { // Make sure we never replace a T with "Foo" or something similar, // because the substitution will not terminate in this case 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 6e5e4e8e7e0..901b2d240fe 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/types/TypeSubstitutor.java @@ -122,8 +122,16 @@ public class TypeSubstitutor { return create(new CompositeTypeSubstitution(substitutions)); } + /** No assertion for immediate recursion */ + public static TypeSubstitutor createUnsafe(@NotNull Map substitutionContext) { + Map cleanContext = SubstitutionUtils.removeTrivialSubstitutions(substitutionContext); + return create(new MapToTypeSubstitutionAdapter(cleanContext)); + } + public static TypeSubstitutor create(@NotNull Map substitutionContext) { - return create(new MapToTypeSubstitutionAdapter(substitutionContext)); + Map cleanContext = SubstitutionUtils.removeTrivialSubstitutions(substitutionContext); + SubstitutionUtils.assertNotImmediatelyRecursive(cleanContext); + return createUnsafe(cleanContext); } public static TypeSubstitutor create(@NotNull JetType context) {