CYCLIC_INHERITANCE_HIERARCHY reworked

We do not try to recover too gracefully from a cyclic hierarchy any more:
we simply remove all the edges that belong to a cycle instead of intelligently finding one most convenient edge to cut.
This is done in both lazy and eager resolve to keep tests passing.
This commit is contained in:
Andrey Breslav
2014-02-28 20:02:38 +04:00
parent ed81102b2f
commit 8be40c29cf
8 changed files with 167 additions and 105 deletions
@@ -33,6 +33,7 @@ import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
import org.jetbrains.jet.lang.resolve.scopes.WriteThroughScope;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.TypeConstructor;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.utils.DFS;
@@ -247,88 +248,75 @@ public class TypeHierarchyResolver {
private void detectAndDisconnectLoops(@NotNull TopDownAnalysisContext c) {
// Loop detection and disconnection
Set<ClassDescriptor> visited = Sets.newHashSet();
Set<ClassDescriptor> beingProcessed = Sets.newHashSet();
List<ClassDescriptor> currentPath = Lists.newArrayList();
for (MutableClassDescriptorLite klass : c.getClassesTopologicalOrder()) {
traverseTypeHierarchy(klass, visited, beingProcessed, currentPath);
List<Runnable> tasks = new ArrayList<Runnable>();
for (final MutableClassDescriptorLite klass : c.getClassesTopologicalOrder()) {
for (final JetType supertype : klass.getSupertypes()) {
ClassifierDescriptor supertypeDescriptor = supertype.getConstructor().getDeclarationDescriptor();
if (supertypeDescriptor instanceof MutableClassDescriptorLite) {
MutableClassDescriptorLite superclass = (MutableClassDescriptorLite) supertypeDescriptor;
if (isReachable(superclass, klass, new HashSet<ClassDescriptor>())) {
tasks.add(new Runnable() {
@Override
public void run() {
klass.getSupertypes().remove(supertype);
}
});
reportCyclicInheritanceHierarchyError(trace, klass, superclass);
}
}
}
}
for (Runnable task : tasks) {
task.run();
}
}
private void traverseTypeHierarchy(
MutableClassDescriptorLite currentClass,
Set<ClassDescriptor> visited,
Set<ClassDescriptor> beingProcessed,
List<ClassDescriptor> currentPath
// Temporary. Duplicates logic from LazyClassTypeConstructor.isReachable
private static boolean isReachable(MutableClassDescriptorLite from, MutableClassDescriptorLite to, Set<ClassDescriptor> visited) {
if (!visited.add(from)) return false;
for (JetType supertype : from.getSupertypes()) {
TypeConstructor supertypeConstructor = supertype.getConstructor();
if (supertypeConstructor.getDeclarationDescriptor() == to) {
return true;
}
ClassifierDescriptor superclass = supertypeConstructor.getDeclarationDescriptor();
if (superclass instanceof MutableClassDescriptorLite && isReachable((MutableClassDescriptorLite) superclass, to, visited)) {
return true;
}
}
return false;
}
public static void reportCyclicInheritanceHierarchyError(
@NotNull BindingTrace trace,
@NotNull ClassDescriptor classDescriptor,
@NotNull ClassDescriptor superclass
) {
if (!visited.add(currentClass)) {
if (beingProcessed.contains(currentClass)) {
markCycleErrors(currentPath, currentClass);
assert !currentPath.isEmpty() : "Cycle cannot be found on an empty currentPath";
ClassDescriptor subclassOfCurrent = currentPath.get(currentPath.size() - 1);
assert subclassOfCurrent instanceof MutableClassDescriptor;
// Disconnect the loop
for (Iterator<JetType> iterator = ((MutableClassDescriptor) subclassOfCurrent).getSupertypes().iterator();
iterator.hasNext(); ) {
JetType type = iterator.next();
if (type.getConstructor() == currentClass.getTypeConstructor()) {
iterator.remove();
break;
}
}
}
return;
}
PsiElement psiElement = BindingContextUtils.classDescriptorToDeclaration(trace.getBindingContext(), classDescriptor);
beingProcessed.add(currentClass);
currentPath.add(currentClass);
for (JetType supertype : Lists.newArrayList(currentClass.getSupertypes())) {
DeclarationDescriptor declarationDescriptor = supertype.getConstructor().getDeclarationDescriptor();
if (declarationDescriptor instanceof MutableClassDescriptor) {
MutableClassDescriptor mutableClassDescriptor = (MutableClassDescriptor) declarationDescriptor;
traverseTypeHierarchy(mutableClassDescriptor, visited, beingProcessed, currentPath);
PsiElement elementToMark = null;
if (psiElement instanceof JetClassOrObject) {
JetClassOrObject classOrObject = (JetClassOrObject) psiElement;
for (JetDelegationSpecifier delegationSpecifier : classOrObject.getDelegationSpecifiers()) {
JetTypeReference typeReference = delegationSpecifier.getTypeReference();
if (typeReference == null) continue;
JetType supertype = trace.get(TYPE, typeReference);
if (supertype != null && supertype.getConstructor() == superclass.getTypeConstructor()) {
elementToMark = typeReference;
}
}
}
beingProcessed.remove(currentClass);
currentPath.remove(currentPath.size() - 1);
}
private void markCycleErrors(List<ClassDescriptor> currentPath, @NotNull ClassDescriptor current) {
int size = currentPath.size();
for (int i = size - 1; i >= 0; i--) {
ClassDescriptor classDescriptor = currentPath.get(i);
ClassDescriptor superclass = (i < size - 1) ? currentPath.get(i + 1) : current;
PsiElement psiElement = BindingContextUtils.classDescriptorToDeclaration(trace.getBindingContext(), classDescriptor);
PsiElement elementToMark = null;
if (psiElement instanceof JetClassOrObject) {
JetClassOrObject classOrObject = (JetClassOrObject) psiElement;
for (JetDelegationSpecifier delegationSpecifier : classOrObject.getDelegationSpecifiers()) {
JetTypeReference typeReference = delegationSpecifier.getTypeReference();
if (typeReference == null) continue;
JetType supertype = trace.get(TYPE, typeReference);
if (supertype != null && supertype.getConstructor() == superclass.getTypeConstructor()) {
elementToMark = typeReference;
}
}
}
if (elementToMark == null && psiElement instanceof PsiNameIdentifierOwner) {
PsiNameIdentifierOwner namedElement = (PsiNameIdentifierOwner) psiElement;
PsiElement nameIdentifier = namedElement.getNameIdentifier();
if (nameIdentifier != null) {
elementToMark = nameIdentifier;
}
}
if (elementToMark != null) {
trace.report(CYCLIC_INHERITANCE_HIERARCHY.on(elementToMark));
}
if (classDescriptor == current) {
// Beginning of cycle is found
break;
if (elementToMark == null && psiElement instanceof PsiNameIdentifierOwner) {
PsiNameIdentifierOwner namedElement = (PsiNameIdentifierOwner) psiElement;
PsiElement nameIdentifier = namedElement.getNameIdentifier();
if (nameIdentifier != null) {
elementToMark = nameIdentifier;
}
}
if (elementToMark != null) {
trace.report(CYCLIC_INHERITANCE_HIERARCHY.on(elementToMark));
}
}
private void checkTypesInClassHeaders(@NotNull TopDownAnalysisContext c) {
@@ -24,6 +24,7 @@ import kotlin.Function0;
import kotlin.Function1;
import kotlin.Unit;
import kotlin.KotlinPackage;
import org.jetbrains.annotations.Mutable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
@@ -33,6 +34,7 @@ import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.AnnotationResolver;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.TypeHierarchyResolver;
import org.jetbrains.jet.lang.resolve.lazy.ForceResolveUtil;
import org.jetbrains.jet.lang.resolve.lazy.LazyEntity;
import org.jetbrains.jet.lang.resolve.lazy.ResolveSession;
@@ -383,44 +385,65 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements LazyEnti
getVisibility();
}
private static class Supertypes {
@Mutable
public final Collection<JetType> trueSupertypes;
@Mutable
public final Collection<JetType> cyclicSupertypes;
private Supertypes(@Mutable @NotNull Collection<JetType> trueSupertypes) {
this(trueSupertypes, new ArrayList<JetType>(0));
}
private Supertypes(@Mutable @NotNull Collection<JetType> trueSupertypes, @Mutable @NotNull Collection<JetType> cyclicSupertypes) {
this.trueSupertypes = trueSupertypes;
this.cyclicSupertypes = cyclicSupertypes;
}
@NotNull
public Collection<JetType> getAllSupertypes() {
return KotlinPackage.plus(trueSupertypes, cyclicSupertypes);
}
}
private class LazyClassTypeConstructor implements LazyEntity, TypeConstructor {
private final NotNullLazyValue<Collection<JetType>> supertypes = resolveSession.getStorageManager().createLazyValueWithPostCompute(
new Function0<Collection<JetType>>() {
private final NotNullLazyValue<Supertypes> supertypes = resolveSession.getStorageManager().createLazyValueWithPostCompute(
new Function0<Supertypes>() {
@Override
public Collection<JetType> invoke() {
public Supertypes invoke() {
if (KotlinBuiltIns.isSpecialClassWithNoSupertypes(LazyClassDescriptor.this)) {
return Collections.emptyList();
return new Supertypes(Collections.<JetType>emptyList());
}
JetClassLikeInfo info = declarationProvider.getOwnerInfo();
if (info instanceof SyntheticClassObjectInfo) {
LazyClassDescriptor descriptor = ((SyntheticClassObjectInfo) info).getClassDescriptor();
if (descriptor.getKind().isSingleton()) {
return Collections.singleton(descriptor.getDefaultType());
return new Supertypes(Collections.singleton(descriptor.getDefaultType()));
}
}
JetClassOrObject classOrObject = info.getCorrespondingClassOrObject();
if (classOrObject == null) {
return Collections.singleton(KotlinBuiltIns.getInstance().getAnyType());
return new Supertypes(Collections.singleton(KotlinBuiltIns.getInstance().getAnyType()));
}
List<JetType> allSupertypes = resolveSession.getDescriptorResolver()
.resolveSupertypes(getScopeForClassHeaderResolution(), LazyClassDescriptor.this, classOrObject,
resolveSession.getTrace());
return Lists.newArrayList(Collections2.filter(allSupertypes, VALID_SUPERTYPE));
return new Supertypes(Lists.newArrayList(Collections2.filter(allSupertypes, VALID_SUPERTYPE)));
}
},
new Function1<Boolean, Collection<JetType>>() {
new Function1<Boolean, Supertypes>() {
@Override
public Collection<JetType> invoke(Boolean firstTime) {
return Collections.emptyList();
public Supertypes invoke(Boolean firstTime) {
return new Supertypes(Collections.<JetType>emptyList());
}
},
new Function1<Collection<JetType>, Unit>() {
new Function1<Supertypes, Unit>() {
@Override
public Unit invoke(@NotNull Collection<JetType> supertypes) {
public Unit invoke(@NotNull Supertypes supertypes) {
findAndDisconnectLoopsInTypeHierarchy(supertypes);
return Unit.VALUE;
}
@@ -460,21 +483,32 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements LazyEnti
@NotNull
@Override
public Collection<JetType> getSupertypes() {
return supertypes.invoke();
return supertypes.invoke().trueSupertypes;
}
private void findAndDisconnectLoopsInTypeHierarchy(Collection<JetType> supertypes) {
for (Iterator<JetType> iterator = supertypes.iterator(); iterator.hasNext(); ) {
private void findAndDisconnectLoopsInTypeHierarchy(Supertypes supertypes) {
for (Iterator<JetType> iterator = supertypes.trueSupertypes.iterator(); iterator.hasNext(); ) {
JetType supertype = iterator.next();
if (isReachable(supertype.getConstructor(), this, new HashSet<TypeConstructor>())) {
iterator.remove();
supertypes.cyclicSupertypes.add(supertype);
ClassifierDescriptor supertypeDescriptor = supertype.getConstructor().getDeclarationDescriptor();
if (supertypeDescriptor instanceof ClassDescriptor) {
ClassDescriptor superclass = (ClassDescriptor) supertypeDescriptor;
TypeHierarchyResolver.reportCyclicInheritanceHierarchyError(resolveSession.getTrace(), LazyClassDescriptor.this,
superclass);
}
}
}
}
private boolean isReachable(TypeConstructor from, TypeConstructor to, Set<TypeConstructor> visited) {
if (!visited.add(from)) return false;
for (JetType supertype : from.getSupertypes()) {
Collection<JetType> supertypes = from instanceof LazyClassTypeConstructor
? ((LazyClassTypeConstructor) from).supertypes.invoke().getAllSupertypes()
: from.getSupertypes();
for (JetType supertype : supertypes) {
TypeConstructor supertypeConstructor = supertype.getConstructor();
if (supertypeConstructor == to) {
return true;
@@ -2,10 +2,10 @@ trait A {
fun foo() {}
}
trait B : A, <!CYCLIC_INHERITANCE_HIERARCHY!>E<!> {}
trait C : B {}
trait C : <!CYCLIC_INHERITANCE_HIERARCHY!>B<!> {}
trait D : <!CYCLIC_INHERITANCE_HIERARCHY!>B<!> {}
trait E : <!CYCLIC_INHERITANCE_HIERARCHY!>F<!> {}
trait F : <!CYCLIC_INHERITANCE_HIERARCHY!>D<!>, C {}
trait F : <!CYCLIC_INHERITANCE_HIERARCHY!>D<!>, <!CYCLIC_INHERITANCE_HIERARCHY!>C<!> {}
trait G : F {}
trait H : F {}
@@ -21,10 +21,10 @@ val h : H? = null
fun test() {
a?.foo()
b?.foo()
c?.foo()
d?.foo()
c?.<!UNRESOLVED_REFERENCE!>foo<!>()
d?.<!UNRESOLVED_REFERENCE!>foo<!>()
e?.<!UNRESOLVED_REFERENCE!>foo<!>()
f?.foo()
g?.foo()
h?.foo()
}
f?.<!UNRESOLVED_REFERENCE!>foo<!>()
g?.<!UNRESOLVED_REFERENCE!>foo<!>()
h?.<!UNRESOLVED_REFERENCE!>foo<!>()
}
@@ -3,4 +3,4 @@ open class RecB<T>: <!CYCLIC_INHERITANCE_HIERARCHY!>RecA<T><!>()
open class SelfR<T>: <!CYCLIC_INHERITANCE_HIERARCHY!>SelfR<T><!>()
fun test(f: SelfR<String>) = f is <!CANNOT_CHECK_FOR_ERASED!>RecA<String><!>
fun test(f: RecB<String>) = f is RecA<String>
fun test(f: RecB<String>) = f is <!CANNOT_CHECK_FOR_ERASED!>RecA<String><!>
@@ -0,0 +1,10 @@
package test
trait A
trait B : A, E
trait C : B
trait D : B
trait E : F
trait F : D, C
trait G : F
trait H : F
@@ -0,0 +1,25 @@
package test
internal trait A {
}
internal trait B : test.A {
}
internal trait C {
}
internal trait D {
}
internal trait E {
}
internal trait F {
}
internal trait G : test.F {
}
internal trait H : test.F {
}
@@ -2515,6 +2515,11 @@ public class LazyResolveRecursiveComparingTestGenerated extends AbstractLazyReso
doTestCheckingPrimaryConstructors("compiler/testData/lazyResolve/recursiveComparator/classObjectHeader.kt");
}
@TestMetadata("CyclicHierarchy.kt")
public void testCyclicHierarchy() throws Exception {
doTestCheckingPrimaryConstructors("compiler/testData/lazyResolve/recursiveComparator/CyclicHierarchy.kt");
}
@TestMetadata("enum.kt")
public void testEnum() throws Exception {
doTestCheckingPrimaryConstructors("compiler/testData/lazyResolve/recursiveComparator/enum.kt");
+7 -7
View File
@@ -2,10 +2,10 @@ trait A {
fun foo() {}
}
trait B : A, <error>E</error> {}
trait C : B {}
trait C : <error>B</error> {}
trait D : <error>B</error> {}
trait E : <error>F</error> {}
trait F : <error>D</error>, C {}
trait F : <error>D</error>, <error>C</error> {}
trait G : F {}
trait H : F {}
@@ -21,10 +21,10 @@ val h : H? = null
fun test() {
a?.foo()
b?.foo()
c?.foo()
d?.foo()
c?.<error>foo</error>()
d?.<error>foo</error>()
e?.<error>foo</error>()
f?.foo()
g?.foo()
h?.foo()
f?.<error>foo</error>()
g?.<error>foo</error>()
h?.<error>foo</error>()
}