Commits

Carl Friedrich Bolz committed ede526d

solve various recurivity problems in the thunk space

Comments (0)

Files changed (2)

pypy/objspace/test/test_thunkobjspace.py

 from pypy.conftest import gettestobjspace
+from pypy.interpreter import gateway
 
 class AppTest_Thunk:
 
         become(y, z)
         assert x is y is z
 
+    def test_double_become2(self):
+        from __pypy__ import thunk, become
+        x = []
+        y = []
+        z = []
+        become(x, y)
+        become(x, z)
+        assert x is y is z
+
     def test_thunk_forcing_while_forcing(self):
         from __pypy__ import thunk, become
         def f():
         x = thunk(f)
         raises(RuntimeError, 'x+1')
 
-    def INPROGRESS_test_thunk_forcing_while_forcing_2(self):
+    def test_thunk_forcing_while_forcing_2(self):
         from __pypy__ import thunk, become
         def f():
             return x
         assert is_thunk(thunk(f))
         assert not is_thunk(42)
 
+    def test_is_thunk2(self):
+        from __pypy__ import thunk, become, is_thunk
+        def f():
+            return 42
+        x = thunk(f)
+        assert is_thunk(x)
+        assert x == 42
+        assert not is_thunk(x)
+
+    def test_is_thunk_become(self):
+        from __pypy__ import thunk, become, is_thunk
+        def f():
+            return 42
+        x = thunk(f)
+        y = []
+        become(y, x)
+        assert is_thunk(y)
+        assert y == 42
+        assert not is_thunk(y)
+
     def test_lazy(self):
         from __pypy__ import lazy
         lst = []
         assert y == 42
         y = f(0)
         raises(ValueError, "str(y)")
-        raises(ValueError, "str(y)") # raises "RuntimeError: thunk is already being computed"
+        raises(ValueError, "str(y)")
+
+    def test_become_yourself(self):
+        from __pypy__ import become
+        x = []
+        become(x, x)
+        assert str(x) == "[]"

pypy/objspace/thunk.py

             except OperationError, operr:
                 w_self.operr = operr
                 raise
-            # XXX detect circular w_alias result
+            if _is_circular(w_self, w_alias):
+                operr = OperationError(space.w_RuntimeError,
+                                       space.wrap("circular thunk alias"))
+                w_self.operr = operr
+                raise operr
             w_self.w_thunkalias = w_alias
+        # XXX do path compression?
         w_self = w_alias
         w_alias = w_self.w_thunkalias
     return w_self
 
+def _is_circular(w_obj, w_alias):
+    assert (w_obj.w_thunkalias is None or
+            w_obj.w_thunkalias is w_NOT_COMPUTED_THUNK)
+    while 1:
+        if w_obj is w_alias:
+            return True
+        w_next = w_alias.w_thunkalias
+        if w_next is None:
+            return False
+        if w_next is w_NOT_COMPUTED_THUNK:
+            return False
+        w_alias = w_next
+
 def force(space, w_self):
     if w_self.w_thunkalias is not None:
         w_self = _force(space, w_self)
 
 def is_thunk(space, w_obj):
     """Check if an object is a thunk that has not been computed yet."""
-    return space.newbool(w_obj.w_thunkalias is w_NOT_COMPUTED_THUNK)
+    while 1:
+        w_alias = w_obj.w_thunkalias
+        if w_alias is None:
+            return space.w_False
+        if w_alias is w_NOT_COMPUTED_THUNK:
+            return space.w_True
+        w_obj = w_alias
 app_is_thunk = gateway.interp2app(is_thunk)
 
 def become(space, w_target, w_source):
     """Globally replace the target object with the source one."""
     w_target = force(space, w_target)
-    w_target.w_thunkalias = w_source
+    if not _is_circular(w_target, w_source):
+        w_target.w_thunkalias = w_source
     return space.w_None
 app_become = gateway.interp2app(become)