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:
+6
-1
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user