Infinite recursion prevented on cyclic class hierarchies
This commit is contained in:
+30
-23
@@ -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>())) {
|
||||
|
||||
+37
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user