[pypy-svn] r11439 - in pypy/dist/pypy: objspace/flow objspace/flow/test translator

arigo at codespeak.net arigo at codespeak.net
Mon Apr 25 21:19:24 CEST 2005


Author: arigo
Date: Mon Apr 25 21:19:23 2005
New Revision: 11439

Modified:
   pypy/dist/pypy/objspace/flow/flowcontext.py
   pypy/dist/pypy/objspace/flow/model.py
   pypy/dist/pypy/objspace/flow/objspace.py
   pypy/dist/pypy/objspace/flow/test/test_objspace.py
   pypy/dist/pypy/translator/simplify.py
   pypy/dist/pypy/translator/transform.py
Log:
Reworked the implicit exception handling.  Now, an implicit exception
is raised as an ImplicitOperationError (a subclass of OperationError).
If it is caught, fine.  If it falls all the way through the function,
then it means it is not caught, and we now it should be ignored.  If
it is caught but re-raised with a bare 'raise' statement, a minor hack
ensures that a normal OperationError gets re-raised then.

For convenience, when an ImplicitOperationError falls through, the
flow obj space makes a link to the except block but with a constant
AssertionError, basically saying "this implicit exception shouldn't
have happened".  It might be nice to try running code with such
assertion checks.

Normally, though, we don't want them.  So the flow graph model is now
that such jumps with AssertionError to the except block are regarded as
"dead code", i.e. links that are not supposed to be ever followed at
run-time.  So simplify.py now removes such links.

Some carefulness is needed to avoid breaking the graph by removing too
many links.

New tests check that the correct implicit exceptions are removed and
not more.


Modified: pypy/dist/pypy/objspace/flow/flowcontext.py
==============================================================================
--- pypy/dist/pypy/objspace/flow/flowcontext.py	(original)
+++ pypy/dist/pypy/objspace/flow/flowcontext.py	Mon Apr 25 21:19:23 2005
@@ -7,6 +7,9 @@
 class OperationThatShouldNotBePropagatedError(OperationError):
     pass
 
+class ImplicitOperationError(OperationError):
+    pass
+
 class StopFlowing(Exception):
     pass
 
@@ -239,6 +242,17 @@
                         self.space.unwrap(e.w_type).__name__,
                         self.space.unwrap(e.w_value)))
 
+            except ImplicitOperationError, e:
+                if isinstance(e.w_type, Constant):
+                    exc_cls = e.w_type.value
+                else:
+                    exc_cls = Exception
+                msg = "implicit %s shouldn't occur" % exc_cls.__name__
+                w_type = Constant(AssertionError)
+                w_value = Constant(AssertionError(msg))
+                link = Link([w_type, w_value], self.graph.exceptblock)
+                self.recorder.crnt_block.closeblock(link)
+
             except OperationError, e:
                 link = Link([e.w_type, e.w_value], self.graph.exceptblock)
                 self.recorder.crnt_block.closeblock(link)
@@ -320,3 +334,10 @@
                 block.recloseblock(Link(outputargs, newblock))
             candidates.insert(0, newblock)
             self.pendingblocks.append(newblock)
+
+    def sys_exc_info(self):
+        operr = ExecutionContext.sys_exc_info(self)
+        if isinstance(operr, ImplicitOperationError):
+            # re-raising an implicit operation makes it an explicit one
+            operr = OperationError(operr.w_type, operr.w_value)
+        return operr

Modified: pypy/dist/pypy/objspace/flow/model.py
==============================================================================
--- pypy/dist/pypy/objspace/flow/model.py	(original)
+++ pypy/dist/pypy/objspace/flow/model.py	Mon Apr 25 21:19:23 2005
@@ -342,7 +342,7 @@
                         assert block.exits[0].exitcase is None
                 elif block.exitswitch == Constant(last_exception):
                     assert len(block.operations) >= 1
