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.google.common.collect.Lists;
import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Computable;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.descriptors.*;
@@ -389,7 +390,8 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements LazyDesc
} }
private class LazyClassTypeConstructor implements LazyDescriptor, TypeConstructor { private class LazyClassTypeConstructor implements LazyDescriptor, TypeConstructor {
private final LazyValue<Collection<JetType>> supertypes = resolveSession.getStorageManager().createLazyValue(new Computable<Collection<JetType>>() { private final LazyValue<Collection<JetType>> supertypes = resolveSession.getStorageManager().createLazyValueWithPostCompute(
new Computable<Collection<JetType>>() {
@Override @Override
public Collection<JetType> compute() { public Collection<JetType> compute() {
if (resolveSession.isClassSpecial(DescriptorUtils.getFQName(LazyClassDescriptor.this))) { if (resolveSession.isClassSpecial(DescriptorUtils.getFQName(LazyClassDescriptor.this))) {
@@ -405,14 +407,19 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements LazyDesc
.resolveSupertypes(getScopeForClassHeaderResolution(), .resolveSupertypes(getScopeForClassHeaderResolution(),
LazyClassDescriptor.this, classOrObject, LazyClassDescriptor.this, classOrObject,
resolveSession.getTrace()); resolveSession.getTrace());
List<JetType> validSupertypes = Lists.newArrayList(Collections2.filter(allSupertypes, VALID_SUPERTYPE));
findAndDisconnectLoopsInTypeHierarchy(validSupertypes); return Lists.newArrayList(Collections2.filter(allSupertypes, VALID_SUPERTYPE));
return validSupertypes;
} }
} }
} }
},
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>>() { private final LazyValue<List<TypeParameterDescriptor>> parameters = resolveSession.getStorageManager().createLazyValue(new Computable<List<TypeParameterDescriptor>>() {
@Override @Override
public List<TypeParameterDescriptor> compute() { public List<TypeParameterDescriptor> compute() {
@@ -440,7 +447,7 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements LazyDesc
return supertypes.get(); return supertypes.get();
} }
private void findAndDisconnectLoopsInTypeHierarchy(List<JetType> supertypes) { private void findAndDisconnectLoopsInTypeHierarchy(Collection<JetType> supertypes) {
for (Iterator<JetType> iterator = supertypes.iterator(); iterator.hasNext(); ) { for (Iterator<JetType> iterator = supertypes.iterator(); iterator.hasNext(); ) {
JetType supertype = iterator.next(); JetType supertype = iterator.next();
if (isReachable(supertype.getConstructor(), this, new HashSet<TypeConstructor>())) { if (isReachable(supertype.getConstructor(), this, new HashSet<TypeConstructor>())) {
@@ -17,6 +17,7 @@
package org.jetbrains.jet.lang.resolve.lazy; package org.jetbrains.jet.lang.resolve.lazy;
import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Computable;
import com.intellij.util.Consumer;
import com.intellij.util.Function; import com.intellij.util.Function;
import com.intellij.util.containers.ConcurrentWeakValueHashMap; import com.intellij.util.containers.ConcurrentWeakValueHashMap;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -106,12 +107,37 @@ public class LockBasedStorageManager implements StorageManager {
return new LockBasedLazyValue<T>(lock, computable); 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 @NotNull
@Override @Override
public <T> LazyValue<T> createNullableLazyValue(@NotNull Computable<T> computable) { public <T> LazyValue<T> createNullableLazyValue(@NotNull Computable<T> computable) {
return new LockBasedNullableLazyValue<T>(lock, 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 @NotNull
@Override @Override
public BindingTrace createSafeTrace(@NotNull BindingTrace originalTrace) { public BindingTrace createSafeTrace(@NotNull BindingTrace originalTrace) {
@@ -145,10 +171,15 @@ public class LockBasedStorageManager implements StorageManager {
if (_value == null) { if (_value == null) {
_value = computable.compute(); _value = computable.compute();
value = _value; value = _value;
postCompute(_value);
} }
return _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> { private static class LockBasedNullableLazyValue<T> implements LazyValue<T> {
@@ -183,11 +214,17 @@ public class LockBasedStorageManager implements StorageManager {
value = _value; value = _value;
computed = true; computed = true;
postCompute(_value);
return _value; return _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 { private static class LockProtectedTrace implements BindingTrace {
@@ -17,6 +17,7 @@
package org.jetbrains.jet.lang.resolve.lazy; package org.jetbrains.jet.lang.resolve.lazy;
import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Computable;
import com.intellij.util.Consumer;
import com.intellij.util.Function; import com.intellij.util.Function;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.resolve.BindingTrace; import org.jetbrains.jet.lang.resolve.BindingTrace;
@@ -35,9 +36,23 @@ public interface StorageManager {
@NotNull @NotNull
<T> LazyValue<T> createLazyValue(@NotNull Computable<T> computable); <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 @NotNull
<T> LazyValue<T> createNullableLazyValue(@NotNull Computable<T> computable); <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 @NotNull
BindingTrace createSafeTrace(@NotNull BindingTrace originalTrace); 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"); doTest("idea/testData/checker/TraitSupertypeList.kt");
} }
@TestMetadata("trivialHierarchyLoop.kt")
public void testTrivialHierarchyLoop() throws Exception {
doTest("idea/testData/checker/trivialHierarchyLoop.kt");
}
@TestMetadata("UnreachableCode.kt") @TestMetadata("UnreachableCode.kt")
public void testUnreachableCode() throws Exception { public void testUnreachableCode() throws Exception {
doTest("idea/testData/checker/UnreachableCode.kt"); doTest("idea/testData/checker/UnreachableCode.kt");