Author: Armin Rigo <ar...@tunes.org>
Branch: 
Changeset: r92345:f70ed7c5cc49
Date: 2017-09-07 22:54 +0200
http://bitbucket.org/pypy/pypy/changeset/f70ed7c5cc49/

Log:    merge heads

diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -1629,12 +1629,15 @@
         # return text_w(w_obj) or None
         return None if self.is_none(w_obj) else self.text_w(w_obj)
 
+    @specialize.argtype(1)
     def bytes_w(self, w_obj):
         """ Takes an application level :py:class:`bytes`
             (on PyPy2 this equals `str`) and returns a rpython byte string.
         """
+        assert w_obj is not None
         return w_obj.str_w(self)
 
+    @specialize.argtype(1)
     def text_w(self, w_obj):
         """ PyPy2 takes either a :py:class:`str` and returns a
             rpython byte string, or it takes an :py:class:`unicode`
@@ -1644,6 +1647,7 @@
             On PyPy3 it takes a :py:class:`str` and it will return
             an utf-8 encoded rpython string.
         """
+        assert w_obj is not None
         return w_obj.str_w(self)
 
     @not_rpython    # tests only; should be replaced with bytes_w or text_w
@@ -1692,6 +1696,7 @@
             raise oefmt(self.w_ValueError, "byte must be in range(0, 256)")
         return chr(value)
 
+    @specialize.argtype(1)
     def int_w(self, w_obj, allow_conversion=True):
         """
         Unwrap an app-level int object into an interpret-level int.
@@ -1704,26 +1709,35 @@
         If allow_conversion=False, w_obj needs to be an app-level int or a
         subclass.
         """
+        assert w_obj is not None
         return w_obj.int_w(self, allow_conversion)
 
+    @specialize.argtype(1)
     def int(self, w_obj):
+        assert w_obj is not None
         return w_obj.int(self)
 
+    @specialize.argtype(1)
     def uint_w(self, w_obj):
+        assert w_obj is not None
         return w_obj.uint_w(self)
 
+    @specialize.argtype(1)
     def bigint_w(self, w_obj, allow_conversion=True):
         """
         Like int_w, but return a rlib.rbigint object and call __long__ if
         allow_conversion is True.
         """
+        assert w_obj is not None
         return w_obj.bigint_w(self, allow_conversion)
 
+    @specialize.argtype(1)
     def float_w(self, w_obj, allow_conversion=True):
         """
         Like int_w, but return an interp-level float and call __float__ if
         allow_conversion is True.
         """
+        assert w_obj is not None
         return w_obj.float_w(self, allow_conversion)
 
     def realtext_w(self, w_obj):
@@ -1733,7 +1747,9 @@
             raise oefmt(self.w_TypeError, "argument must be a string")
         return self.bytes_w(w_obj)
 
+    @specialize.argtype(1)
     def unicode_w(self, w_obj):
+        assert w_obj is not None
         return w_obj.unicode_w(self)
 
     def unicode0_w(self, w_obj):
@@ -1758,7 +1774,9 @@
         # This is here mostly just for gateway.int_unwrapping_space_method().
         return bool(self.int_w(w_obj))
 
+    @specialize.argtype(1)
     def ord(self, w_obj):
+        assert w_obj is not None
         return w_obj.ord(self)
 
     # This is all interface for gateway.py.
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -40,6 +40,7 @@
 from rpython.rlib import rawrefcount
 from rpython.rlib import rthread
 from rpython.rlib.debug import fatalerror_notb
+from rpython.rlib import rstackovf
 from pypy.objspace.std.typeobject import W_TypeObject, find_best_base
 from pypy.module.cpyext.cparser import CTypeSpace
 
@@ -940,6 +941,11 @@
                     message = str(e)
                 state.set_exception(OperationError(space.w_SystemError,
                                                    space.newtext(message)))
+            except rstackovf.StackOverflow as e:
+                rstackovf.check_stack_overflow()
+                failed = True
+                state.set_exception(OperationError(space.w_RuntimeError,
+                         space.newtext("maximum recursion depth exceeded")))
             else:
                 failed = False
 
diff --git a/rpython/jit/codewriter/call.py b/rpython/jit/codewriter/call.py
--- a/rpython/jit/codewriter/call.py
+++ b/rpython/jit/codewriter/call.py
@@ -185,6 +185,27 @@
                                          FUNC.RESULT, EffectInfo.MOST_GENERAL)
         return (fnaddr, calldescr)
 
+    def _raise_effect_error(self, op, extraeffect, functype, calling_graph):
+        explanation = []
+        if extraeffect == EffectInfo.EF_RANDOM_EFFECTS:
+            explanation = 
self.randomeffects_analyzer.explain_analyze_slowly(op)
+        elif extraeffect == EffectInfo.EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE:
+            explanation = 
self.virtualizable_analyzer.explain_analyze_slowly(op)
+        msg = []
+        if explanation:
+            msg = [
+                "_______ ERROR AT BOTTOM ______",
+                "RPython callstack leading to problem:",
+                ]
+            msg.extend(explanation)
+            msg.append("_______ ERROR: ______")
+        msg.append("operation %r" % op)
+        msg.append("in graph %s" % (calling_graph or "<unknown>"))
+        msg.append("this calls a %s function," % (functype, ))
+        msg.append(" but this contradicts other sources (e.g. it can have 
random"
+                   " effects): EF=%s" % (extraeffect, ))
+        raise Exception("\n".join(msg))
+
     def getcalldescr(self, op, oopspecindex=EffectInfo.OS_NONE,
                      extraeffect=None, extradescr=None,
                      calling_graph=None):
