KT-286 Check supertype lists (in progress)

Consistency for type arguments in the supertype list
This commit is contained in:
Andrey Breslav
2011-09-13 20:27:44 +04:00
parent 78a51bd9c4
commit 8ee1096e37
6 changed files with 177 additions and 14 deletions
@@ -2,6 +2,7 @@ package org.jetbrains.jet.lang.psi;
import com.intellij.lang.ASTNode; import com.intellij.lang.ASTNode;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.List; import java.util.List;
@@ -13,6 +14,11 @@ public class JetNullableType extends JetTypeElement {
super(node); super(node);
} }
@NotNull
public ASTNode getQuestionMarkNode() {
return getNode().findChildByType(JetTokens.QUEST);
}
@NotNull @NotNull
@Override @Override
public List<JetTypeReference> getTypeArgumentsAsTypes() { public List<JetTypeReference> getTypeArgumentsAsTypes() {
@@ -1,6 +1,7 @@
package org.jetbrains.jet.lang.resolve; package org.jetbrains.jet.lang.resolve;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import com.intellij.lang.ASTNode; import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
@@ -10,8 +11,7 @@ import org.jetbrains.jet.lang.cfg.JetFlowInformationProvider;
import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lang.types.JetTypeInferrer;
import org.jetbrains.jet.lexer.JetTokens; import org.jetbrains.jet.lexer.JetTokens;
import java.util.*; import java.util.*;
@@ -24,6 +24,8 @@ import static org.jetbrains.jet.lang.resolve.BindingContext.TYPE;
*/ */
public class TypeHierarchyResolver { public class TypeHierarchyResolver {
private final TopDownAnalysisContext context; private final TopDownAnalysisContext context;
private LinkedList<MutableClassDescriptor> topologicalOrder;
public TypeHierarchyResolver(TopDownAnalysisContext context) { public TypeHierarchyResolver(TopDownAnalysisContext context) {
this.context = context; this.context = context;
@@ -35,14 +37,20 @@ public class TypeHierarchyResolver {
createTypeConstructors(); // create type constructors for classes and generic parameters, supertypes are not filled in createTypeConstructors(); // create type constructors for classes and generic parameters, supertypes are not filled in
resolveTypesInClassHeaders(); // Generic bounds and types in supertype lists (no expressions or constructor resolution) resolveTypesInClassHeaders(); // Generic bounds and types in supertype lists (no expressions or constructor resolution)
topologicalOrder = topologicallySortClassesAndObjects();
// Detect and disconnect all loops in the hierarchy // Detect and disconnect all loops in the hierarchy
detectAndDisconnectLoops(); detectAndDisconnectLoops();
// At this point, there are no loops in the type hierarchy
checkSupertypesForConsistency();
// computeSuperclasses();
// Add supertypes to resolution scopes of classes // Add supertypes to resolution scopes of classes
addSupertypesToScopes(); addSupertypesToScopes();
checkTypesInClassHeaders(); // Check bounds in the types used in generic bounds and supertype lists
checkTypesInClassHeaders(); // Generic bounds and supertype lists
} }
private void collectNamespacesAndClassifiers( private void collectNamespacesAndClassifiers(
@@ -269,7 +277,7 @@ public class TypeHierarchyResolver {
} }
} }
private void detectAndDisconnectLoops() { private LinkedList<MutableClassDescriptor> topologicallySortClassesAndObjects() {
// A topsort is needed only for better diagnostics: // A topsort is needed only for better diagnostics:
// edges that get removed to disconnect loops are more reasonable in this case // edges that get removed to disconnect loops are more reasonable in this case
LinkedList<MutableClassDescriptor> topologicalOrder = Lists.newLinkedList(); LinkedList<MutableClassDescriptor> topologicalOrder = Lists.newLinkedList();
@@ -277,9 +285,15 @@ public class TypeHierarchyResolver {
for (MutableClassDescriptor mutableClassDescriptor : context.getClasses().values()) { for (MutableClassDescriptor mutableClassDescriptor : context.getClasses().values()) {
topologicallySort(mutableClassDescriptor, visited, topologicalOrder); topologicallySort(mutableClassDescriptor, visited, topologicalOrder);
} }
for (MutableClassDescriptor mutableClassDescriptor : context.getObjects().values()) {
topologicallySort(mutableClassDescriptor, visited, topologicalOrder);
}
return topologicalOrder;
}
private void detectAndDisconnectLoops() {
// Loop detection and disconnection // Loop detection and disconnection
visited.clear(); Set<ClassDescriptor> visited = Sets.newHashSet();
Set<ClassDescriptor> beingProcessed = Sets.newHashSet(); Set<ClassDescriptor> beingProcessed = Sets.newHashSet();
List<ClassDescriptor> currentPath = Lists.newArrayList(); List<ClassDescriptor> currentPath = Lists.newArrayList();
for (MutableClassDescriptor mutableClassDescriptor : topologicalOrder) { for (MutableClassDescriptor mutableClassDescriptor : topologicalOrder) {
@@ -369,6 +383,81 @@ public class TypeHierarchyResolver {
} }
} }
private void checkSupertypesForConsistency() {
for (MutableClassDescriptor mutableClassDescriptor : topologicalOrder) {
Multimap<TypeConstructor, TypeProjection> multimap = TypeUtils.buildDeepSubstitutionMultimap(mutableClassDescriptor.getDefaultType());
for (Map.Entry<TypeConstructor, Collection<TypeProjection>> entry : multimap.asMap().entrySet()) {
Collection<TypeProjection> projections = entry.getValue();
if (projections.size() > 1) {
TypeConstructor typeConstructor = entry.getKey();
DeclarationDescriptor declarationDescriptor = typeConstructor.getDeclarationDescriptor();
assert declarationDescriptor instanceof TypeParameterDescriptor : declarationDescriptor;
TypeParameterDescriptor typeParameterDescriptor = (TypeParameterDescriptor) declarationDescriptor;
// Immediate arguments of supertypes cannot be projected
Set<JetType> conflictingTypes = Sets.newLinkedHashSet();
for (TypeProjection projection : projections) {
conflictingTypes.add(projection.getType());
}
switch (typeParameterDescriptor.getVariance()) {
case INVARIANT:
// Leave conflicting types as is
break;
case IN_VARIANCE:
// Filter out those who have supertypes in this set (common supertype)
Filter.REMOVE_IF_SUPERTYPE_IN_THE_SET.proceed(conflictingTypes);
break;
case OUT_VARIANCE:
// Filter out those who have subtypes in this set (common subtype)
Filter.REMOVE_IF_SUBTYPE_IN_THE_SET.proceed(conflictingTypes);
break;
}
if (conflictingTypes.size() > 1) {
DeclarationDescriptor containingDeclaration = typeParameterDescriptor.getContainingDeclaration();
assert containingDeclaration instanceof ClassDescriptor : containingDeclaration;
PsiElement psiElement = context.getTrace().get(DESCRIPTOR_TO_DECLARATION, mutableClassDescriptor);
assert psiElement instanceof JetClassOrObject : psiElement;
JetClassOrObject declaration = (JetClassOrObject) psiElement;
JetDelegationSpecifierList delegationSpecifierList = declaration.getDelegationSpecifierList();
assert delegationSpecifierList != null;
context.getTrace().getErrorHandler().genericError(delegationSpecifierList.getNode(), "Type parameter " + typeParameterDescriptor.getName() + " of " + containingDeclaration.getName() + " has inconsistent values: " + conflictingTypes);
}
}
}
}
}
private enum Filter {
REMOVE_IF_SUBTYPE_IN_THE_SET {
@Override
public boolean removeNeeded(JetType subject, JetType other) {
return JetTypeChecker.INSTANCE.isSubtypeOf(other, subject);
}
},
REMOVE_IF_SUPERTYPE_IN_THE_SET {
@Override
public boolean removeNeeded(JetType subject, JetType other) {
return JetTypeChecker.INSTANCE.isSubtypeOf(subject, other);
}
};
private void proceed(Set<JetType> conflictingTypes) {
for (Iterator<JetType> iterator = conflictingTypes.iterator(); iterator.hasNext(); ) {
JetType type = iterator.next();
for (JetType otherType : conflictingTypes) {
boolean subtypeOf = removeNeeded(type, otherType);
if (type != otherType && subtypeOf) {
iterator.remove();
break;
}
}
}
}
public abstract boolean removeNeeded(JetType subject, JetType other);
}
private void addSupertypesToScopes() { private void addSupertypesToScopes() {
for (MutableClassDescriptor mutableClassDescriptor : context.getClasses().values()) { for (MutableClassDescriptor mutableClassDescriptor : context.getClasses().values()) {
mutableClassDescriptor.addSupertypesToScopeForMemberLookup(); mutableClassDescriptor.addSupertypesToScopeForMemberLookup();
@@ -1,8 +1,6 @@
package org.jetbrains.jet.lang.types; package org.jetbrains.jet.lang.types;
import com.google.common.collect.Lists; import com.google.common.collect.*;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor; import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
@@ -11,6 +9,7 @@ import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.resolve.ChainedScope; import org.jetbrains.jet.lang.resolve.ChainedScope;
import org.jetbrains.jet.lang.resolve.JetScope; import org.jetbrains.jet.lang.resolve.JetScope;
import org.jetbrains.jet.util.CommonSuppliers;
import java.util.*; import java.util.*;
@@ -229,15 +228,25 @@ public class TypeUtils {
*/ */
@NotNull @NotNull
public static TypeSubstitutor buildDeepSubstitutor(@NotNull JetType type) { public static TypeSubstitutor buildDeepSubstitutor(@NotNull JetType type) {
HashMap<TypeConstructor, TypeProjection> substitution = Maps.<TypeConstructor, TypeProjection>newHashMap(); Map<TypeConstructor, TypeProjection> substitution = Maps.newHashMap();
TypeSubstitutor typeSubstitutor = TypeSubstitutor.create(substitution); TypeSubstitutor typeSubstitutor = TypeSubstitutor.create(substitution);
// we use the mutability of the map here // we use the mutability of the map here
fillInDeepSubstitutor(type, typeSubstitutor, substitution); fillInDeepSubstitutor(type, typeSubstitutor, substitution, null);
return typeSubstitutor; return typeSubstitutor;
} }
@NotNull
public static Multimap<TypeConstructor, TypeProjection> buildDeepSubstitutionMultimap(@NotNull JetType type) {
Multimap<TypeConstructor, TypeProjection> fullSubstitution = Multimaps.newSetMultimap(Maps.<TypeConstructor, Collection<TypeProjection>>newHashMap(), CommonSuppliers.<TypeProjection>getLinkedHashSetSupplier());
Map<TypeConstructor, TypeProjection> substitution = Maps.newHashMap();
TypeSubstitutor typeSubstitutor = TypeSubstitutor.create(substitution);
// we use the mutability of the map here
fillInDeepSubstitutor(type, typeSubstitutor, substitution, fullSubstitution);
return fullSubstitution;
}
// we use the mutability of the substitution map here // we use the mutability of the substitution map here
private static void fillInDeepSubstitutor(JetType context, TypeSubstitutor substitutor, Map<TypeConstructor, TypeProjection> substitution) { private static void fillInDeepSubstitutor(@NotNull JetType context, @NotNull TypeSubstitutor substitutor, @NotNull Map<TypeConstructor, TypeProjection> substitution, @Nullable Multimap<TypeConstructor, TypeProjection> fullSubstitution) {
List<TypeParameterDescriptor> parameters = context.getConstructor().getParameters(); List<TypeParameterDescriptor> parameters = context.getConstructor().getParameters();
List<TypeProjection> arguments = context.getArguments(); List<TypeProjection> arguments = context.getArguments();
for (int i = 0; i < arguments.size(); i++) { for (int i = 0; i < arguments.size(); i++) {
@@ -248,9 +257,12 @@ public class TypeUtils {
assert substitute != null; assert substitute != null;
TypeProjection substitutedTypeProjection = new TypeProjection(argument.getProjectionKind(), substitute); TypeProjection substitutedTypeProjection = new TypeProjection(argument.getProjectionKind(), substitute);
substitution.put(typeParameterDescriptor.getTypeConstructor(), substitutedTypeProjection); substitution.put(typeParameterDescriptor.getTypeConstructor(), substitutedTypeProjection);
if (fullSubstitution != null) {
fullSubstitution.put(typeParameterDescriptor.getTypeConstructor(), substitutedTypeProjection);
}
} }
for (JetType supertype : context.getConstructor().getSupertypes()) { for (JetType supertype : context.getConstructor().getSupertypes()) {
fillInDeepSubstitutor(supertype, substitutor, substitution); fillInDeepSubstitutor(supertype, substitutor, substitution, fullSubstitution);
} }
} }
@@ -2,8 +2,10 @@ package org.jetbrains.jet.util;
import com.google.common.base.Supplier; import com.google.common.base.Supplier;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.util.List; import java.util.List;
import java.util.Set;
/** /**
* @author abreslav * @author abreslav
@@ -18,8 +20,20 @@ public class CommonSuppliers {
} }
}; };
private static final Supplier<?> LINKED_HASH_SET_SUPPLIER = new Supplier() {
@Override
public Set get() {
return Sets.newLinkedHashSet();
}
};
public static <T> Supplier<List<T>> getArrayListSupplier() { public static <T> Supplier<List<T>> getArrayListSupplier() {
//noinspection unchecked //noinspection unchecked
return (Supplier<List<T>>) ARRAY_LIST_SUPPLIER; return (Supplier<List<T>>) ARRAY_LIST_SUPPLIER;
} }
public static <T> Supplier<Set<T>> getLinkedHashSetSupplier() {
//noinspection unchecked
return (Supplier<Set<T>>) LINKED_HASH_SET_SUPPLIER;
}
} }
@@ -0,0 +1,42 @@
trait A<in T> {}
trait B<T> : A<Int> {}
trait C<T> : <error>B<T>, A<T></error> {}
trait C1<T> : B<T>, A<Any> {}
trait D : <error>C<Boolean>, B<Double></error>{}
trait A1<out T> {}
trait B1 : A1<Int> {}
trait B2 : A1<Any>, B1 {}
trait BA1<T> {}
trait BB1 : BA1<Int> {}
trait BB2 : <error>BA1<Any>, BB1</error> {}
namespace x {
trait AA1<out T> {}
trait AB1 : AA1<Int> {}
trait AB3 : AA1<Comparable<Int>> {}
trait AB2 : AA1<Number>, AB1, AB3 {}
}
namespace x2 {
trait AA1<out T> {}
trait AB1 : AA1<Any> {}
trait AB3 : AA1<Comparable<Int>> {}
trait AB2 : <error>AA1<Number>, AB1, AB3</error> {}
}
namespace x3 {
trait AA1<in T> {}
trait AB1 : AA1<Any> {}
trait AB3 : AA1<Comparable<Int>> {}
trait AB2 : AA1<Number>, AB1, AB3 {}
}
namespace sx2 {
trait AA1<in T> {}
trait AB1 : AA1<Int> {}
trait AB3 : AA1<Comparable<Int>> {}
trait AB2 : <error>AA1<Number>, AB1, AB3</error> {}
}
@@ -3,4 +3,4 @@ trait B<T> {}
trait C<T> {} trait C<T> {}
trait D<T> {} trait D<T> {}
trait Test : A<<error>in</error> Int>, B<<error>out</error> T>, C<<error>*</error>><error>?</error><error>?</error><error>?</error>, D<Int> {} trait Test : A<<error>in</error> Int>, B<<error>out</error> Int>, C<<error>*</error>><error>?</error><error>?</error><error>?</error>, D<Int> {}