KT-286 Check supertype lists (in progress)
Consistency for type arguments in the supertype list
This commit is contained in:
@@ -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> {}
|
||||
Reference in New Issue
Block a user