Author: Maciej Fijalkowski <fij...@gmail.com>
Branch: 
Changeset: r74882:db19352f7582
Date: 2014-12-10 17:46 +0200
http://bitbucket.org/pypy/pypy/changeset/db19352f7582/

Log:    merge

diff --git a/pypy/module/_rawffi/alt/interp_funcptr.py 
b/pypy/module/_rawffi/alt/interp_funcptr.py
--- a/pypy/module/_rawffi/alt/interp_funcptr.py
+++ b/pypy/module/_rawffi/alt/interp_funcptr.py
@@ -14,6 +14,7 @@
 from rpython.rlib.objectmodel import we_are_translated
 from pypy.module._rawffi.alt.type_converter import FromAppLevelConverter, 
ToAppLevelConverter
 from pypy.module._rawffi.interp_rawffi import got_libffi_error, 
wrap_dlopenerror
+from pypy.module._rawffi import lasterror
 
 import os
 if os.name == 'nt':
@@ -201,11 +202,23 @@
         self.func = func
         self.argchain = argchain
 
+    def before(self):
+        lasterror.restore_last_error(self.space)
+
+    def after(self):
+        lasterror.save_last_error(self.space)
+
     def get_longlong(self, w_ffitype):
-        return self.func.call(self.argchain, rffi.LONGLONG)
+        self.before()
+        x = self.func.call(self.argchain, rffi.LONGLONG)
+        self.after()
+        return x
 
     def get_ulonglong(self, w_ffitype):
-        return self.func.call(self.argchain, rffi.ULONGLONG)
+        self.before()
+        x = self.func.call(self.argchain, rffi.ULONGLONG)
+        self.after()
+        return x
 
     def get_signed(self, w_ffitype):
         # if the declared return type of the function is smaller than LONG,
@@ -216,64 +229,94 @@
         # to space.wrap in order to get a nice applevel <int>.
         #
         restype = w_ffitype.get_ffitype()
+        self.before()
         call = self.func.call
         if restype is libffi.types.slong:
-            return call(self.argchain, rffi.LONG)
+            x = call(self.argchain, rffi.LONG)
         elif restype is libffi.types.sint:
-            return rffi.cast(rffi.LONG, call(self.argchain, rffi.INT))
+            x = rffi.cast(rffi.LONG, call(self.argchain, rffi.INT))
         elif restype is libffi.types.sshort:
-            return rffi.cast(rffi.LONG, call(self.argchain, rffi.SHORT))
+            x = rffi.cast(rffi.LONG, call(self.argchain, rffi.SHORT))
         elif restype is libffi.types.schar:
-            return rffi.cast(rffi.LONG, call(self.argchain, rffi.SIGNEDCHAR))
+            x = rffi.cast(rffi.LONG, call(self.argchain, rffi.SIGNEDCHAR))
         else:
-            self.error(w_ffitype)
+            raise self.error(w_ffitype)
+        self.after()
+        return x
 
     def get_unsigned(self, w_ffitype):
-        return self.func.call(self.argchain, rffi.ULONG)
+        self.before()
+        x = self.func.call(self.argchain, rffi.ULONG)
+        self.after()
+        return x
 
     def get_unsigned_which_fits_into_a_signed(self, w_ffitype):
         # the same comment as get_signed apply
         restype = w_ffitype.get_ffitype()
+        self.before()
         call = self.func.call
         if restype is libffi.types.uint:
             assert not libffi.IS_32_BIT
             # on 32bit machines, we should never get here, because it's a case
             # which has already been handled by get_unsigned above.
-            return rffi.cast(rffi.LONG, call(self.argchain, rffi.UINT))
+            x = rffi.cast(rffi.LONG, call(self.argchain, rffi.UINT))
         elif restype is libffi.types.ushort:
-            return rffi.cast(rffi.LONG, call(self.argchain, rffi.USHORT))
+            x = rffi.cast(rffi.LONG, call(self.argchain, rffi.USHORT))
         elif restype is libffi.types.uchar:
