[pypy-svn] r25965 - in pypy/dist/pypy: annotation objspace/flow objspace/flow/test rpython rpython/test translator

pedronis at codespeak.net pedronis at codespeak.net
Wed Apr 19 01:13:32 CEST 2006


Author: pedronis
Date: Wed Apr 19 01:13:26 2006
New Revision: 25965

Modified:
   pypy/dist/pypy/annotation/annrpython.py
   pypy/dist/pypy/annotation/binaryop.py
   pypy/dist/pypy/objspace/flow/objspace.py
   pypy/dist/pypy/objspace/flow/test/test_objspace.py
   pypy/dist/pypy/rpython/rdict.py
   pypy/dist/pypy/rpython/rtuple.py
   pypy/dist/pypy/rpython/test/test_objectmodel.py
   pypy/dist/pypy/rpython/test/test_rdict.py
   pypy/dist/pypy/rpython/test/test_rlist.py
   pypy/dist/pypy/rpython/test/test_rtuple.py
   pypy/dist/pypy/translator/simplify.py
Log:
fix propagation of exception out r_dict operations.

Notice that this required having the flow space assume that getitem/setitem/delitem can raise any exceptions.
Changes with some tests at various levels to cope with this.

Notice that the pruning of excepts links when a getitem appears in an unrelated try/except is less strong as a 
consequence. 

Now {}[[]] fails with a TypeError as expected in a translated PyPy.



Modified: pypy/dist/pypy/annotation/annrpython.py
==============================================================================
--- pypy/dist/pypy/annotation/annrpython.py	(original)
+++ pypy/dist/pypy/annotation/annrpython.py	Wed Apr 19 01:13:26 2006
@@ -516,11 +516,11 @@
                 arg1 = self.binding(op.args[0])
                 arg2 = self.binding(op.args[1])
                 binop = getattr(pair(arg1, arg2), op.opname, None)
-                can_only_throw = getattr(binop, "can_only_throw", None)
+                can_only_throw = read_can_only_throw(binop, arg1, arg2)
             elif op.opname in annmodel.UNARY_OPERATIONS:
                 arg1 = self.binding(op.args[0])
                 unop = getattr(arg1, op.opname, None)
-                can_only_throw = getattr(unop, "can_only_throw", None)
+                can_only_throw = read_can_only_throw(unop, arg1)
             else:
                 can_only_throw = None
 
@@ -722,3 +722,9 @@
         return "<BlockedInference break_at %s [%s]>" %(break_at, self.op)
 
     __str__ = __repr__
+
+def read_can_only_throw(opimpl, *args):
+    can_only_throw = getattr(opimpl, "can_only_throw", None)
+    if can_only_throw is None or isinstance(can_only_throw, list):
+        return can_only_throw
+    return can_only_throw(*args)

Modified: pypy/dist/pypy/annotation/binaryop.py
==============================================================================
--- pypy/dist/pypy/annotation/binaryop.py	(original)
+++ pypy/dist/pypy/annotation/binaryop.py	Wed Apr 19 01:13:26 2006
@@ -400,22 +400,27 @@
 
 class __extend__(pairtype(SomeDict, SomeObject)):
 
+    def _can_only_throw(dic1, *ignore):
+        if dic1.dictdef.dictkey.custom_eq_hash:
+            return None
+        return [KeyError]
+
     def getitem((dic1, obj2)):
         getbookkeeper().count("dict_getitem", dic1)
         dic1.dictdef.generalize_key(obj2)
         return dic1.dictdef.read_value()
-    getitem.can_only_throw = [KeyError]
+    getitem.can_only_throw = _can_only_throw
 
     def setitem((dic1, obj2), s_value):
         getbookkeeper().count("dict_setitem", dic1)
         dic1.dictdef.generalize_key(obj2)
         dic1.dictdef.generalize_value(s_value)
-    setitem.can_only_throw = [KeyError]
+    setitem.can_only_throw = _can_only_throw
 
     def delitem((dic1, obj2)):
         getbookkeeper().count("dict_delitem", dic1)
         dic1.dictdef.generalize_key(obj2)
