Infinite recursion prevented on cyclic class hierarchies

This commit is contained in:
Andrey Breslav
2013-02-11 19:01:35 +04:00
parent 71bad95783
commit 3e0ead494e
5 changed files with 90 additions and 23 deletions
@@ -22,6 +22,7 @@ import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.PsiElement;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
@@ -389,30 +390,36 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements LazyDesc
}
private class LazyClassTypeConstructor implements LazyDescriptor, TypeConstructor {
private final LazyValue<Collection<JetType>> supertypes = resolveSession.getStorageManager().createLazyValue(new Computable<Collection<JetType>>() {
@Override
public Collection<JetType> compute() {
if (resolveSession.isClassSpecial(DescriptorUtils.getFQName(LazyClassDescriptor.this))) {
return Collections.emptyList();
}
else {
JetClassOrObject classOrObject = declarationProvider.getOwnerInfo().getCorrespondingClassOrObject();
if (classOrObject == null) {
return Collections.emptyList();
}
else {
List<JetType> allSupertypes = resolveSession.getInjector().getDescriptorResolver()
.resolveSupertypes(getScopeForClassHeaderResolution(),
LazyClassDescriptor.this, classOrObject,
resolveSession.getTrace());
List<JetType> validSupertypes = Lists.newArrayList(Collections2.filter(allSupertypes, VALID_SUPERTYPE));
private final LazyValue<Collection<JetType>> supertypes = resolveSession.getStorageManager().createLazyValueWithPostCompute(
new Computable<Collection<JetType>>() {
@Override
public Collection<JetType> compute() {
if (resolveSession.isClassSpecial(DescriptorUtils.getFQName(LazyClassDescriptor.this))) {
return Collections.emptyList();
}
else {
JetClassOrObject classOrObject = declarationProvider.getOwnerInfo().getCorrespondingClassOrObject();
if (classOrObject == null) {
return Collections.emptyList();
}
else {
List<JetType> allSupertypes = resolveSession.getInjector().getDescriptorResolver()
.resolveSupertypes(getScopeForClassHeaderResolution(),
LazyClassDescriptor.this, classOrObject,
resolveSession.getTrace());
findAndDisconnectLoopsInTypeHierarchy(validSupertypes);
return validSupertypes;
return Lists.newArrayList(Collections2.filter(allSupertypes, VALID_SUPERTYPE));
}
}
}
}
}
});
},
new Consumer<Collection<JetType>>() {
@Override
public void consume(@NotNull Collection<JetType> supertypes) {
findAndDisconnectLoopsInTypeHierarchy(supertypes);
}
});
private final LazyValue<List<TypeParameterDescriptor>> parameters = resolveSession.getStorageManager().createLazyValue(new Computable<List<TypeParameterDescriptor>>() {
@Override
public List<TypeParameterDescriptor> compute() {
@@ -440,7 +447,7 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements LazyDesc
return supertypes.get();
}
private void findAndDisconnectLoopsInTypeHierarchy(List<JetType> supertypes) {
private void findAndDisconnectLoopsInTypeHierarchy(Collection<JetType> supertypes) {
for (Iterator<JetType> iterator = supertypes.iterator(); iterator.hasNext(); ) {
JetType supertype = iterator.next();
if (isReachable(supertype.getConstructor(), this, new HashSet<TypeConstructor>())) {
@@ -17,6 +17,7 @@
package org.jetbrains.jet.lang.resolve.lazy;
import com.intellij.openapi.util.Computable;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.containers.ConcurrentWeakValueHashMap;
import org.jetbrains.annotations.NotNull;
@@ -106,12 +107,37 @@ public class LockBasedStorageManager implements StorageManager {
return new LockBasedLazyValue<T>(lock, computable);
}
@NotNull
@Override
public <T> LazyValue<T> createLazyValueWithPostCompute(@NotNull Computable<T> computable, @NotNull final Consumer<T> postCompute) {
return new LockBasedLazyValue<T>(lock, computable) {
@Override
protected void postCompute(@NotNull T value) {
postCompute.consume(value);
}
};
}
@NotNull
@Override
public <T> LazyValue<T> createNullableLazyValue(@NotNull Computable<T> computable) {
return new LockBasedNullableLazyValue<T>(lock, computable);
}
@NotNull
@Override
public <T> LazyValue<T> createNullableLazyValueWithPostCompute(
@NotNull Computable<T> computable,
@NotNull final Consumer<T> postCompute
) {
return new LockBasedNullableLazyValue<T>(lock, computable) {
@Override
protected void postCompute(@Nullable T value) {
postCompute.consume(value);
}
};
}
@NotNull
@Override
public BindingTrace createSafeTrace(@NotNull BindingTrace originalTrace) {
@@ -145,10 +171,15 @@ public class LockBasedStorageManager implements StorageManager {
if (_value == null) {
_value = computable.compute();
value = _value;
postCompute(_value);
}
return _value;
}
}
protected void postCompute(@NotNull T value) {
// Doing something in post-compute helps prevent infinite recursion
}
}
private static class LockBasedNullableLazyValue<T> implements LazyValue<T> {
@@ -183,11 +214,17 @@ public class LockBasedStorageManager implements StorageManager {
value = _value;
computed = true;
postCompute(_value);
return _value;
}
return value;
}
}
protected void postCompute(@Nullable T value) {
// Doing something in post-compute helps prevent infinite recursion
}
}
private static class LockProtectedTrace implements BindingTrace {
@@ -17,6 +17,7 @@
package org.jetbrains.jet.lang.resolve.lazy;
import com.intellij.openapi.util.Computable;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.resolve.BindingTrace;
@@ -35,9 +36,23 @@ public interface StorageManager {
@NotNull
<T> LazyValue<T> createLazyValue(@NotNull Computable<T> computable);
/**
* {@code postCompute} is called after the value is computed, but before any other thread sees it (the current thread may
* see it in between)
*/
@NotNull
<T> LazyValue<T> createLazyValueWithPostCompute(@NotNull Computable<T> computable, @NotNull Consumer<T> postCompute);
@NotNull
<T> LazyValue<T> createNullableLazyValue(@NotNull Computable<T> computable);
/**
* {@code postCompute} is called after the value is computed, but before any other thread sees it (the current thread may
* see it in between)
*/
@NotNull
<T> LazyValue<T> createNullableLazyValueWithPostCompute(@NotNull Computable<T> computable, @NotNull Consumer<T> postCompute);
@NotNull
BindingTrace createSafeTrace(@NotNull BindingTrace originalTrace);
@@ -0,0 +1,3 @@
class A : <error>A</error>() {}
val x : Int = <error>A()</error>
@@ -243,6 +243,11 @@ public class JetPsiCheckerTestGenerated extends AbstractJetPsiCheckerTest {
doTest("idea/testData/checker/TraitSupertypeList.kt");
}
@TestMetadata("trivialHierarchyLoop.kt")
public void testTrivialHierarchyLoop() throws Exception {
doTest("idea/testData/checker/trivialHierarchyLoop.kt");
}
@TestMetadata("UnreachableCode.kt")
public void testUnreachableCode() throws Exception {
doTest("idea/testData/checker/UnreachableCode.kt");