diff --git a/core/util.runtime/src/org/jetbrains/jet/storage/LockBasedStorageManager.java b/core/util.runtime/src/org/jetbrains/jet/storage/LockBasedStorageManager.java index 47fc5af18ca..c287a774221 100644 --- a/core/util.runtime/src/org/jetbrains/jet/storage/LockBasedStorageManager.java +++ b/core/util.runtime/src/org/jetbrains/jet/storage/LockBasedStorageManager.java @@ -324,14 +324,25 @@ public class LockBasedStorageManager implements StorageManager { value = cache.get(input); if (value != null) return WrappedValues.unescapeExceptionOrNull(value); + AssertionError error = null; try { V typedValue = compute.invoke(input); Object oldValue = cache.put(input, WrappedValues.escapeNull(typedValue)); - assert oldValue == null : "Race condition or recursion detected. Old value is " + oldValue; + + // This code effectively asserts that oldValue is null + // The trickery is here because below we catch all exceptions thrown here, and this is the only exception that shouldn't be stored + // A seemingly obvious way to come about this case would be to declare a special exception class, but the problem is that + // one memoized function is likely to (indirectly) call another, and if this second one throws this exception, we are screwed + if (oldValue != null) { + error = new AssertionError("Race condition or recursion detected. Old value is " + oldValue); + throw error; + } return typedValue; } catch (Throwable throwable) { + if (throwable == error) throw error; + Object oldValue = cache.put(input, WrappedValues.escapeThrowable(throwable)); assert oldValue == null : "Race condition or recursion detected. Old value is " + oldValue;