diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LazyClassDescriptor.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LazyClassDescriptor.java index 1067bf0d781..dad73c44acc 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LazyClassDescriptor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LazyClassDescriptor.java @@ -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> supertypes = resolveSession.getStorageManager().createLazyValue(new Computable>() { - @Override - public Collection 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 allSupertypes = resolveSession.getInjector().getDescriptorResolver() - .resolveSupertypes(getScopeForClassHeaderResolution(), - LazyClassDescriptor.this, classOrObject, - resolveSession.getTrace()); - List validSupertypes = Lists.newArrayList(Collections2.filter(allSupertypes, VALID_SUPERTYPE)); + private final LazyValue> supertypes = resolveSession.getStorageManager().createLazyValueWithPostCompute( + new Computable>() { + @Override + public Collection 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 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>() { + @Override + public void consume(@NotNull Collection supertypes) { + findAndDisconnectLoopsInTypeHierarchy(supertypes); + } + }); + private final LazyValue> parameters = resolveSession.getStorageManager().createLazyValue(new Computable>() { @Override public List compute() { @@ -440,7 +447,7 @@ public class LazyClassDescriptor extends ClassDescriptorBase implements LazyDesc return supertypes.get(); } - private void findAndDisconnectLoopsInTypeHierarchy(List supertypes) { + private void findAndDisconnectLoopsInTypeHierarchy(Collection supertypes) { for (Iterator iterator = supertypes.iterator(); iterator.hasNext(); ) { JetType supertype = iterator.next(); if (isReachable(supertype.getConstructor(), this, new HashSet())) { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LockBasedStorageManager.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LockBasedStorageManager.java index c9f61bd8188..d2e327994eb 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LockBasedStorageManager.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/LockBasedStorageManager.java @@ -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(lock, computable); } + @NotNull + @Override + public LazyValue createLazyValueWithPostCompute(@NotNull Computable computable, @NotNull final Consumer postCompute) { + return new LockBasedLazyValue(lock, computable) { + @Override + protected void postCompute(@NotNull T value) { + postCompute.consume(value); + } + }; + } + @NotNull @Override public LazyValue createNullableLazyValue(@NotNull Computable computable) { return new LockBasedNullableLazyValue(lock, computable); } + @NotNull + @Override + public LazyValue createNullableLazyValueWithPostCompute( + @NotNull Computable computable, + @NotNull final Consumer postCompute + ) { + return new LockBasedNullableLazyValue(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 implements LazyValue { @@ -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 { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/StorageManager.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/StorageManager.java index 0bb388e002c..fe4d4f8fa22 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/StorageManager.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/StorageManager.java @@ -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 LazyValue createLazyValue(@NotNull Computable 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 + LazyValue createLazyValueWithPostCompute(@NotNull Computable computable, @NotNull Consumer postCompute); + @NotNull LazyValue createNullableLazyValue(@NotNull Computable 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 + LazyValue createNullableLazyValueWithPostCompute(@NotNull Computable computable, @NotNull Consumer postCompute); + @NotNull BindingTrace createSafeTrace(@NotNull BindingTrace originalTrace); diff --git a/idea/testData/checker/trivialHierarchyLoop.kt b/idea/testData/checker/trivialHierarchyLoop.kt new file mode 100644 index 00000000000..bbb2156a5b9 --- /dev/null +++ b/idea/testData/checker/trivialHierarchyLoop.kt @@ -0,0 +1,3 @@ +class A : A() {} + +val x : Int = A() \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTestGenerated.java b/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTestGenerated.java index 7a88d21a9a8..eb1bf090847 100644 --- a/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/checkers/JetPsiCheckerTestGenerated.java @@ -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");