-            return rffi.cast(rffi.LONG, call(self.argchain, rffi.UCHAR))
+            x = rffi.cast(rffi.LONG, call(self.argchain, rffi.UCHAR))
         else:
-            self.error(w_ffitype)
+            raise self.error(w_ffitype)
+        self.after()
+        return x
 
 
     def get_pointer(self, w_ffitype):
+        self.before()
         ptrres = self.func.call(self.argchain, rffi.VOIDP)
+        self.after()
         return rffi.cast(rffi.ULONG, ptrres)
 
     def get_char(self, w_ffitype):
-        return self.func.call(self.argchain, rffi.UCHAR)
+        self.before()
+        x = self.func.call(self.argchain, rffi.UCHAR)
+        self.after()
+        return x
 
     def get_unichar(self, w_ffitype):
-        return self.func.call(self.argchain, rffi.WCHAR_T)
+        self.before()
+        x = self.func.call(self.argchain, rffi.WCHAR_T)
+        self.after()
+        return x
 
     def get_float(self, w_ffitype):
-        return self.func.call(self.argchain, rffi.DOUBLE)
+        self.before()
+        x = self.func.call(self.argchain, rffi.DOUBLE)
+        self.after()
+        return x
 
     def get_singlefloat(self, w_ffitype):
-        return self.func.call(self.argchain, rffi.FLOAT)
+        self.before()
+        x = self.func.call(self.argchain, rffi.FLOAT)
+        self.after()
+        return x
 
     def get_struct(self, w_ffitype, w_structdescr):
+        self.before()
         addr = self.func.call(self.argchain, rffi.LONG, is_struct=True)
+        self.after()
         return w_structdescr.fromaddress(self.space, addr)
 
     def get_struct_rawffi(self, w_ffitype, w_structdescr):
+        self.before()
         uintval = self.func.call(self.argchain, rffi.ULONG, is_struct=True)
+        self.after()
         return w_structdescr.fromaddress(self.space, uintval)
 
     def get_void(self, w_ffitype):
-        return self.func.call(self.argchain, lltype.Void)
+        self.before()
+        x = self.func.call(self.argchain, lltype.Void)
+        self.after()
+        return x
 
 
 def unpack_argtypes(space, w_argtypes, w_restype):
diff --git a/pypy/module/_rawffi/interp_rawffi.py 
b/pypy/module/_rawffi/interp_rawffi.py
--- a/pypy/module/_rawffi/interp_rawffi.py
+++ b/pypy/module/_rawffi/interp_rawffi.py
@@ -18,6 +18,7 @@
 from rpython.rlib.rarithmetic import intmask, r_uint
 from pypy.module._rawffi.buffer import RawFFIBuffer
 from pypy.module._rawffi.tracker import tracker