@@ -278,18 +299,13 @@
         # check that the result is really as expected
         if loopinvariant:
             if extraeffect != EffectInfo.EF_LOOPINVARIANT:
-                raise Exception(
-                "operation %r in %s: this calls a _jit_loop_invariant_ 
function,"
-                " but this contradicts other sources (e.g. it can have random"
-                " effects): EF=%s" % (op, calling_graph, extraeffect))
+                self._raise_effect_error(op, extraeffect, 
"_jit_loop_invariant_", calling_graph)
         if elidable:
             if extraeffect not in (EffectInfo.EF_ELIDABLE_CANNOT_RAISE,
                                    EffectInfo.EF_ELIDABLE_OR_MEMORYERROR,
                                    EffectInfo.EF_ELIDABLE_CAN_RAISE):
-                raise Exception(
-                "operation %r in %s: this calls an elidable function,"
-                " but this contradicts other sources (e.g. it can have random"
-                " effects): EF=%s" % (op, calling_graph, extraeffect))
+
+                self._raise_effect_error(op, extraeffect, "elidable", 
calling_graph)
             elif RESULT is lltype.Void:
                 raise Exception(
                     "operation %r in %s: this calls an elidable function "
diff --git a/rpython/jit/codewriter/test/test_call.py 
b/rpython/jit/codewriter/test/test_call.py
--- a/rpython/jit/codewriter/test/test_call.py
+++ b/rpython/jit/codewriter/test/test_call.py
@@ -299,11 +299,23 @@
     def f4(n, m):
         return compute_hash(str(n) + str(m))
 
+    T = rffi.CArrayPtr(rffi.TIME_T)
+    external = rffi.llexternal("time", [T], rffi.TIME_T, releasegil=True)
+
+    def effect():
+        return external(lltype.nullptr(T.TO))
+
+    @jit.elidable
+    def f5(n, m):
+        effect()
+        return 1
+
     def f(n, m):
         a = f1(n, m)
         b = f2(n, m)
         c = f3(n, m)
         d = f4(n, m)
+        f5(n, m)
         enable_siphash24()
         return a + len(b) + c + d
 
@@ -323,6 +335,14 @@
         call_descr = cc.getcalldescr(call_op)
         assert call_descr.extrainfo.extraeffect == expected
 
+    call_op = f_graph.startblock.operations[4]
+    assert call_op.opname == 'direct_call'
+    excinfo = py.test.raises(Exception, cc.getcalldescr, call_op)
+    lines = excinfo.value.args[0].splitlines()
+    assert "f5" in lines[2]
+    assert "effect" in lines[3]
+    assert "random effects" in lines[-1]
+
 def test_raise_elidable_no_result():
     from rpython.jit.backend.llgraph.runner import LLGraphCPU
     l = []
diff --git a/rpython/translator/backendopt/graphanalyze.py 
b/rpython/translator/backendopt/graphanalyze.py
--- a/rpython/translator/backendopt/graphanalyze.py
+++ b/rpython/translator/backendopt/graphanalyze.py
@@ -4,6 +4,7 @@
 
 class GraphAnalyzer(object):
     verbose = False
+    explanation = None
 
     def __init__(self, translator):
         self.translator = translator
@@ -73,6 +74,20 @@
     def compute_graph_info(self, graph):
         return None
 
+    def explain_analyze_slowly(self, op):
+        # this is a hack! usually done before a crash
+        self.__init__(self.translator)
+        self.explanation = explanation = []
+        oldverbose = self.verbose
+        self.verbose = True
+        try:
+            self.analyze(op)
+        finally:
+            del self.explanation
+            self.verbose = oldverbose
+        explanation.reverse()
+        return explanation
+
     def analyze(self, op, seen=None, graphinfo=None):
         if op.opname == "direct_call":
             try:
@@ -113,7 +128,11 @@
         return x
 
     def dump_info(self, info):
-        print '[%s] %s' % (self.__class__.__name__, info)
+        st = '[%s] %s' % (self.__class__.__name__, info)
+        if self.explanation is not None:
+            self.explanation.append(st)
+        else:
+            print st
 
     def analyze_direct_call(self, graph, seen=None):
         if seen is None:
diff --git a/rpython/translator/backendopt/test/test_writeanalyze.py 
b/rpython/translator/backendopt/test/test_writeanalyze.py
--- a/rpython/translator/backendopt/test/test_writeanalyze.py
+++ b/rpython/translator/backendopt/test/test_writeanalyze.py
@@ -531,3 +531,37 @@
         typed_effects = self._analyze_graph(t, wa, typed_write)
         typed_effects = self._filter_reads(typed_effects)
         assert typed_effects == direct_effects
+
+    def test_explanation(self):
+        class A(object):
+            def methodname(self):
+                self.x = 1
+                return 1
+            def m(self):
+                raise ValueError
+        class B(A):
+            def methodname(self):
+                return 2
+            def m(self):
+                return 3
+        def fancyname(a):
+            return a.methodname()
+        def m(a):
+            return a.m()
+        def h(flag):
+            if flag:
+                obj = A()
+            else:
+                obj = B()
+            fancyname(obj)
+            m(obj)
+
+        t, wa = self.translate(h, [int])
+        hgraph = graphof(t, h)
+        # fiiiish :-(
+        block = hgraph.startblock.exits[0].target.exits[0].target
+        op_call_fancyname = block.operations[0]
+
+        explanation = wa.explain_analyze_slowly(op_call_fancyname)
+        assert "fancyname" in explanation[0]
+        assert "methodname" in explanation[1]
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to