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 org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.List;
@@ -12,6 +13,11 @@ public class JetNullableType extends JetTypeElement {
public JetNullableType(@NotNull ASTNode node) {
super(node);
}
@NotNull
public ASTNode getQuestionMarkNode() {
return getNode().findChildByType(JetTokens.QUEST);
}
@NotNull
@Override
@@ -1,6 +1,7 @@
package org.jetbrains.jet.lang.resolve;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.intellij.lang.ASTNode;
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.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.JetTypeInferrer;
import org.jetbrains.jet.lang.types.*;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.*;
@@ -24,6 +24,8 @@ import static org.jetbrains.jet.lang.resolve.BindingContext.TYPE;
*/
public class TypeHierarchyResolver {
private final TopDownAnalysisContext context;
private LinkedList<MutableClassDescriptor> topologicalOrder;
public TypeHierarchyResolver(TopDownAnalysisContext 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
resolveTypesInClassHeaders(); // Generic bounds and types in supertype lists (no expressions or constructor resolution)
topologicalOrder = topologicallySortClassesAndObjects();
// Detect and disconnect all loops in the hierarchy
detectAndDisconnectLoops();
// At this point, there are no loops in the type hierarchy
checkSupertypesForConsistency();
// computeSuperclasses();
// Add supertypes to resolution scopes of classes
addSupertypesToScopes();
checkTypesInClassHeaders(); // Generic bounds and supertype lists
checkTypesInClassHeaders(); // Check bounds in the types used in generic bounds and supertype lists
}
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:
// edges that get removed to disconnect loops are more reasonable in this case
LinkedList<MutableClassDescriptor> topologicalOrder = Lists.newLinkedList();
@@ -277,9 +285,15 @@ public class TypeHierarchyResolver {
for (MutableClassDescriptor mutableClassDescriptor : context.getClasses().values()) {
topologicallySort(mutableClassDescriptor, visited, topologicalOrder);
}
for (MutableClassDescriptor mutableClassDescriptor : context.getObjects().values()) {
topologicallySort(mutableClassDescriptor, visited, topologicalOrder);
}
return topologicalOrder;
}
private void detectAndDisconnectLoops() {
// Loop detection and disconnection
visited.clear();
Set<ClassDescriptor> visited = Sets.newHashSet();
Set<ClassDescriptor> beingProcessed = Sets.newHashSet();
List<ClassDescriptor> currentPath = Lists.newArrayList();
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() {
for (MutableClassDescriptor mutableClassDescriptor : context.getClasses().values()) {
mutableClassDescriptor.addSupertypesToScopeForMemberLookup();
@@ -1,8 +1,6 @@
package org.jetbrains.jet.lang.types;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.collect.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
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.resolve.ChainedScope;
import org.jetbrains.jet.lang.resolve.JetScope;
import org.jetbrains.jet.util.CommonSuppliers;
import java.util.*;
@@ -229,15 +228,25 @@ public class TypeUtils {
*/
@NotNull
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);
// we use the mutability of the map here
fillInDeepSubstitutor(type, typeSubstitutor, substitution);
fillInDeepSubstitutor(type, typeSubstitutor, substitution, null);
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
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<TypeProjection> arguments = context.getArguments();
for (int i = 0; i < arguments.size(); i++) {
@@ -248,9 +257,12 @@ public class TypeUtils {
assert substitute != null;
TypeProjection substitutedTypeProjection = new TypeProjection(argument.getProjectionKind(), substitute);
substitution.put(typeParameterDescriptor.getTypeConstructor(), substitutedTypeProjection);
if (fullSubstitution != null) {
fullSubstitution.put(typeParameterDescriptor.getTypeConstructor(), substitutedTypeProjection);
}
}
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.collect.Lists;
import com.google.common.collect.Sets;
import java.util.List;
import java.util.Set;
/**
* @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() {
//noinspection unchecked
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 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> {}