-                    assert len(block.exits) >= 1
+                    assert len(block.exits) >= 2
                     assert block.exits[0].exitcase is None
                     for link in block.exits[1:]:
                         assert issubclass(link.exitcase, Exception)

Modified: pypy/dist/pypy/objspace/flow/objspace.py
==============================================================================
--- pypy/dist/pypy/objspace/flow/objspace.py	(original)
+++ pypy/dist/pypy/objspace/flow/objspace.py	Mon Apr 25 21:19:23 2005
@@ -383,11 +383,9 @@
         if exceptions:
             # catch possible exceptions implicitly.  If the OperationError
             # below is not caught in the same function, it will produce an
-            # exception-raising return block in the flow graph.  The special
-            # value 'wrap(last_exception)' is used as a marker for this kind
-            # of implicit exceptions, and simplify.py will remove it as per
-            # the RPython definition: implicit exceptions not explicitly
-            # caught in the same function are assumed not to occur.
+            # exception-raising return block in the flow graph.  Note that
+            # even if the interpreter re-raises the exception, it will not
+            # be the same ImplicitOperationError instance internally.
             context = self.getexecutioncontext()
             outcome, w_exc_cls, w_exc_value = context.guessexception(*exceptions)
             if outcome is not None:
@@ -396,7 +394,8 @@
                 # unless 'outcome' is Exception.
                 if outcome is not Exception:
                     w_exc_cls = Constant(outcome)
-                raise OperationError(w_exc_cls, w_exc_value)
+                raise flowcontext.ImplicitOperationError(w_exc_cls,
+                                                         w_exc_value)
 
 # ______________________________________________________________________
 

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	Mon Apr 25 21:19:23 2005
@@ -222,7 +222,52 @@
 
     def test_implicitIndexError(self):
         x = self.codetest(self.implicitIndexError)
+        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 reraiseKeyError(dic):
+        try:
+            x = dic[5]
+        except KeyError:
+            raise
+
+    def test_reraiseKeyError(self):
+        x = self.codetest(self.reraiseKeyError)
+        simplify_graph(x)
         self.show(x)