+from pypy.module._rawffi import lasterror
 
 TYPEMAP = {
     # XXX A mess with unsigned/signed/normal chars :-/
@@ -495,10 +496,14 @@
         try:
             if self.resshape is not None:
                 result = self.resshape.allocate(space, 1, autofree=True)
+                lasterror.restore_last_error(space)
                 self.ptr.call(args_ll, result.ll_buffer)
+                lasterror.save_last_error(space)
                 return space.wrap(result)
             else:
+                lasterror.restore_last_error(space)
                 self.ptr.call(args_ll, lltype.nullptr(rffi.VOIDP.TO))
+                lasterror.save_last_error(space)
                 return space.w_None
         except StackCheckError, e:
             raise OperationError(space.w_ValueError, space.wrap(e.message))
@@ -615,12 +620,10 @@
 
 if sys.platform == 'win32':
     def get_last_error(space):
-        from rpython.rlib.rwin32 import GetLastError
-        return space.wrap(GetLastError())
+        return space.wrap(lasterror.fetch_last_error(space))
     @unwrap_spec(error=int)
     def set_last_error(space, error):
-        from rpython.rlib.rwin32 import SetLastError
-        SetLastError(error)
+        lasterror.store_last_error(space, error)
 else:
     # always have at least a dummy version of these functions
     # (https://bugs.pypy.org/issue1242)
diff --git a/pypy/module/_rawffi/lasterror.py b/pypy/module/_rawffi/lasterror.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/_rawffi/lasterror.py
@@ -0,0 +1,40 @@
+# For Windows only.
+# https://bitbucket.org/pypy/pypy/issue/1944/ctypes-on-windows-getlasterror
+
+import os
+
+_MS_WINDOWS = os.name == "nt"
+
+
+if _MS_WINDOWS:
+    from rpython.rlib import rwin32
+    from pypy.interpreter.executioncontext import ExecutionContext
+
+
+    ExecutionContext._rawffi_last_error = 0
+
+    def fetch_last_error(space):
+        ec = space.getexecutioncontext()
+        return ec._rawffi_last_error
+
+    def store_last_error(space, last_error):
+        ec = space.getexecutioncontext()
+        ec._rawffi_last_error = last_error
+
+    def restore_last_error(space):
+        ec = space.getexecutioncontext()
+        lasterror = ec._rawffi_last_error
+        rwin32.SetLastError(lasterror)
+
+    def save_last_error(space):
+        lasterror = rwin32.GetLastError()
+        ec = space.getexecutioncontext()
+        ec._rawffi_last_error = lasterror
+
+else:
+
+    def restore_last_error(space):
+        pass
+
+    def save_last_error(space):
+        pass
diff --git a/pypy/module/_rawffi/test/test__rawffi.py 
b/pypy/module/_rawffi/test/test__rawffi.py
--- a/pypy/module/_rawffi/test/test__rawffi.py
+++ b/pypy/module/_rawffi/test/test__rawffi.py
@@ -16,6 +16,7 @@
         #include "src/precommondefs.h"
         #include <stdlib.h>
         #include <stdio.h>
+        #include <errno.h>
 
         struct x
         {
@@ -204,6 +205,24 @@
             return inp;
         }
 
+        RPY_EXPORTED
+        int check_errno(int incoming)
+        {
+            int old_errno = errno;
+            errno = incoming;
+            return old_errno;
+        }
+
+        #ifdef _WIN32
+        #include <Windows.h>
+        RPY_EXPORTED
+        int check_last_error(int incoming)
+        {
+            int old_errno = GetLastError();
+            SetLastError(incoming);
+            return old_errno;
+        }
+        #endif
         '''))
         eci = ExternalCompilationInfo(include_dirs=[cdir])
         return str(platform.compile([c_file], eci, 'x', standalone=False))
@@ -1150,6 +1169,37 @@
         raises(OverflowError, "arg1[0] = 10**900")
         arg1.free()
 
+    def test_errno(self):
+        import _rawffi
+        lib = _rawffi.CDLL(self.lib_name)
+        A = _rawffi.Array('i')
+        f = lib.ptr('check_errno', ['i'], 'i')
+        _rawffi.set_errno(42)
+        arg = A(1)
+        arg[0] = 43
+        res = f(arg)
+        assert res[0] == 42
+        z = _rawffi.get_errno()
+        assert z == 43
+        arg.free()
+
+    def test_last_error(self):
+        import sys
+        if sys.platform != 'win32':
+            skip("Windows test")
+        import _rawffi
+        lib = _rawffi.CDLL(self.lib_name)
+        A = _rawffi.Array('i')
+        f = lib.ptr('check_last_error', ['i'], 'i')
+        _rawffi.set_last_error(42)
+        arg = A(1)
+        arg[0] = 43
+        res = f(arg)
+        assert res[0] == 42
+        z = _rawffi.get_last_error()
+        assert z == 43
+        arg.free()
+
 
 class AppTestAutoFree:
     spaceconfig = dict(usemodules=['_rawffi', 'struct'])
diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py
--- a/pypy/objspace/std/typeobject.py
+++ b/pypy/objspace/std/typeobject.py
@@ -17,9 +17,13 @@
         self.w_value = w_value
 
 
+def needs_to_unwrap_cell(space, w_value):
+    return (space.config.objspace.std.withtypeversion and
+            isinstance(w_value, TypeCell))
+
+
 def unwrap_cell(space, w_value):
-    if (space.config.objspace.std.withtypeversion and
-            isinstance(w_value, TypeCell)):
+    if needs_to_unwrap_cell(space, w_value):
         return w_value.w_value
     return w_value
 
@@ -366,8 +370,11 @@
             tup = w_self._lookup_where(name)
             return tup
         name = promote_string(name)
-        w_class, w_value = w_self._pure_lookup_where_with_method_cache(name, 
version_tag)
-        return w_class, unwrap_cell(space, w_value)
+        tup_w = w_self._pure_lookup_where_with_method_cache(name, version_tag)
+        w_class, w_value = tup_w
+        if needs_to_unwrap_cell(space, w_value):
+            return w_class, w_value.value
+        return tup_w   # don't make a new tuple, reuse the old one
 
     def _pure_lookup_where_possibly_with_method_cache(w_self, name, 
version_tag):
         if w_self.space.config.objspace.std.withmethodcache:
diff --git a/rpython/config/translationoption.py 
b/rpython/config/translationoption.py
--- a/rpython/config/translationoption.py
+++ b/rpython/config/translationoption.py
@@ -129,7 +129,8 @@
                default=False, cmdline=None),
 
     # misc
-    BoolOption("verbose", "Print extra information", default=False),
+    BoolOption("verbose", "Print extra information", default=False,
+               cmdline="--verbose"),
     StrOption("cc", "Specify compiler to use for compiling generated C", 
cmdline="--cc"),
     StrOption("profopt", "Specify profile based optimization script",
               cmdline="--profopt"),
diff --git a/rpython/flowspace/operation.py b/rpython/flowspace/operation.py
--- a/rpython/flowspace/operation.py
+++ b/rpython/flowspace/operation.py
@@ -528,6 +528,10 @@
     pyfunc = staticmethod(getattr)
 
     def constfold(self):
+        from rpython.flowspace.flowcontext import FlowingError
+        if len(self.args) == 3:
+            raise FlowingError(
+                "getattr() with three arguments not supported: %s" % (self,))
         w_obj, w_name = self.args
         # handling special things like sys
         if (w_obj in NOT_REALLY_CONST and
@@ -538,7 +542,6 @@
             try:
                 result = getattr(obj, name)
             except Exception as e:
-                from rpython.flowspace.flowcontext import FlowingError
                 etype = e.__class__
                 msg = "getattr(%s, %s) always raises %s: %s" % (
                     obj, name, etype, e)
diff --git a/rpython/jit/metainterp/pyjitpl.py 
b/rpython/jit/metainterp/pyjitpl.py
--- a/rpython/jit/metainterp/pyjitpl.py
+++ b/rpython/jit/metainterp/pyjitpl.py
@@ -964,9 +964,40 @@
         assembler_call = False
         if warmrunnerstate.inlining:
             if warmrunnerstate.can_inline_callable(greenboxes):
+                # We've found a potentially inlinable function; now we need to
+                # see if it's already on the stack. In other words: are we 
about
+                # to enter recursion? If so, we don't want to inline the
+                # recursion, which would be equivalent to unrolling a while
+                # loop.
                 portal_code = targetjitdriver_sd.mainjitcode
-                return self.metainterp.perform_call(portal_code, allboxes,
-                                                    greenkey=greenboxes)
+                count = 0
+                for f in self.metainterp.framestack:
+                    if f.jitcode is not portal_code:
+                        continue
+                    gk = f.greenkey
+                    if gk is None:
+                        continue
+                    assert len(gk) == len(greenboxes)
+                    i = 0
+                    for i in range(len(gk)):
+                        if not gk[i].same_constant(greenboxes[i]):
+                            break
+                    else:
+                        count += 1
+                memmgr = 
self.metainterp.staticdata.warmrunnerdesc.memory_manager
+                if count >= memmgr.max_unroll_recursion:
+                    # This function is recursive and has exceeded the
+                    # maximum number of unrollings we allow. We want to stop
+                    # inlining it further and to make sure that, if it
+                    # hasn't happened already, the function is traced
+                    # separately as soon as possible.
+                    if have_debug_prints():
+                        loc = 
targetjitdriver_sd.warmstate.get_location_str(greenboxes)
+                        debug_print("recursive function (not inlined):", loc)
+                    warmrunnerstate.dont_trace_here(greenboxes)
+                else:
+                    return self.metainterp.perform_call(portal_code, allboxes,
+                                greenkey=greenboxes)
             assembler_call = True
             # verify that we have all green args, needed to make sure
             # that assembler that we call is still correct
diff --git a/rpython/jit/metainterp/test/test_list.py 
b/rpython/jit/metainterp/test/test_list.py
--- a/rpython/jit/metainterp/test/test_list.py
+++ b/rpython/jit/metainterp/test/test_list.py
@@ -97,28 +97,6 @@
         assert res == f(10)
         self.check_resops(setarrayitem_gc=0, call=0, getarrayitem_gc=0)
 
-    def test_vlist_alloc_and_set(self):
-        # the check_loops fails, because [non-null] * n is only supported
-        # if n < 15 (otherwise it is implemented as a residual call)
-        jitdriver = JitDriver(greens = [], reds = ['n'])
-        def f(n):
-            l = [1] * 20
-            while n > 0:
-                jitdriver.can_enter_jit(n=n)
-                jitdriver.jit_merge_point(n=n)
-                l = [1] * 20
-                l[3] = 5
-                x = l[-17] + l[5] - 1
-                if n < 3:
-                    return x
-                n -= 1
-            return l[0]
-
-        res = self.meta_interp(f, [10], listops=True)
-        assert res == f(10)
-        py.test.skip("'[non-null] * n' for n >= 15 gives a residual call so 
far")
-        self.check_loops(setarrayitem_gc=0, getarrayitem_gc=0, call=0)
-
     def test_arraycopy_simpleoptimize(self):
         def f():
             l = [1, 2, 3, 4]
diff --git a/rpython/jit/metainterp/test/test_recursive.py 
b/rpython/jit/metainterp/test/test_recursive.py
--- a/rpython/jit/metainterp/test/test_recursive.py
+++ b/rpython/jit/metainterp/test/test_recursive.py
@@ -1112,6 +1112,37 @@
         assert res == 2095
         self.check_resops(call_assembler=12)
 
+    def test_inline_recursion_limit(self):
+        driver = JitDriver(greens = ["threshold", "loop"], reds=["i"])
+        @dont_look_inside
+        def f():
+            set_param(driver, "max_unroll_recursion", 10)
+        def portal(threshold, loop, i):
+            f()
+            if i > threshold:
+                return i
+            while True:
+                driver.jit_merge_point(threshold=threshold, loop=loop, i=i)
+                if loop:
+                    portal(threshold, False, 0)
+                else:
+                    portal(threshold, False, i + 1)
+                    return i
+                if i > 10:
+                    return 1
+                i += 1
+                driver.can_enter_jit(threshold=threshold, loop=loop, i=i)
+
+        res1 = portal(10, True, 0)
+        res2 = self.meta_interp(portal, [10, True, 0], inline=True)
+        assert res1 == res2
+        self.check_resops(call_assembler=2)
+
+        res1 = portal(9, True, 0)
+        res2 = self.meta_interp(portal, [9, True, 0], inline=True)
+        assert res1 == res2
+        self.check_resops(call_assembler=0)
+
     def test_handle_jitexception_in_portal(self):
         # a test for _handle_jitexception_in_portal in blackhole.py
         driver = JitDriver(greens = ['codeno'], reds = ['i', 'str'],
diff --git a/rpython/jit/metainterp/test/test_send.py 
b/rpython/jit/metainterp/test/test_send.py
--- a/rpython/jit/metainterp/test/test_send.py
+++ b/rpython/jit/metainterp/test/test_send.py
@@ -385,8 +385,7 @@
         self.check_target_token_count(4)
 
     def test_two_behaviors(self):
-        py.test.skip("XXX fix me!!!!!!! problem in optimize.py")
-        myjitdriver = JitDriver(greens = [], reds = ['x', 'y'])
+        myjitdriver = JitDriver(greens = [], reds = ['y', 'x'])
         class Int:
             def __init__(self, value):
                 self.value = value
@@ -402,11 +401,6 @@
             return x.value
         res = self.meta_interp(f, [len(cases)])
         assert res == 110
-        # The generated loops don't contain a new_with_vtable at all.  This
-        # is true if we replace "if cases[y]" above with "if not cases[y]"
-        # -- so there is no good reason that it fails.
-        self.check_loops(new_with_vtable=0)
-        self.check_trace_count(2)
 
     def test_behavior_change_after_a_while(self):
         myjitdriver = JitDriver(greens = [], reds = ['y', 'x'])
diff --git a/rpython/jit/metainterp/warmspot.py 
b/rpython/jit/metainterp/warmspot.py
--- a/rpython/jit/metainterp/warmspot.py
+++ b/rpython/jit/metainterp/warmspot.py
@@ -69,7 +69,8 @@
                     backendopt=False, trace_limit=sys.maxint,
                     inline=False, loop_longevity=0, retrace_limit=5,
                     function_threshold=4,
-                    enable_opts=ALL_OPTS_NAMES, max_retrace_guards=15, **kwds):
+                    enable_opts=ALL_OPTS_NAMES, max_retrace_guards=15, 
+                    max_unroll_recursion=7, **kwds):
     from rpython.config.config import ConfigError
     translator = interp.typer.annotator.translator
     try:
@@ -91,6 +92,7 @@
         jd.warmstate.set_param_retrace_limit(retrace_limit)
         jd.warmstate.set_param_max_retrace_guards(max_retrace_guards)
         jd.warmstate.set_param_enable_opts(enable_opts)
+        jd.warmstate.set_param_max_unroll_recursion(max_unroll_recursion)
     warmrunnerdesc.finish()
     if graph_and_interp_only:
         return interp, graph
diff --git a/rpython/jit/metainterp/warmstate.py 
b/rpython/jit/metainterp/warmstate.py
--- a/rpython/jit/metainterp/warmstate.py
+++ b/rpython/jit/metainterp/warmstate.py
@@ -291,6 +291,11 @@
             if self.warmrunnerdesc.memory_manager:
                 self.warmrunnerdesc.memory_manager.max_unroll_loops = value
 
+    def set_param_max_unroll_recursion(self, value):
+        if self.warmrunnerdesc:
+            if self.warmrunnerdesc.memory_manager:
+                self.warmrunnerdesc.memory_manager.max_unroll_recursion = value
+
     def disable_noninlinable_function(self, greenkey):
         cell = self.JitCell.ensure_jit_cell_at_key(greenkey)
         cell.flags |= JC_DONT_TRACE_HERE
@@ -567,19 +572,26 @@
         jd = self.jitdriver_sd
         cpu = self.cpu
 
-        def can_inline_greenargs(*greenargs):
+        def can_inline_callable(greenkey):
+            greenargs = unwrap_greenkey(greenkey)
             if can_never_inline(*greenargs):
                 return False
             cell = JitCell.get_jitcell(*greenargs)
             if cell is not None and (cell.flags & JC_DONT_TRACE_HERE) != 0:
                 return False
             return True
-        def can_inline_callable(greenkey):
-            greenargs = unwrap_greenkey(greenkey)
-            return can_inline_greenargs(*greenargs)
-        self.can_inline_greenargs = can_inline_greenargs
         self.can_inline_callable = can_inline_callable
 
+        def dont_trace_here(greenkey):
+            # Set greenkey as somewhere that tracing should not occur into;
+            # notice that, as per the description of JC_DONT_TRACE_HERE 
earlier,
+            # if greenkey hasn't been traced separately, setting
+            # JC_DONT_TRACE_HERE will force tracing the next time the function
+            # is encountered.
+            cell = JitCell.ensure_jit_cell_at_key(greenkey)
+            cell.flags |= JC_DONT_TRACE_HERE
+        self.dont_trace_here = dont_trace_here
+
         if jd._should_unroll_one_iteration_ptr is None:
             def should_unroll_one_iteration(greenkey):
                 return False
diff --git a/rpython/rlib/jit.py b/rpython/rlib/jit.py
--- a/rpython/rlib/jit.py
+++ b/rpython/rlib/jit.py
@@ -463,6 +463,7 @@
     'max_unroll_loops': 'number of extra unrollings a loop can cause',
     'enable_opts': 'INTERNAL USE ONLY (MAY NOT WORK OR LEAD TO CRASHES): '
                    'optimizations to enable, or all = %s' % ENABLE_ALL_OPTS,
+    'max_unroll_recursion': 'how many levels deep to unroll a recursive 
function'
     }
 
 PARAMETERS = {'threshold': 1039, # just above 1024, prime
@@ -476,6 +477,7 @@
               'max_retrace_guards': 15,
               'max_unroll_loops': 0,
               'enable_opts': 'all',
+              'max_unroll_recursion': 7,
               }
 unroll_parameters = unrolling_iterable(PARAMETERS.items())
 
diff --git a/rpython/rtyper/lltypesystem/lltype.py 
b/rpython/rtyper/lltypesystem/lltype.py
--- a/rpython/rtyper/lltypesystem/lltype.py
+++ b/rpython/rtyper/lltypesystem/lltype.py
@@ -182,8 +182,8 @@
     def _freeze_(self):
         return True
 
-    def _inline_is_varsize(self, last):
-        return False
+    def _note_inlined_into(self, parent, first, last):
+        """Called when this type is being used inline in a container."""
 
     def _is_atomic(self):
         return False
@@ -201,8 +201,9 @@
 class ContainerType(LowLevelType):
     _adtmeths = {}
 
-    def _inline_is_varsize(self, last):
-        raise TypeError("%r cannot be inlined in structure" % self)
+    def _note_inlined_into(self, parent, first, last):
+        raise TypeError("%r cannot be inlined in %r" % (
+            self.__class__.__name__, parent.__class__.__name__))
 
     def _install_extras(self, adtmeths={}, hints={}):
         self._adtmeths = frozendict(adtmeths)
@@ -279,11 +280,13 @@
 
         # look if we have an inlined variable-sized array as the last field
         if fields:
+            first = True
             for name, typ in fields[:-1]:
-                typ._inline_is_varsize(False)
+                typ._note_inlined_into(self, first=first, last=False)
                 first = False
             name, typ = fields[-1]
-            if typ._inline_is_varsize(True):
+            typ._note_inlined_into(self, first=first, last=True)
+            if typ._is_varsize():
                 self._arrayfld = name
         self._flds = frozendict(flds)
         self._names = tuple(names)
@@ -299,11 +302,14 @@
                 return first, FIRSTTYPE
         return None, None
 
-    def _inline_is_varsize(self, last):
-        if self._arrayfld:
+    def _note_inlined_into(self, parent, first, last):
+        if self._arrayfld is not None:
             raise TypeError("cannot inline a var-sized struct "
                             "inside another container")
-        return False
+        if self._gckind == 'gc':
+            if not first or not isinstance(parent, GcStruct):
+                raise TypeError("a GcStruct can only be inlined as the first "
+                                "field of another GcStruct")
 
     def _is_atomic(self):
         for typ in self._flds.values():
@@ -428,15 +434,18 @@
         if isinstance(self.OF, ContainerType) and self.OF._gckind != 'raw':
             raise TypeError("cannot have a %s container as array item type"
                             % (self.OF._gckind,))
-        self.OF._inline_is_varsize(False)
+        self.OF._note_inlined_into(self, first=False, last=False)
 
         self._install_extras(**kwds)
 
-    def _inline_is_varsize(self, last):
-        if not last:
+    def _note_inlined_into(self, parent, first, last):
+        if not last or not isinstance(parent, Struct):
             raise TypeError("cannot inline an array in another container"
                             " unless as the last field of a structure")
-        return True
+        if self._gckind == 'gc':
+            raise TypeError("cannot inline a GC array inside a structure")
+        if parent._gckind == 'gc' and self._hints.get('nolength', False):
+            raise TypeError("cannot inline a no-length array inside a 
GcStruct")
 
     def _is_atomic(self):
         return self.OF._is_atomic()
@@ -474,9 +483,6 @@
 
 class GcArray(Array):
     _gckind = 'gc'
-    def _inline_is_varsize(self, last):
-        raise TypeError("cannot inline a GC array inside a structure")
-
 
 class FixedSizeArray(Struct):
     # behaves more or less like a Struct with fields item0, item1, ...
@@ -508,7 +514,7 @@
         if isinstance(self.OF, ContainerType) and self.OF._gckind != 'raw':
             raise TypeError("cannot have a %s container as array item type"
                             % (self.OF._gckind,))
-        self.OF._inline_is_varsize(False)
+        self.OF._note_inlined_into(self, first=False, last=False)
 
     def _str_fields(self):
         return str(self.OF)
@@ -579,8 +585,11 @@
     def __str__(self):
         return "%s (opaque)" % self.tag
 
-    def _inline_is_varsize(self, last):
-        return False    # OpaqueType can be inlined
+    def _note_inlined_into(self, parent, first, last):
+        # OpaqueType can be inlined, but not GcOpaqueType
+        if self._gckind == 'gc':
+            raise TypeError("%r cannot be inlined in %r" % (
+                self.__class__.__name__, parent.__class__.__name__))
 
     def _container_example(self):
         return _opaque(self)
@@ -599,10 +608,6 @@
     def __str__(self):
         return "%s (gcopaque)" % self.tag
 
-    def _inline_is_varsize(self, last):
-        raise TypeError("%r cannot be inlined in structure" % self)
-
-
 class ForwardReference(ContainerType):
     _gckind = 'raw'
     def become(self, realcontainertype):
diff --git a/rpython/rtyper/lltypesystem/test/test_lltype.py 
b/rpython/rtyper/lltypesystem/test/test_lltype.py
--- a/rpython/rtyper/lltypesystem/test/test_lltype.py
+++ b/rpython/rtyper/lltypesystem/test/test_lltype.py
@@ -808,6 +808,20 @@
     assert F.RESULT == Signed
     assert F.ARGS == (Signed,)
 
+def test_cannot_inline_random_stuff_in_gcstruct():
+    S = GcStruct('S')
+    GcStruct('X', ('a', S))    # works
+    py.test.raises(TypeError, GcStruct, 'X', ('a', Signed), ('b', S))
+    GcStruct('X', ('a', Array(Signed)))   # works
+    py.test.raises(TypeError, GcStruct, 'X', ('a', Array(Signed)),
+                                             ('b', Signed))
+    Struct('X', ('a', Array(Signed, hints={'nolength': True})))   # works
+    py.test.raises(TypeError, GcStruct, 'X',
+                   ('a', Array(Signed, hints={'nolength': True})))
+    GcStruct('X', ('a', OpaqueType('foo')))   # works
+    py.test.raises(TypeError, GcStruct, 'X', ('a', GcOpaqueType('foo')))
+
+
 class TestTrackAllocation:
     def test_automatic_tracking(self):
         # calls to start_tracking_allocations/stop_tracking_allocations
diff --git a/rpython/translator/backendopt/inline.py 
b/rpython/translator/backendopt/inline.py
--- a/rpython/translator/backendopt/inline.py
+++ b/rpython/translator/backendopt/inline.py
@@ -478,6 +478,7 @@
               'malloc': 2,
               'instrument_count': 0,
               'debug_assert': -1,
+              'jit_force_virtualizable': 0,
               }
 
 def block_weight(block, weights=OP_WEIGHTS):
diff --git a/rpython/translator/backendopt/storesink.py 
b/rpython/translator/backendopt/storesink.py
--- a/rpython/translator/backendopt/storesink.py
+++ b/rpython/translator/backendopt/storesink.py
@@ -5,7 +5,7 @@
 from rpython.translator import simplify
 
 def has_side_effects(op):
-    if op.opname == 'debug_assert':
+    if op.opname == 'debug_assert' or op.opname == 'jit_force_virtualizable':
         return False
     try:
         return getattr(llop, op.opname).sideeffects
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to