Guard from immediately recursive substitutions

* substitutions of the form T -> T are removed
* an assertion added against nontrivial cases like T -> Foo<T>
This commit is contained in:
Andrey Breslav
2012-05-03 17:25:35 +04:00
parent 22166bad1b
commit a0367bcfa2
3 changed files with 31 additions and 2 deletions
@@ -140,7 +140,12 @@ public class MutableClassDescriptorLite extends MutableDeclarationDescriptor imp
List<TypeParameterDescriptor> typeParameters = getTypeConstructor().getParameters();
Map<TypeConstructor, TypeProjection> 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);
}
@@ -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<TypeConstructor, TypeProjection> removeTrivialSubstitutions(Map<TypeConstructor, TypeProjection> context) {
Map<TypeConstructor, TypeProjection> clean = Maps.newHashMap(context);
boolean changed = false;
for (Iterator<Map.Entry<TypeConstructor, TypeProjection>> iterator = clean.entrySet().iterator(); iterator.hasNext(); ) {
Map.Entry<TypeConstructor, TypeProjection> 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<TypeConstructor, TypeProjection> context) {
// Make sure we never replace a T with "Foo<T>" or something similar,
// because the substitution will not terminate in this case
@@ -122,8 +122,16 @@ public class TypeSubstitutor {
return create(new CompositeTypeSubstitution(substitutions));
}
/** No assertion for immediate recursion */
public static TypeSubstitutor createUnsafe(@NotNull Map<TypeConstructor, TypeProjection> substitutionContext) {
Map<TypeConstructor, TypeProjection> cleanContext = SubstitutionUtils.removeTrivialSubstitutions(substitutionContext);
return create(new MapToTypeSubstitutionAdapter(cleanContext));
}
public static TypeSubstitutor create(@NotNull Map<TypeConstructor, TypeProjection> substitutionContext) {
return create(new MapToTypeSubstitutionAdapter(substitutionContext));
Map<TypeConstructor, TypeProjection> cleanContext = SubstitutionUtils.removeTrivialSubstitutions(substitutionContext);
SubstitutionUtils.assertNotImmediatelyRecursive(cleanContext);
return createUnsafe(cleanContext);
}
public static TypeSubstitutor create(@NotNull JetType context) {