KT-303 Stack overflow on a cyclic class hierarchy

This commit is contained in:
Andrey Breslav
2011-09-13 17:17:45 +04:00
parent 261f88c215
commit 910ba57f8a
6 changed files with 222 additions and 42 deletions
@@ -125,9 +125,8 @@ public class MutableClassDescriptor extends MutableDeclarationDescriptor impleme
public void addSupertype(@NotNull JetType supertype) {
if (!ErrorUtils.isErrorType(supertype)) {
scopeForMemberLookup.importScope(supertype.getMemberScope());
supertypes.add(supertype);
}
supertypes.add(supertype);
}
public void setTypeParameterDescriptors(List<TypeParameterDescriptor> typeParameters) {
@@ -166,6 +165,12 @@ public class MutableClassDescriptor extends MutableDeclarationDescriptor impleme
}
}
public void addSupertypesToScopeForMemberLookup() {
for (JetType supertype : supertypes) {
scopeForMemberLookup.importScope(supertype.getMemberScope());
}
}
@NotNull
@Override
public JetScope getMemberScope(List<TypeProjection> typeArguments) {
@@ -278,4 +283,8 @@ public class MutableClassDescriptor extends MutableDeclarationDescriptor impleme
public String toString() {
return DescriptorRenderer.TEXT.render(this) + "[" + getClass().getCanonicalName() + "@" + System.identityHashCode(this) + "]";
}
public Collection<JetType> getSupertypes() {
return supertypes;
}
}
@@ -66,28 +66,52 @@ public class ClassDescriptorResolver {
public void resolveSupertypes(@NotNull JetClassOrObject jetClass, @NotNull MutableClassDescriptor descriptor) {
List<JetDelegationSpecifier> delegationSpecifiers = jetClass.getDelegationSpecifiers();
JetType defaultSupertype = JetStandardClasses.getAnyType();
if (delegationSpecifiers.isEmpty()) {
descriptor.addSupertype(getDefaultSupertype(jetClass));
}
else {
Collection<JetType> supertypes = resolveDelegationSpecifiers(
descriptor.getScopeForSupertypeResolution(),
delegationSpecifiers,
typeResolverNotCheckingBounds);
for (JetType supertype : supertypes) {
descriptor.addSupertype(supertype);
}
}
}
private JetType getDefaultSupertype(JetClassOrObject jetClass) {
// TODO : beautify
if (jetClass instanceof JetEnumEntry) {
JetClassOrObject parent = PsiTreeUtil.getParentOfType(jetClass, JetClassOrObject.class);
ClassDescriptor parentDescriptor = trace.getBindingContext().get(BindingContext.CLASS, parent);
if (parentDescriptor.getTypeConstructor().getParameters().isEmpty()) {
defaultSupertype = parentDescriptor.getDefaultType();
return parentDescriptor.getDefaultType();
}
else if (delegationSpecifiers.isEmpty()) {
else {
trace.getErrorHandler().genericError(((JetEnumEntry) jetClass).getNameIdentifier().getNode(), "generic arguments of the base type must be specified");
return ErrorUtils.createErrorType("Supertype not specified");
}
}
Collection<? extends JetType> supertypes = delegationSpecifiers.isEmpty()
? Collections.singleton(defaultSupertype)
: resolveDelegationSpecifiers(
descriptor.getScopeForSupertypeResolution(),
delegationSpecifiers,
typeResolverNotCheckingBounds);
return JetStandardClasses.getAnyType();
}
for (JetType supertype : supertypes) {
descriptor.addSupertype(supertype);
public Collection<JetType> resolveDelegationSpecifiers(JetScope extensibleScope, List<JetDelegationSpecifier> delegationSpecifiers, @NotNull TypeResolver resolver) {
if (delegationSpecifiers.isEmpty()) {
return Collections.emptyList();
}
Collection<JetType> result = Lists.newArrayList();
for (JetDelegationSpecifier delegationSpecifier : delegationSpecifiers) {
JetTypeReference typeReference = delegationSpecifier.getTypeReference();
if (typeReference != null) {
result.add(resolver.resolveType(extensibleScope, typeReference));
}
else {
result.add(ErrorUtils.createErrorType("No type reference"));
}
}
return result;
}
@NotNull
@@ -315,23 +339,6 @@ public class ClassDescriptorResolver {
return jetType;
}
public Collection<JetType> resolveDelegationSpecifiers(JetScope extensibleScope, List<JetDelegationSpecifier> delegationSpecifiers, @NotNull TypeResolver resolver) {
if (delegationSpecifiers.isEmpty()) {
return Collections.emptyList();
}
Collection<JetType> result = new ArrayList<JetType>();
for (JetDelegationSpecifier delegationSpecifier : delegationSpecifiers) {
JetTypeReference typeReference = delegationSpecifier.getTypeReference();
if (typeReference != null) {
result.add(resolver.resolveType(extensibleScope, typeReference));
}
else {
result.add(ErrorUtils.createErrorType("No type reference"));
}
}
return result;
}
@NotNull
public VariableDescriptor resolveLocalVariableDescriptor(@NotNull DeclarationDescriptor containingDeclaration, @NotNull JetScope scope, @NotNull JetParameter parameter) {
JetType type = resolveParameterType(scope, parameter);
@@ -1,5 +1,10 @@
package org.jetbrains.jet.lang.resolve;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNameIdentifierOwner;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.cfg.JetFlowInformationProvider;
import org.jetbrains.jet.lang.descriptors.*;
@@ -9,10 +14,10 @@ import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.JetTypeInferrer;
import org.jetbrains.jet.lexer.JetTokens;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.*;
import static org.jetbrains.jet.lang.resolve.BindingContext.DESCRIPTOR_TO_DECLARATION;
import static org.jetbrains.jet.lang.resolve.BindingContext.TYPE;
/**
* @author abreslav
@@ -27,8 +32,16 @@ public class TypeHierarchyResolver {
public void process(@NotNull JetScope outerScope, NamespaceLike owner, @NotNull List<JetDeclaration> declarations) {
collectNamespacesAndClassifiers(outerScope, owner, declarations); // namespaceScopes, classes
createTypeConstructors(); // create type constructors for classes and generic parameters
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)
// Detect and disconnect all loops in the hierarchy
detectAndDisconnectLoops();
// Add supertypes to resolution scopes of classes
addSupertypesToScopes();
checkTypesInClassHeaders(); // Generic bounds and supertype lists
}
@@ -256,6 +269,115 @@ public class TypeHierarchyResolver {
}
}
private void detectAndDisconnectLoops() {
// 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();
Set<ClassDescriptor> visited = Sets.newHashSet();
for (MutableClassDescriptor mutableClassDescriptor : context.getClasses().values()) {
topologicallySort(mutableClassDescriptor, visited, topologicalOrder);
}
// Loop detection and disconnection
visited.clear();
Set<ClassDescriptor> beingProcessed = Sets.newHashSet();
List<ClassDescriptor> currentPath = Lists.newArrayList();
for (MutableClassDescriptor mutableClassDescriptor : topologicalOrder) {
traverseTypeHierarchy(mutableClassDescriptor, visited, beingProcessed, currentPath);
}
}
private static void topologicallySort(MutableClassDescriptor mutableClassDescriptor, Set<ClassDescriptor> visited, LinkedList<MutableClassDescriptor> topologicalOrder) {
if (!visited.add(mutableClassDescriptor)) {
return;
}
for (JetType supertype : mutableClassDescriptor.getSupertypes()) {
DeclarationDescriptor declarationDescriptor = supertype.getConstructor().getDeclarationDescriptor();
if (declarationDescriptor instanceof MutableClassDescriptor) {
MutableClassDescriptor classDescriptor = (MutableClassDescriptor) declarationDescriptor;
topologicallySort(classDescriptor, visited, topologicalOrder);
}
}
topologicalOrder.addFirst(mutableClassDescriptor);
}
private void traverseTypeHierarchy(MutableClassDescriptor currentClass, Set<ClassDescriptor> visited, Set<ClassDescriptor> beingProcessed, List<ClassDescriptor> currentPath) {
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;
}
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);
}
}
beingProcessed.remove(currentClass);
currentPath.remove(currentPath.size() - 1);
}
private void markCycleErrors(List<ClassDescriptor> currentPath, @NotNull ClassDescriptor current) {
int size = currentPath.size();
boolean found = false;
for (int i = 0; i < size; i++) {
ClassDescriptor classDescriptor = currentPath.get(i);
if (classDescriptor == current) found = true;
if (!found) continue;
ClassDescriptor superclass = (i < size - 1) ? currentPath.get(i + 1) : current;
PsiElement psiElement = context.getTrace().get(DESCRIPTOR_TO_DECLARATION, classDescriptor);
ASTNode node = null;
if (psiElement instanceof JetClassOrObject) {
JetClassOrObject classOrObject = (JetClassOrObject) psiElement;
for (JetDelegationSpecifier delegationSpecifier : classOrObject.getDelegationSpecifiers()) {
JetTypeReference typeReference = delegationSpecifier.getTypeReference();
if (typeReference == null) continue;
JetType supertype = context.getTrace().get(TYPE, typeReference);
if (supertype != null && supertype.getConstructor() == superclass.getTypeConstructor()) {
node = typeReference.getNode();
}
}
}
if (node == null && psiElement instanceof PsiNameIdentifierOwner) {
PsiNameIdentifierOwner namedElement = (PsiNameIdentifierOwner) psiElement;
PsiElement nameIdentifier = namedElement.getNameIdentifier();
if (nameIdentifier != null) {
node = nameIdentifier.getNode();
}
}
if (node != null) {
context.getTrace().getErrorHandler().genericError(node, "There's a cycle in the inheritance hierarchy for this type");
}
}
}
private void addSupertypesToScopes() {
for (MutableClassDescriptor mutableClassDescriptor : context.getClasses().values()) {
mutableClassDescriptor.addSupertypesToScopeForMemberLookup();
}
for (MutableClassDescriptor mutableClassDescriptor : context.getObjects().values()) {
mutableClassDescriptor.addSupertypesToScopeForMemberLookup();
}
}
private void checkTypesInClassHeaders() {
for (Map.Entry<JetClass, MutableClassDescriptor> entry : context.getClasses().entrySet()) {
JetClass jetClass = entry.getKey();
@@ -263,7 +385,7 @@ public class TypeHierarchyResolver {
for (JetDelegationSpecifier delegationSpecifier : jetClass.getDelegationSpecifiers()) {
JetTypeReference typeReference = delegationSpecifier.getTypeReference();
if (typeReference != null) {
JetType type = context.getTrace().getBindingContext().get(BindingContext.TYPE, typeReference);
JetType type = context.getTrace().getBindingContext().get(TYPE, typeReference);
if (type != null) {
context.getClassDescriptorResolver().checkBounds(typeReference, type);
}
@@ -273,7 +395,7 @@ public class TypeHierarchyResolver {
for (JetTypeParameter jetTypeParameter : jetClass.getTypeParameters()) {
JetTypeReference extendsBound = jetTypeParameter.getExtendsBound();
if (extendsBound != null) {
JetType type = context.getTrace().getBindingContext().get(BindingContext.TYPE, extendsBound);
JetType type = context.getTrace().getBindingContext().get(TYPE, extendsBound);
if (type != null) {
context.getClassDescriptorResolver().checkBounds(extendsBound, type);
}
@@ -283,7 +405,7 @@ public class TypeHierarchyResolver {
for (JetTypeConstraint constraint : jetClass.getTypeConstaints()) {
JetTypeReference extendsBound = constraint.getBoundTypeReference();
if (extendsBound != null) {
JetType type = context.getTrace().getBindingContext().get(BindingContext.TYPE, extendsBound);
JetType type = context.getTrace().getBindingContext().get(TYPE, extendsBound);
if (type != null) {
context.getClassDescriptorResolver().checkBounds(extendsBound, type);
}
@@ -2,13 +2,14 @@ package org.jetbrains.jet.lang.types;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotatedImpl;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotatedImpl;
import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
@@ -34,8 +35,8 @@ public class TypeConstructorImpl extends AnnotatedImpl implements TypeConstructo
this.declarationDescriptor = declarationDescriptor;
this.sealed = sealed;
this.debugName = debugName;
this.parameters = new ArrayList<TypeParameterDescriptor>(parameters);
this.supertypes = supertypes;
this.parameters = Collections.unmodifiableList(new ArrayList<TypeParameterDescriptor>(parameters));
this.supertypes = Collections.unmodifiableCollection(supertypes);
}
@Override
+30
View File
@@ -0,0 +1,30 @@
open trait A {
fun foo() {}
}
open trait B : A, <error>E</error> {}
open trait C : B {}
open trait D : <error>B</error> {}
open trait E : <error>F</error> {}
open trait F : <error>D</error>, C {}
open trait G : F {}
open trait H : F {}
val a : A? = null
val b : B? = null
val c : C? = null
val d : D? = null
val e : E? = null
val f : F? = null
val g : G? = null
val h : H? = null
fun test() {
a?.foo()
b?.foo()
c?.foo()
d?.foo()
e?.<error>foo</error>()
f?.foo()
g?.foo()
h?.foo()
}
@@ -0,0 +1,11 @@
// KT-303 Stack overflow on a cyclic class hierarchy
open class Foo() : <error>Bar</error>() {
val a : Int = 1
}
open class Bar() : <error>Foo</error>() {
}
val x : Int = <error>Foo()</error>