+        found_KeyError = []
+        def only_raise_KeyError(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
+
+    #__________________________________________________________
+    def reraiseAnything(dic):
+        try:
+            dic[5]
+        except:
+            raise
+
+    def test_reraiseAnything(self):
+        x = self.codetest(self.reraiseAnything)
+        simplify_graph(x)
+        self.show(x)
+        found = {}
+        def find_exceptions(link):
+            if isinstance(link, Link):
+                if link.target is x.exceptblock:
+                    assert isinstance(link.args[0], Constant)
+                    found[link.args[0].value] = True
+        traverse(find_exceptions, x)
+        assert found == {KeyError: True, IndexError: True}
 
     #__________________________________________________________
     def freevar(self, x):

Modified: pypy/dist/pypy/translator/simplify.py
==============================================================================
--- pypy/dist/pypy/translator/simplify.py	(original)
+++ pypy/dist/pypy/translator/simplify.py	Mon Apr 25 21:19:23 2005
@@ -11,7 +11,7 @@
     """inplace-apply all the existing optimisations to the graph."""
     checkgraph(graph)
     eliminate_empty_blocks(graph)
-    remove_implicit_exceptions(graph)
+    remove_assertion_errors(graph)
     join_blocks(graph)
     transform_dead_op_vars(graph)
     remove_identical_vars(graph)
@@ -76,29 +76,33 @@
                     visit(exit)
     traverse(visit, graph)
 
-def remove_implicit_exceptions(graph):
-    """An exception raised implicitely has a particular value of
-    space.wrap(last_exception) -- see pypy.objspace.flow.objspace.make_op --
-    which shows up in the flow graph if the exception is not caught.  This
-    function removes such exceptions entierely.  This gets rid for example
-    of possible IndexErrors by 'getitem', assuming they cannot happen unless
-    there is an exception handler in the same function."""
-    def visit(link):
-        if isinstance(link, Link) and link in link.prevblock.exits:
-            if (link.target is graph.exceptblock and
-                link.prevblock.exitswitch == Constant(last_exception) and
-                isinstance(link.exitcase, type(Exception)) and
-                issubclass(link.exitcase, Exception) and
-                len(link.args) == 2 and
-                link.args[1] == Constant(last_exc_value) and
-                link.args[0] in [Constant(last_exception),
-                                 Constant(link.exitcase)]):
-                # remove the link
-                lst = list(link.prevblock.exits)
-                lst.remove(link)
-                link.prevblock.exits = tuple(lst)
-                if len(lst) <= 1:
-                    link.prevblock.exitswitch = None
+def remove_assertion_errors(graph):
+    """Remove branches that go directly to raising an AssertionError,
+    assuming that AssertionError shouldn't occur at run-time.  Note that
+    this is how implicit exceptions are removed (see _implicit_ in
+    flowcontext.py).
+    """
+    def visit(block):
+        if isinstance(block, Block):
+            for i in range(len(block.exits)-1, -1, -1):
+                exit = block.exits[i]
+                if not (exit.target is graph.exceptblock and
+                        exit.args[0] == Constant(AssertionError)):
+                    continue
+                # can we remove this exit without breaking the graph?
+                if len(block.exits) < 2:
+                    break
+                if block.exitswitch == Constant(last_exception):
+                    if exit.exitcase is None:
+                        break
+                    if len(block.exits) == 2:
+                        # removing the last non-exceptional exit
+                        block.exitswitch = None
+                        exit.exitcase = None
+                # remove this exit
+                lst = list(block.exits)
+                del lst[i]
+                block.exits = tuple(lst)
     traverse(visit, graph)
 
 def transform_dead_op_vars(graph):

Modified: pypy/dist/pypy/translator/transform.py
==============================================================================
--- pypy/dist/pypy/translator/transform.py	(original)
+++ pypy/dist/pypy/translator/transform.py	Mon Apr 25 21:19:23 2005
@@ -266,16 +266,14 @@
                 if not block.exits:
                     # oups! cannot reach the end of this block
                     cutoff_alwaysraising_block(self, block)
-                elif block.exitswitch != Constant(last_exception):
-                    # non-exceptional exit
-                    if len(block.exits) == 1:
-                        block.exitswitch = None
-                        block.exits[0].exitcase = None
-                else:
+                elif block.exitswitch == Constant(last_exception):
                     # exceptional exit
                     if block.exits[0].exitcase is not None:
                         # killed the non-exceptional path!
                         cutoff_alwaysraising_block(self, block)
+                if len(block.exits) == 1:
+                    block.exitswitch = None
+                    block.exits[0].exitcase = None
 
 def cutoff_alwaysraising_block(self, block):
     "Fix a block whose end can never be reached at run-time."
@@ -293,18 +291,18 @@
     del block.operations[n+1:]
     s_impossible = annmodel.SomeImpossibleValue()
     self.bindings[block.operations[n].result] = s_impossible
-    # insert the equivalent of 'raise SystemError'
+    # insert the equivalent of 'raise AssertionError'
     # XXX no sane way to get the graph from the block!
     fn = self.annotated[block]
     assert fn in self.translator.flowgraphs, (
         "Cannot find the graph that this block belong to! "
         "fn=%r" % (fn,))
     graph = self.translator.flowgraphs[fn]
-    c1 = Constant(SystemError)
-    c2 = Constant(SystemError(
-        "Call to %r should have raised an exception" % (fn,)))
+    msg = "Call to %r should have raised an exception" % (fn,)
+    c1 = Constant(AssertionError)
+    c2 = Constant(AssertionError(msg))
     errlink = Link([c1, c2], graph.exceptblock)
-    block.recloseblock(errlink)
+    block.recloseblock(errlink, *block.exits)
     # XXX do something about the annotation of the
     #     exceptblock.inputargs
 



More information about the Pypy-commit mailing list