-    delitem.can_only_throw = [KeyError]
+    delitem.can_only_throw = _can_only_throw
 
 
 class __extend__(pairtype(SomeSlice, SomeSlice)):

Modified: pypy/dist/pypy/objspace/flow/objspace.py
==============================================================================
--- pypy/dist/pypy/objspace/flow/objspace.py	(original)
+++ pypy/dist/pypy/objspace/flow/objspace.py	Wed Apr 19 01:13:26 2006
@@ -460,6 +460,12 @@
     int: [ValueError],      # built-ins that can always raise exceptions
     chr: [ValueError],
     unichr: [ValueError],
+    # specifying IndexError, and KeyError beyond Exception,
+    # allows the annotator to be more precise, see test_reraiseAnything/KeyError in
+    # the annotator tests
+    'getitem': [IndexError, KeyError, Exception],
+    'setitem': [IndexError, KeyError, Exception],
+    'delitem': [IndexError, KeyError, Exception],    
     }
 
 def _add_exceptions(names, exc):
@@ -477,13 +483,13 @@
         lis.append(OverflowError)
         implicit_exceptions[name+"_ovf"] = lis
 
-for _err in IndexError, KeyError:
-    _add_exceptions("""getitem setitem delitem""", _err)
+#for _err in IndexError, KeyError:
+#    _add_exceptions("""getitem setitem delitem""", _err)
 for _name in 'getattr', 'delattr':
     _add_exceptions(_name, AttributeError)
 for _name in 'iter', 'coerce':
     _add_exceptions(_name, TypeError)
