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.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,30 +390,36 @@ 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(
|
||||||
@Override
|
new Computable<Collection<JetType>>() {
|
||||||
public Collection<JetType> compute() {
|
@Override
|
||||||
if (resolveSession.isClassSpecial(DescriptorUtils.getFQName(LazyClassDescriptor.this))) {
|
public Collection<JetType> compute() {
|
||||||
return Collections.emptyList();
|
if (resolveSession.isClassSpecial(DescriptorUtils.getFQName(LazyClassDescriptor.this))) {
|
||||||
}
|
return Collections.emptyList();
|
||||||
else {
|
}
|
||||||
JetClassOrObject classOrObject = declarationProvider.getOwnerInfo().getCorrespondingClassOrObject();
|
else {
|
||||||
if (classOrObject == null) {
|
JetClassOrObject classOrObject = declarationProvider.getOwnerInfo().getCorrespondingClassOrObject();
|
||||||
return Collections.emptyList();
|
if (classOrObject == null) {
|
||||||
}
|
return Collections.emptyList();
|
||||||
else {
|
}
|
||||||
List<JetType> allSupertypes = resolveSession.getInjector().getDescriptorResolver()
|
else {
|
||||||
.resolveSupertypes(getScopeForClassHeaderResolution(),
|
List<JetType> allSupertypes = resolveSession.getInjector().getDescriptorResolver()
|
||||||
LazyClassDescriptor.this, classOrObject,
|
.resolveSupertypes(getScopeForClassHeaderResolution(),
|
||||||
resolveSession.getTrace());
|
LazyClassDescriptor.this, classOrObject,
|
||||||
List<JetType> validSupertypes = Lists.newArrayList(Collections2.filter(allSupertypes, VALID_SUPERTYPE));
|
resolveSession.getTrace());
|
||||||
|
|
||||||
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>())) {
|
||||||
|
|||||||
+37
@@ -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");
|
||||||
|
|||||||
Reference in New Issue
Block a user