Consider outer classes when topsorting hierarchy

- when building a topsorted hierarchy, consider not only edges to the class'
  superclasses, but also from inner/nested classes to their container. This
  effectively means that a class cannot inherit from its nested class. Similar
  cases (see the tests) are disallowed in Java.
- filter classes by their presence in TopDownAnalysisContext, not by being a
  MutableClassDescriptor (otherwise analysis for local classes ends up trying
  to do something with outer superclasses)
- delete outdated comment: topsort is needed not only for better diagnostics,
  but also for correct order of fake overrides construction
This commit is contained in:
Alexander Udalov
2014-04-02 22:51:26 +04:00
parent 9b2291d381
commit cbe6e49a93
6 changed files with 81 additions and 34 deletions
@@ -16,10 +16,8 @@
package org.jetbrains.jet.lang.resolve;
import com.google.common.collect.Lists;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNameIdentifierOwner;
import kotlin.KotlinPackage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
@@ -32,7 +30,6 @@ 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;
@@ -49,6 +46,25 @@ import static org.jetbrains.jet.lang.resolve.ModifiersChecker.resolveVisibilityF
import static org.jetbrains.jet.lang.resolve.name.SpecialNames.getClassObjectName;
public class TypeHierarchyResolver {
private static final DFS.Neighbors<ClassDescriptor> CLASS_INHERITANCE_EDGES = new DFS.Neighbors<ClassDescriptor>() {
@NotNull
@Override
public Iterable<ClassDescriptor> getNeighbors(ClassDescriptor current) {
List<ClassDescriptor> result = new ArrayList<ClassDescriptor>();
for (JetType supertype : current.getDefaultType().getConstructor().getSupertypes()) {
DeclarationDescriptor descriptor = supertype.getConstructor().getDeclarationDescriptor();
if (descriptor instanceof ClassDescriptor) {
result.add((ClassDescriptor) descriptor);
}
}
DeclarationDescriptor container = current.getContainingDeclaration();
if (container instanceof ClassDescriptor) {
result.add((ClassDescriptor) container);
}
return result;
}
};
@NotNull
private ImportsResolver importsResolver;
@NotNull
@@ -220,28 +236,12 @@ public class TypeHierarchyResolver {
}
@NotNull
@SuppressWarnings("unchecked")
private static List<MutableClassDescriptor> topologicallySortClassesAndObjects(@NotNull TopDownAnalysisContext c) {
// A topsort is needed only for better diagnostics:
// edges that get removed to disconnect loops are more reasonable in this case
//noinspection unchecked
List<ClassDescriptor> orderedClasses = DFS.topologicalOrder(
(Iterable) c.getAllClasses(),
new DFS.Neighbors<ClassDescriptor>() {
@NotNull
@Override
public Iterable<ClassDescriptor> getNeighbors(ClassDescriptor current) {
List<ClassDescriptor> result = Lists.newArrayList();
for (JetType supertype : current.getDefaultType().getConstructor().getSupertypes()) {
DeclarationDescriptor descriptor = supertype.getConstructor().getDeclarationDescriptor();
if (descriptor instanceof ClassDescriptor) {
result.add((ClassDescriptor) descriptor);
}
}
return result;
}
}
);
return KotlinPackage.filterIsInstance(orderedClasses, MutableClassDescriptor.class);
Collection<ClassDescriptor> sourceClasses = (Collection) c.getAllClasses();
List<ClassDescriptor> allClassesOrdered = DFS.topologicalOrder(sourceClasses, CLASS_INHERITANCE_EDGES);
allClassesOrdered.retainAll(sourceClasses);
return (List) allClassesOrdered;
}
private void detectAndDisconnectLoops(@NotNull TopDownAnalysisContext c) {
@@ -269,22 +269,15 @@ public class TypeHierarchyResolver {
}
}
// Temporary. Duplicates logic from LazyClassTypeConstructor.isReachable
// TODO: use DFS and copy to LazyClassTypeConstructor.isReachable
private static boolean isReachable(
@NotNull ClassDescriptor from,
@NotNull MutableClassDescriptor to,
@NotNull Set<ClassDescriptor> visited
) {
if (!visited.add(from)) return false;
for (JetType supertype : from.getDefaultType().getConstructor().getSupertypes()) {
TypeConstructor supertypeConstructor = supertype.getConstructor();
if (supertypeConstructor.getDeclarationDescriptor() == to) {
return true;
}
ClassifierDescriptor superclass = supertypeConstructor.getDeclarationDescriptor();
if (superclass instanceof ClassDescriptor && isReachable((ClassDescriptor) superclass, to, visited)) {
return true;
}
for (ClassDescriptor superclass : CLASS_INHERITANCE_EDGES.getNeighbors(from)) {
if (superclass == to || isReachable(superclass, to, visited)) return true;
}
return false;
}
@@ -0,0 +1,4 @@
open class C : <!CYCLIC_INHERITANCE_HIERARCHY!>D<!>() {
open class CC
}
open class D : <!CYCLIC_INHERITANCE_HIERARCHY!>C.CC<!>()
@@ -0,0 +1,3 @@
open class E : <!CYCLIC_INHERITANCE_HIERARCHY!>E.EE<!>() {
open class EE
}
@@ -0,0 +1,21 @@
// FILE: ExceptionTracker.kt
trait ExceptionTracker : <!CYCLIC_INHERITANCE_HIERARCHY!>LockBasedStorageManager.ExceptionHandlingStrategy<!> {
}
// FILE: StorageManager.kt
trait StorageManager : <!CYCLIC_INHERITANCE_HIERARCHY!>ExceptionTracker<!> {
fun foo()
}
// FILE: LockBasedStorageManager.java
class LockBasedStorageManager extends StorageManager {
interface ExceptionHandlingStrategy {
void bar();
}
@Override
void foo() {}
}
@@ -0,0 +1,6 @@
open class A : <!CYCLIC_INHERITANCE_HIERARCHY!>B.BB<!>() {
open class AA
}
open class B : <!CYCLIC_INHERITANCE_HIERARCHY!>A.AA<!>() {
open class BB
}
@@ -1823,6 +1823,16 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/diagnostics/tests/cyclicHierarchy"), Pattern.compile("^(.+)\\.kt$"), true);
}
@TestMetadata("classIndirectlyInheritsNested.kt")
public void testClassIndirectlyInheritsNested() throws Exception {
doTest("compiler/testData/diagnostics/tests/cyclicHierarchy/classIndirectlyInheritsNested.kt");
}
@TestMetadata("classInheritsNested.kt")
public void testClassInheritsNested() throws Exception {
doTest("compiler/testData/diagnostics/tests/cyclicHierarchy/classInheritsNested.kt");
}
@TestMetadata("cyclicHierarchy.kt")
public void testCyclicHierarchy() throws Exception {
doTest("compiler/testData/diagnostics/tests/cyclicHierarchy/cyclicHierarchy.kt");
@@ -1843,11 +1853,21 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
doTest("compiler/testData/diagnostics/tests/cyclicHierarchy/kotlinJavaKotlinCycle.kt");
}
@TestMetadata("kotlinJavaNestedCycle.kt")
public void testKotlinJavaNestedCycle() throws Exception {
doTest("compiler/testData/diagnostics/tests/cyclicHierarchy/kotlinJavaNestedCycle.kt");
}
@TestMetadata("kt303.kt")
public void testKt303() throws Exception {
doTest("compiler/testData/diagnostics/tests/cyclicHierarchy/kt303.kt");
}
@TestMetadata("twoClassesWithNestedCycle.kt")
public void testTwoClassesWithNestedCycle() throws Exception {
doTest("compiler/testData/diagnostics/tests/cyclicHierarchy/twoClassesWithNestedCycle.kt");
}
}
@TestMetadata("compiler/testData/diagnostics/tests/dataClasses")