-del _name, _err
+del _name#, _err
 
 _add_exceptions("""div mod divmod truediv floordiv pow
                    inplace_div inplace_mod inplace_divmod inplace_truediv

Modified: pypy/dist/pypy/objspace/flow/test/test_objspace.py
==============================================================================
--- pypy/dist/pypy/objspace/flow/test/test_objspace.py	(original)
+++ pypy/dist/pypy/objspace/flow/test/test_objspace.py	Wed Apr 19 01:13:26 2006
@@ -248,15 +248,32 @@
         self.show(x)
 
     #__________________________________________________________
-    def implicitIndexError(lst):
+    def implicitException(lst):
         try:
             x = lst[5]
-        except IndexError:
+        except Exception:
             return 'catch'
         return lst[3]   # not caught
 
-    def test_implicitIndexError(self):
-        x = self.codetest(self.implicitIndexError)
+    def test_implicitException(self):
+        x = self.codetest(self.implicitException)
+        simplify_graph(x)
+        self.show(x)
+        def cannot_reach_exceptblock(link):
+            if isinstance(link, Link):
+                assert link.target is not x.exceptblock
+        traverse(cannot_reach_exceptblock, x)
+
+    
+    def implicitAttributeError(x):
+        try:
+            x = getattr(x, "y")
+        except AttributeError:
+            return 'catch'
+        return getattr(x, "z")   # not caught
+
+    def test_implicitAttributeError(self):
+        x = self.codetest(self.implicitAttributeError)
         simplify_graph(x)
         self.show(x)
         def cannot_reach_exceptblock(link):
@@ -296,32 +313,72 @@
         assert d == {None: True, OSError: True, Exception: True}
 
     #__________________________________________________________
-    def reraiseKeyError(dic):
+    def reraiseAttributeError(v):
+        try:
+            x = getattr(v, "y")
+        except AttributeError:
+            raise
+
+    def test_reraiseAttributeError(self):
+        x = self.codetest(self.reraiseAttributeError)
+        simplify_graph(x)
+        self.show(x)
+        found_AttributeError = []
+        def only_raise_AttributeError(link):
+            if isinstance(link, Link):
+                if link.target is x.exceptblock:
+                    assert link.args[0] == Constant(AttributeError)
+                    found_AttributeError.append(link)
+        traverse(only_raise_AttributeError, x)
+        assert found_AttributeError
+
+    def reraiseTypeError(dic):
         try:
             x = dic[5]
-        except KeyError:
+        except TypeError:
             raise
 
-    def test_reraiseKeyError(self):
-        x = self.codetest(self.reraiseKeyError)
+    def test_reraiseTypeError(self):
+        x = self.codetest(self.reraiseTypeError)
         simplify_graph(x)
         self.show(x)
-        found_KeyError = []
-        def only_raise_KeyError(link):
+        found = []
+        def can_reach_exceptblock(link):
             if isinstance(link, Link):
                 if link.target is x.exceptblock:
-                    assert link.args[0] == Constant(KeyError)
-                    found_KeyError.append(link)
-        traverse(only_raise_KeyError, x)
-        assert found_KeyError
+                    found.append(link)                
+        traverse(can_reach_exceptblock, x)
+        assert found
+
 
     #__________________________________________________________
-    def reraiseAnything(dic):
+    def reraiseAnythingDicCase(dic):
         try:
             dic[5]
         except:
             raise
 
+    def test_reraiseAnythingDicCase(self):
+        x = self.codetest(self.reraiseAnythingDicCase)
+        simplify_graph(x)
+        self.show(x)
+        found = {}
+        def find_exceptions(link):
+            if isinstance(link, Link):
+                if link.target is x.exceptblock:
+                    if isinstance(link.args[0], Constant):
+                        found[link.args[0].value] = True
+                    else:
+                        found[link.exitcase] = None
+        traverse(find_exceptions, x)
+        assert found == {IndexError: True, KeyError: True, Exception: None}
+    
+    def reraiseAnything(x):
+        try:
+            pow(x, 5)
+        except:
+            raise
+
     def test_reraiseAnything(self):
         x = self.codetest(self.reraiseAnything)
         simplify_graph(x)
@@ -333,7 +390,7 @@
                     assert isinstance(link.args[0], Constant)
                     found[link.args[0].value] = True
         traverse(find_exceptions, x)
-        assert found == {KeyError: True, IndexError: True}
+        assert found == {ValueError: True, ZeroDivisionError: True, OverflowError: True}
 
     #__________________________________________________________
     def freevar(self, x):

Modified: pypy/dist/pypy/rpython/rdict.py
==============================================================================
--- pypy/dist/pypy/rpython/rdict.py	(original)
+++ pypy/dist/pypy/rpython/rdict.py	Wed Apr 19 01:13:26 2006
@@ -329,19 +329,25 @@
 
     def rtype_getitem((r_dict, r_key), hop):
         v_dict, v_key = hop.inputargs(r_dict, r_dict.key_repr)
-        hop.has_implicit_exception(KeyError)   # record that we know about it
+        if not r_dict.custom_eq_hash:
+            hop.has_implicit_exception(KeyError)   # record that we know about it
         hop.exception_is_here()
         v_res = hop.gendirectcall(ll_dict_getitem, v_dict, v_key)
         return r_dict.recast_value(hop.llops, v_res)
 
     def rtype_delitem((r_dict, r_key), hop):
         v_dict, v_key = hop.inputargs(r_dict, r_dict.key_repr)
-        hop.has_implicit_exception(KeyError)   # record that we know about it
+        if not r_dict.custom_eq_hash:
+            hop.has_implicit_exception(KeyError)   # record that we know about it        
         hop.exception_is_here()
         return hop.gendirectcall(ll_dict_delitem, v_dict, v_key)
 
     def rtype_setitem((r_dict, r_key), hop):
         v_dict, v_key, v_value = hop.inputargs(r_dict, r_dict.key_repr, r_dict.value_repr)
+        if r_dict.custom_eq_hash:
+            hop.exception_is_here()
+        else:
+            hop.exception_cannot_occur()
         hop.gendirectcall(ll_dict_setitem, v_dict, v_key, v_value)
 
     def rtype_contains((r_dict, r_key), hop):

Modified: pypy/dist/pypy/rpython/rtuple.py
==============================================================================
--- pypy/dist/pypy/rpython/rtuple.py	(original)
+++ pypy/dist/pypy/rpython/rtuple.py	Wed Apr 19 01:13:26 2006
@@ -139,6 +139,8 @@
         v_tuple, v_index = hop.inputargs(r_tup, Signed)
         if not isinstance(v_index, Constant):
             raise TyperError("non-constant tuple index")
+        if hop.has_implicit_exception(IndexError):
+            hop.exception_cannot_occur()
         index = v_index.value
         v = r_tup.getitem(hop.llops, v_tuple, index)
         return hop.llops.convertvar(v, r_tup.items_r[index], r_tup.external_items_r[index])

Modified: pypy/dist/pypy/rpython/test/test_objectmodel.py
==============================================================================
--- pypy/dist/pypy/rpython/test/test_objectmodel.py	(original)
+++ pypy/dist/pypy/rpython/test/test_objectmodel.py	Wed Apr 19 01:13:26 2006
@@ -149,19 +149,44 @@
 
 def test_rtype_r_dict_exceptions():
     def raising_hash(obj):
-        if id(obj) % 2 == 0:
+        if obj.startswith("bla"):
             raise TypeError
         return 1
     def eq(obj1, obj2):
         return obj1 is obj2
     def f():
         d1 = r_dict(eq, raising_hash)
+        d1['xxx'] = 1
         try:
             x = d1["blabla"]
         except Exception:
-            return 1
+            return 42
         return x
     res = interpret(f, [])
+    assert res == 42
+
+    def f():
+        d1 = r_dict(eq, raising_hash)
+        d1['xxx'] = 1
+        try:
+            x = d1["blabla"]
+        except TypeError:
+            return 42
+        return x
+    res = interpret(f, [])
+    assert res == 42
+
+    def f():
+        d1 = r_dict(eq, raising_hash)
+        d1['xxx'] = 1
+        try:
+            d1["blabla"] = 2
+        except TypeError:
+            return 42
+        return 0
+    res = interpret(f, [])
+    assert res == 42
+
 
 def test_rtype_keepalive():
     from pypy.rpython import objectmodel
@@ -183,3 +208,36 @@
     assert res == 5
 
 
+def test_access_in_try():
+    h = lambda x: 1
+    eq = lambda x,y: x==y
+    def f(d):
+        try:
+            return d[2]
+        except ZeroDivisionError:
+            return 42
+        return -1
+    def g(n):
+        d = r_dict(eq, h)
+        d[1] = n
+        d[2] = 2*n
+        return f(d)
+    res = interpret(g, [3])
+    assert res == 6
+
+def test_access_in_try_set():
+    h = lambda x: 1
+    eq = lambda x,y: x==y
+    def f(d):
+        try:
+            d[2] = 77
+        except ZeroDivisionError:
+            return 42
+        return -1
+    def g(n):
+        d = r_dict(eq, h)
+        d[1] = n
+        f(d)
+        return d[2]
+    res = interpret(g, [3])
+    assert res == 77

Modified: pypy/dist/pypy/rpython/test/test_rdict.py
==============================================================================
--- pypy/dist/pypy/rpython/test/test_rdict.py	(original)
+++ pypy/dist/pypy/rpython/test/test_rdict.py	Wed Apr 19 01:13:26 2006
@@ -680,3 +680,30 @@
     assert res == 2
     res = interpret(f, [6])
     assert res == 0
+
+def test_access_in_try():
+    def f(d):
+        try:
+            return d[2]
+        except ZeroDivisionError:
+            return 42
+        return -1
+    def g(n):
+        d = {1: n, 2: 2*n}
+        return f(d)
+    res = interpret(g, [3])
+    assert res == 6
+
+def test_access_in_try_set():
+    def f(d):
+        try:
+            d[2] = 77
+        except ZeroDivisionError:
+            return 42
+        return -1
+    def g(n):
+        d = {1: n}
+        f(d)
+        return d[2]
+    res = interpret(g, [3])
+    assert res == 77

Modified: pypy/dist/pypy/rpython/test/test_rlist.py
==============================================================================
--- pypy/dist/pypy/rpython/test/test_rlist.py	(original)
+++ pypy/dist/pypy/rpython/test/test_rlist.py	Wed Apr 19 01:13:26 2006
@@ -1104,6 +1104,32 @@
 
         assert r_A_list.lowleveltype == r_B_list.lowleveltype
 
+    def test_access_in_try(self):
+        def f(sq):
+            try:
+                return sq[2]
+            except ZeroDivisionError:
+                return 42
+            return -1
+        def g(n):
+            l = [1] * n
+            return f(l)
+        res = self.interpret(g, [3])
+        assert res == 1
+
+    def test_access_in_try_set(self):
+        def f(sq):
+            try:
+                sq[2] = 77
+            except ZeroDivisionError:
+                return 42
+            return -1
+        def g(n):
+            l = [1] * n
+            f(l)
+            return l[2]
+        res = self.interpret(g, [3])
+        assert res == 77
 
 
 class TestLltypeRtyping(BaseTestListRtyping):

Modified: pypy/dist/pypy/rpython/test/test_rtuple.py
==============================================================================
--- pypy/dist/pypy/rpython/test/test_rtuple.py	(original)
+++ pypy/dist/pypy/rpython/test/test_rtuple.py	Wed Apr 19 01:13:26 2006
@@ -224,6 +224,18 @@
         res = self.interpret(f, [0])
         assert self.class_name(res) == "B"
 
+    def test_access_in_try(self):
+        def f(sq):
+            try:
+                return sq[2]
+            except ZeroDivisionError:
+                return 42
+            return -1
+        def g(n):
+            t = (1,2,n)
+            return f(t)
+        res = self.interpret(g, [3])
+        assert res == 3
 
 class TestLLTuple(AbstractTestRTuple):
 

Modified: pypy/dist/pypy/translator/simplify.py
==============================================================================
--- pypy/dist/pypy/translator/simplify.py	(original)
+++ pypy/dist/pypy/translator/simplify.py	Wed Apr 19 01:13:26 2006
@@ -214,15 +214,17 @@
 
     def visit(block):
         if not (isinstance(block, Block)
-                and block.exitswitch == clastexc and len(block.exits) == 2
-                and block.exits[1].exitcase is Exception):
+                and block.exitswitch == clastexc
+                and block.exits[-1].exitcase is Exception):
             return
+        covered = [link.exitcase for link in block.exits[1:-1]]
         seen = []
-        norm, exc = block.exits
+        preserve = list(block.exits[:-1])
+        exc = block.exits[-1]
         last_exception = exc.last_exception
         last_exc_value = exc.last_exc_value
         query = exc.target
-        switches = [ (None, norm) ]
+        switches = []
         # collect the targets
         while len(query.exits) == 2:
             newrenaming = {}
@@ -238,7 +240,13 @@
             lno, lyes = query.exits
             assert lno.exitcase == False and lyes.exitcase == True
             if case not in seen:
-                switches.append( (case, lyes) )
+                is_covered = False
+                for cov in covered:
+                    if issubclass(case, cov):
+                        is_covered = True
+                        break
+                if not is_covered:
+                    switches.append( (case, lyes) )
                 seen.append(case)
             exc = lno
             query = exc.target
@@ -248,20 +256,20 @@
         exits = []
         for case, oldlink in switches:
             link = oldlink.copy(rename)
-            if case is not None:
-                link.last_exception = last_exception
-                link.last_exc_value = last_exc_value
-                # make the above two variables unique
-                renaming2 = {}
-                def rename2(v):
-                    return renaming2.get(v, v)
-                for v in link.getextravars():
-                    renaming2[v] = Variable(v)
-                link = link.copy(rename2)
+            assert case is not None
+            link.last_exception = last_exception
+            link.last_exc_value = last_exc_value
+            # make the above two variables unique
+            renaming2 = {}
+            def rename2(v):
+                return renaming2.get(v, v)
+            for v in link.getextravars():
+                renaming2[v] = Variable(v)
+            link = link.copy(rename2)
             link.exitcase = case
             link.prevblock = block
             exits.append(link)
-        block.exits = tuple(exits)
+        block.exits = tuple(preserve + exits)
 
     traverse(visit, graph)
 



More information about the Pypy-commit mailing list