[pypy-commit] pypy default: update to cffi 1.9.2

arigo pypy.commits at gmail.com
Thu Dec 22 11:54:38 EST 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r89214:29df4aac463b
Date: 2016-12-22 17:51 +0100
http://bitbucket.org/pypy/pypy/changeset/29df4aac463b/

Log:	update to cffi 1.9.2

diff --git a/lib_pypy/cffi/__init__.py b/lib_pypy/cffi/__init__.py
--- a/lib_pypy/cffi/__init__.py
+++ b/lib_pypy/cffi/__init__.py
@@ -4,8 +4,8 @@
 from .api import FFI, CDefError, FFIError
 from .ffiplatform import VerificationError, VerificationMissing
 
-__version__ = "1.9.1"
-__version_info__ = (1, 9, 1)
+__version__ = "1.9.2"
+__version_info__ = (1, 9, 2)
 
 # The verifier module file names are based on the CRC32 of a string that
 # contains the following version number.  It may be older than __version__
diff --git a/lib_pypy/cffi/_embedding.h b/lib_pypy/cffi/_embedding.h
--- a/lib_pypy/cffi/_embedding.h
+++ b/lib_pypy/cffi/_embedding.h
@@ -233,7 +233,7 @@
         f = PySys_GetObject((char *)"stderr");
         if (f != NULL && f != Py_None) {
             PyFile_WriteString("\nFrom: " _CFFI_MODULE_NAME
-                               "\ncompiled with cffi version: 1.9.1"
+                               "\ncompiled with cffi version: 1.9.2"
                                "\n_cffi_backend module: ", f);
             modules = PyImport_GetModuleDict();
             mod = PyDict_GetItemString(modules, "_cffi_backend");
diff --git a/pypy/module/_cffi_backend/__init__.py b/pypy/module/_cffi_backend/__init__.py
--- a/pypy/module/_cffi_backend/__init__.py
+++ b/pypy/module/_cffi_backend/__init__.py
@@ -3,7 +3,7 @@
 from rpython.rlib import rdynload, clibffi
 from rpython.rtyper.lltypesystem import rffi
 
-VERSION = "1.9.1"
+VERSION = "1.9.2"
 
 FFI_DEFAULT_ABI = clibffi.FFI_DEFAULT_ABI
 try:
diff --git a/pypy/module/_cffi_backend/ctypefunc.py b/pypy/module/_cffi_backend/ctypefunc.py
--- a/pypy/module/_cffi_backend/ctypefunc.py
+++ b/pypy/module/_cffi_backend/ctypefunc.py
@@ -17,7 +17,7 @@
 from pypy.module._cffi_backend.ctypeobj import W_CType
 from pypy.module._cffi_backend.ctypeptr import W_CTypePtrBase, W_CTypePointer
 from pypy.module._cffi_backend.ctypevoid import W_CTypeVoid
-from pypy.module._cffi_backend.ctypestruct import W_CTypeStruct
+from pypy.module._cffi_backend.ctypestruct import W_CTypeStruct, W_CTypeUnion
 from pypy.module._cffi_backend.ctypeprim import (W_CTypePrimitiveSigned,
     W_CTypePrimitiveUnsigned, W_CTypePrimitiveCharOrUniChar,
     W_CTypePrimitiveFloat, W_CTypePrimitiveLongDouble)
@@ -231,6 +231,11 @@
         return cifbuilder.fb_struct_ffi_type(self, is_result_type)
     return _missing_ffi_type(self, cifbuilder, is_result_type)
 
+def _union_ffi_type(self, cifbuilder, is_result_type):
+    if self.size >= 0:   # only for a better error message
+        return cifbuilder.fb_union_ffi_type(self, is_result_type)
+    return _missing_ffi_type(self, cifbuilder, is_result_type)
+
 def _primsigned_ffi_type(self, cifbuilder, is_result_type):
     size = self.size
     if   size == 1: return clibffi.ffi_type_sint8
@@ -266,6 +271,7 @@
 
 W_CType._get_ffi_type                       = _missing_ffi_type
 W_CTypeStruct._get_ffi_type                 = _struct_ffi_type
+W_CTypeUnion._get_ffi_type                  = _union_ffi_type
 W_CTypePrimitiveSigned._get_ffi_type        = _primsigned_ffi_type
 W_CTypePrimitiveCharOrUniChar._get_ffi_type = _primunsigned_ffi_type
 W_CTypePrimitiveUnsigned._get_ffi_type      = _primunsigned_ffi_type
@@ -276,6 +282,12 @@
 # ----------
 
 
+_SUPPORTED_IN_API_MODE = (
+        " are only supported as %s if the function is "
+        "'API mode' and non-variadic (i.e. declared inside ffibuilder"
+        ".cdef()+ffibuilder.set_source() and not taking a final '...' "
+        "argument)")
+
 class CifDescrBuilder(object):
     rawmem = lltype.nullptr(rffi.CCHARP.TO)
 
@@ -297,6 +309,20 @@
     def fb_fill_type(self, ctype, is_result_type):
         return ctype._get_ffi_type(self, is_result_type)
 
+    def fb_unsupported(self, ctype, is_result_type, detail):
+        place = "return value" if is_result_type else "argument"
+        raise oefmt(self.space.w_NotImplementedError,
+            "ctype '%s' not supported as %s.  %s.  "
+            "Such structs" + _SUPPORTED_IN_API_MODE,
+            ctype.name, place, detail, place)
+
+    def fb_union_ffi_type(self, ctype, is_result_type=False):
+        place = "return value" if is_result_type else "argument"
+        raise oefmt(self.space.w_NotImplementedError,
+            "ctype '%s' not supported as %s by libffi.  "
+            "Unions" + _SUPPORTED_IN_API_MODE,
+            ctype.name, place, place)
+
     def fb_struct_ffi_type(self, ctype, is_result_type=False):
         # We can't pass a struct that was completed by verify().
         # Issue: assume verify() is given "struct { long b; ...; }".
@@ -309,37 +335,40 @@
         # Another reason for 'custom_field_pos' would be anonymous
         # nested structures: we lost the information about having it
         # here, so better safe (and forbid it) than sorry (and maybe
-        # crash).
+        # crash).  Note: it seems we only get in this case with
+        # ffi.verify().
         space = self.space
         ctype.force_lazy_struct()
         if ctype._custom_field_pos:
             # these NotImplementedErrors may be caught and ignored until
             # a real call is made to a function of this type
-            place = "return value" if is_result_type else "argument"
-            raise oefmt(space.w_NotImplementedError,
-                "ctype '%s' not supported as %s (it is a struct declared "
-                "with \"...;\", but the C calling convention may depend "
-                "on the missing fields)", ctype.name, place)
+            raise self.fb_unsupported(ctype, is_result_type,
+                "It is a struct declared with \"...;\", but the C "
+                "calling convention may depend on the missing fields; "
+                "or, it contains anonymous struct/unions")
+        # Another reason: __attribute__((packed)) is not supported by libffi.
+        if ctype._with_packed_change:
+            raise self.fb_unsupported(ctype, is_result_type,
+                "It is a 'packed' structure, with a different layout than "
+                "expected by libffi")
 
         # walk the fields, expanding arrays into repetitions; first,
         # only count how many flattened fields there are
         nflat = 0
         for i, cf in enumerate(ctype._fields_list):
             if cf.is_bitfield():
-                place = "return value" if is_result_type else "argument"
-                raise oefmt(space.w_NotImplementedError,
-                    "ctype '%s' not supported as %s"
-                    " (it is a struct with bit fields)", ctype.name, place)
+                raise self.fb_unsupported(ctype, is_result_type,
+                    "It is a struct with bit fields, which libffi does not "
+                    "support")
             flat = 1
             ct = cf.ctype
             while isinstance(ct, ctypearray.W_CTypeArray):
                 flat *= ct.length
                 ct = ct.ctitem
             if flat <= 0:
-                place = "return value" if is_result_type else "argument"
-                raise oefmt(space.w_NotImplementedError,
-                    "ctype '%s' not supported as %s (it is a struct"
-                    " with a zero-length array)", ctype.name, place)
+                raise self.fb_unsupported(ctype, is_result_type,
+                    "It is a struct with a zero-length array, which libffi "
+                    "does not support")
             nflat += flat
 
         if USE_C_LIBFFI_MSVC and is_result_type:
diff --git a/pypy/module/_cffi_backend/ctypestruct.py b/pypy/module/_cffi_backend/ctypestruct.py
--- a/pypy/module/_cffi_backend/ctypestruct.py
+++ b/pypy/module/_cffi_backend/ctypestruct.py
@@ -34,6 +34,7 @@
     _fields_dict = None
     _custom_field_pos = False
     _with_var_array = False
+    _with_packed_changed = False
 
     def __init__(self, space, name):
         W_CType.__init__(self, space, -1, name, len(name))
diff --git a/pypy/module/_cffi_backend/newtype.py b/pypy/module/_cffi_backend/newtype.py
--- a/pypy/module/_cffi_backend/newtype.py
+++ b/pypy/module/_cffi_backend/newtype.py
@@ -303,6 +303,7 @@
     fields_dict = {}
     w_ctype._custom_field_pos = False
     with_var_array = False
+    with_packed_change = False
 
     for i in range(len(fields_w)):
         w_field = fields_w[i]
@@ -333,7 +334,8 @@
         #
         # update the total alignment requirement, but skip it if the
         # field is an anonymous bitfield or if SF_PACKED
-        falign = 1 if sflags & SF_PACKED else ftype.alignof()
+        falignorg = ftype.alignof()
+        falign = 1 if sflags & SF_PACKED else falignorg
         do_align = True
         if (sflags & SF_GCC_ARM_BITFIELDS) == 0 and fbitsize >= 0:
             if (sflags & SF_MSVC_BITFIELDS) == 0:
@@ -359,7 +361,10 @@
                 bs_flag = ctypestruct.W_CField.BS_REGULAR
 
             # align this field to its own 'falign' by inserting padding
+            boffsetorg = (boffset + falignorg*8-1) & ~(falignorg*8-1)
             boffset = (boffset + falign*8-1) & ~(falign*8-1)
+            if boffsetorg != boffset:
+                with_packed_change = True
 
             if foffset >= 0:
                 # a forced field position: ignore the offset just computed,
@@ -372,6 +377,7 @@
             if (fname == '' and
                     isinstance(ftype, ctypestruct.W_CTypeStructOrUnion)):
                 # a nested anonymous struct or union
+                # note: it seems we only get here with ffi.verify()
                 srcfield2names = {}
                 ftype.force_lazy_struct()
                 for name, srcfld in ftype._fields_dict.items():
@@ -530,6 +536,7 @@
     w_ctype._fields_dict = fields_dict
     #w_ctype._custom_field_pos = ...set above already
     w_ctype._with_var_array = with_var_array
+    w_ctype._with_packed_change = with_packed_change
 
 # ____________________________________________________________
 
diff --git a/pypy/module/_cffi_backend/test/_backend_test_c.py b/pypy/module/_cffi_backend/test/_backend_test_c.py
--- a/pypy/module/_cffi_backend/test/_backend_test_c.py
+++ b/pypy/module/_cffi_backend/test/_backend_test_c.py
@@ -1,7 +1,7 @@
 # ____________________________________________________________
 
 import sys
-assert __version__ == "1.9.1", ("This test_c.py file is for testing a version"
+assert __version__ == "1.9.2", ("This test_c.py file is for testing a version"
                                 " of cffi that differs from the one that we"
                                 " get from 'import _cffi_backend'")
 if sys.version_info < (3,):
@@ -1084,9 +1084,13 @@
     BFunc = new_function_type((BStruct,), BDouble)   # internally not callable
     dummy_func = cast(BFunc, 42)
     e = py.test.raises(NotImplementedError, dummy_func, "?")
-    msg = ("ctype \'struct foo\' not supported as argument (it is a struct "
-           'declared with "...;", but the C calling convention may depend on '
-           'the missing fields)')
+    msg = ("ctype 'struct foo' not supported as argument.  It is a struct "
+           'declared with "...;", but the C calling convention may depend '
+           "on the missing fields; or, it contains anonymous struct/unions.  "
+           "Such structs are only supported as argument if the function is "
+           "'API mode' and non-variadic (i.e. declared inside ffibuilder."
+           "cdef()+ffibuilder.set_source() and not taking a final '...' "
+           "argument)")
     assert str(e.value) == msg
 
 def test_new_charp():
diff --git a/pypy/module/_cffi_backend/test/test_recompiler.py b/pypy/module/_cffi_backend/test/test_recompiler.py
--- a/pypy/module/_cffi_backend/test/test_recompiler.py
+++ b/pypy/module/_cffi_backend/test/test_recompiler.py
@@ -6,9 +6,9 @@
 import pypy.module.cpyext.api     # side-effect of pre-importing it
 
 
- at unwrap_spec(cdef=str, module_name=str, source=str)
+ at unwrap_spec(cdef=str, module_name=str, source=str, packed=int)
 def prepare(space, cdef, module_name, source, w_includes=None,
-            w_extra_source=None, w_min_version=None):
+            w_extra_source=None, w_min_version=None, packed=False):
     try:
         import cffi
         from cffi import FFI            # <== the system one, which
@@ -47,7 +47,7 @@
     ffi = FFI()
     for include_ffi_object in includes:
         ffi.include(include_ffi_object._test_recompiler_source_ffi)
-    ffi.cdef(cdef)
+    ffi.cdef(cdef, packed=packed)
     ffi.set_source(module_name, source)
     ffi.emit_c_code(c_file)
 
@@ -1838,3 +1838,149 @@
         raises(ffi.error, ffi.sizeof, "vmat_t")
         p = ffi.new("vmat_t", 4)
         assert ffi.sizeof(p[3]) == 8 * ffi.sizeof("int")
+
+    def test_call_with_custom_field_pos(self):
+        ffi, lib = self.prepare("""
+            struct foo { int x; ...; };
+            struct foo f(void);
+            struct foo g(int, ...);
+            """, "test_call_with_custom_field_pos", """
+            struct foo { int y, x; };
+            struct foo f(void) {
+                struct foo s = { 40, 200 };
+                return s;
+            }
+            struct foo g(int a, ...) { }
+        """)
+        assert lib.f().x == 200
+        e = raises(NotImplementedError, lib.g, 0)
+        assert str(e.value) == (
+            'ctype \'struct foo\' not supported as return value.  It is a '
+            'struct declared with "...;", but the C calling convention may '
+            'depend on the missing fields; or, it contains anonymous '
+            'struct/unions.  Such structs are only supported '
+            'as return value if the function is \'API mode\' and non-variadic '
+            '(i.e. declared inside ffibuilder.cdef()+ffibuilder.set_source() '
+            'and not taking a final \'...\' argument)')
+
+    def test_call_with_nested_anonymous_struct(self):
+        import sys
+        if sys.platform == 'win32':
+            py.test.skip("needs a GCC extension")
+        ffi, lib = self.prepare("""
+            struct foo { int a; union { int b, c; }; };
+            struct foo f(void);
+            struct foo g(int, ...);
+            """, "test_call_with_nested_anonymous_struct", """
+            struct foo { int a; union { int b, c; }; };
+            struct foo f(void) {
+                struct foo s = { 40 };
+                s.b = 200;
+                return s;
+            }
+            struct foo g(int a, ...) { }
+        """)
+        assert lib.f().b == 200
+        e = raises(NotImplementedError, lib.g, 0)
+        assert str(e.value) == (
+            'ctype \'struct foo\' not supported as return value.  It is a '
+            'struct declared with "...;", but the C calling convention may '
+            'depend on the missing fields; or, it contains anonymous '
+            'struct/unions.  Such structs are only supported '
+            'as return value if the function is \'API mode\' and non-variadic '
+            '(i.e. declared inside ffibuilder.cdef()+ffibuilder.set_source() '
+            'and not taking a final \'...\' argument)')
+
+    def test_call_with_bitfield(self):
+        ffi, lib = self.prepare("""
+            struct foo { int x:5; };
+            struct foo f(void);
+            struct foo g(int, ...);
+            """, "test_call_with_bitfield", """
+            struct foo { int x:5; };
+            struct foo f(void) {
+                struct foo s = { 11 };
+                return s;
+            }
+            struct foo g(int a, ...) { }
+        """)
+        assert lib.f().x == 11
+        e = raises(NotImplementedError, lib.g, 0)
+        assert str(e.value) == (
+            "ctype 'struct foo' not supported as return value.  It is a struct "
+            "with bit fields, which libffi does not support.  Such structs are "
+            "only supported as return value if the function is 'API mode' and "
+            "non-variadic (i.e. declared inside ffibuilder.cdef()+ffibuilder."
+            "set_source() and not taking a final '...' argument)")
+
+    def test_call_with_zero_length_field(self):
+        ffi, lib = self.prepare("""
+            struct foo { int a; int x[0]; };
+            struct foo f(void);
+            struct foo g(int, ...);
+            """, "test_call_with_zero_length_field", """
+            struct foo { int a; int x[0]; };
+            struct foo f(void) {
+                struct foo s = { 42 };
+                return s;
+            }
+            struct foo g(int a, ...) { }
+        """)
+        assert lib.f().a == 42
+        e = raises(NotImplementedError, lib.g, 0)
+        assert str(e.value) == (
+           "ctype 'struct foo' not supported as return value.  It is a "
+           "struct with a zero-length array, which libffi does not support.  "
+           "Such structs are only supported as return value if the function is "
+           "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()"
+           "+ffibuilder.set_source() and not taking a final '...' argument)")
+
+    def test_call_with_union(self):
+        ffi, lib = self.prepare("""
+            union foo { int a; char b; };
+            union foo f(void);
+            union foo g(int, ...);
+            """, "test_call_with_union", """
+            union foo { int a; char b; };
+            union foo f(void) {
+                union foo s = { 42 };
+                return s;
+            }
+            union foo g(int a, ...) { }
+        """)
+        assert lib.f().a == 42
+        e = raises(NotImplementedError, lib.g, 0)
+        assert str(e.value) == (
+           "ctype 'union foo' not supported as return value by libffi.  "
+           "Unions are only supported as return value if the function is "
+           "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()"
+           "+ffibuilder.set_source() and not taking a final '...' argument)")
+
+    def test_call_with_packed_struct(self):
+        import sys
+        if sys.platform == 'win32':
+            py.test.skip("needs a GCC extension")
+        ffi, lib = self.prepare("""
+            struct foo { char y; int x; };
+            struct foo f(void);
+            struct foo g(int, ...);
+        """, "test_call_with_packed_struct", """
+            struct foo { char y; int x; } __attribute__((packed));
+            struct foo f(void) {
+                struct foo s = { 40, 200 };
+                return s;
+            }
+            struct foo g(int a, ...) {
+                struct foo s = { 41, 201 };
+                return s;
+            }
+        """, packed=True, min_version=(1, 8, 3))
+        assert lib.f().y == chr(40)
+        assert lib.f().x == 200
+        e = raises(NotImplementedError, lib.g, 0)
+        assert str(e.value) == (
+           "ctype 'struct foo' not supported as return value.  It is a 'packed'"
+           " structure, with a different layout than expected by libffi.  "
+           "Such structs are only supported as return value if the function is "
+           "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()"
+           "+ffibuilder.set_source() and not taking a final '...' argument)")
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_verify.py b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_verify.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_verify.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi0/test_verify.py
@@ -1076,9 +1076,13 @@
         int (*foo)(struct foo_s s) = &foo1;
     """)
     e = py.test.raises(NotImplementedError, lib.foo, "?")
-    msg = ("ctype 'struct foo_s' not supported as argument (it is a struct "
-           'declared with "...;", but the C calling convention may depend '
-           'on the missing fields)')
+    msg = ("ctype 'struct foo_s' not supported as argument.  It is a struct "
+           'declared with "...;", but the C calling convention may depend on '
+           "the missing fields; or, it contains anonymous struct/unions.  "
+           "Such structs are only supported as argument "
+           "if the function is 'API mode' and non-variadic (i.e. declared "
+           "inside ffibuilder.cdef()+ffibuilder.set_source() and not taking "
+           "a final '...' argument)")
     assert str(e.value) == msg
 
 def test_func_returns_struct():
@@ -2147,14 +2151,23 @@
     # assert did not crash so far
     e = py.test.raises(NotImplementedError, fooptr, ffi.new("Data *"))
     assert str(e.value) == (
-        "ctype 'Data' (size 4) not supported as argument")
+        "ctype 'Data' not supported as argument by libffi.  Unions are only "
+        "supported as argument if the function is 'API mode' and "
+        "non-variadic (i.e. declared inside ffibuilder.cdef()+"
+        "ffibuilder.set_source() and not taking a final '...' argument)")
     e = py.test.raises(NotImplementedError, bazptr)
     assert str(e.value) == (
-        "ctype 'Data' (size 4) not supported as return value")
+        "ctype 'Data' not supported as return value by libffi.  Unions are "
+        "only supported as return value if the function is 'API mode' and "
+        "non-variadic (i.e. declared inside ffibuilder.cdef()+"
+        "ffibuilder.set_source() and not taking a final '...' argument)")
     e = py.test.raises(NotImplementedError, barptr)
     assert str(e.value) == (
-        "ctype 'MyStr' not supported as return value "
-        "(it is a struct with bit fields)")
+        "ctype 'MyStr' not supported as return value.  It is a struct with "
+        "bit fields, which libffi does not support.  Such structs are only "
+        "supported as return value if the function is 'API mode' and non-"
+        "variadic (i.e. declared inside ffibuilder.cdef()+ffibuilder."
+        "set_source() and not taking a final '...' argument)")
 
 def test_verify_extra_arguments():
     ffi = FFI()
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_recompiler.py
@@ -2013,3 +2013,159 @@
     py.test.raises(ffi.error, ffi.sizeof, "vmat_t")
     p = ffi.new("vmat_t", 4)
     assert ffi.sizeof(p[3]) == 8 * ffi.sizeof("int")
+
+def test_call_with_custom_field_pos():
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo { int x; ...; };
+        struct foo f(void);
+        struct foo g(int, ...);
+    """)
+    lib = verify(ffi, "test_call_with_custom_field_pos", """
+        struct foo { int y, x; };
+        struct foo f(void) {
+            struct foo s = { 40, 200 };
+            return s;
+        }
+        struct foo g(int a, ...) { }
+    """)
+    assert lib.f().x == 200
+    e = py.test.raises(NotImplementedError, lib.g, 0)
+    assert str(e.value) == (
+        'ctype \'struct foo\' not supported as return value.  It is a '
+        'struct declared with "...;", but the C calling convention may '
+        'depend on the missing fields; or, it contains anonymous '
+        'struct/unions.  Such structs are only supported '
+        'as return value if the function is \'API mode\' and non-variadic '
+        '(i.e. declared inside ffibuilder.cdef()+ffibuilder.set_source() '
+        'and not taking a final \'...\' argument)')
+
+def test_call_with_nested_anonymous_struct():
+    if sys.platform == 'win32':
+        py.test.skip("needs a GCC extension")
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo { int a; union { int b, c; }; };
+        struct foo f(void);
+        struct foo g(int, ...);
+    """)
+    lib = verify(ffi, "test_call_with_nested_anonymous_struct", """
+        struct foo { int a; union { int b, c; }; };
+        struct foo f(void) {
+            struct foo s = { 40 };
+            s.b = 200;
+            return s;
+        }
+        struct foo g(int a, ...) { }
+    """)
+    assert lib.f().b == 200
+    e = py.test.raises(NotImplementedError, lib.g, 0)
+    assert str(e.value) == (
+        'ctype \'struct foo\' not supported as return value.  It is a '
+        'struct declared with "...;", but the C calling convention may '
+        'depend on the missing fields; or, it contains anonymous '
+        'struct/unions.  Such structs are only supported '
+        'as return value if the function is \'API mode\' and non-variadic '
+        '(i.e. declared inside ffibuilder.cdef()+ffibuilder.set_source() '
+        'and not taking a final \'...\' argument)')
+
+def test_call_with_bitfield():
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo { int x:5; };
+        struct foo f(void);
+        struct foo g(int, ...);
+    """)
+    lib = verify(ffi, "test_call_with_bitfield", """
+        struct foo { int x:5; };
+        struct foo f(void) {
+            struct foo s = { 11 };
+            return s;
+        }
+        struct foo g(int a, ...) { }
+    """)
+    assert lib.f().x == 11
+    e = py.test.raises(NotImplementedError, lib.g, 0)
+    assert str(e.value) == (
+        "ctype 'struct foo' not supported as return value.  It is a struct "
+        "with bit fields, which libffi does not support.  Such structs are "
+        "only supported as return value if the function is 'API mode' and "
+        "non-variadic (i.e. declared inside ffibuilder.cdef()+ffibuilder."
+        "set_source() and not taking a final '...' argument)")
+
+def test_call_with_zero_length_field():
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo { int a; int x[0]; };
+        struct foo f(void);
+        struct foo g(int, ...);
+    """)
+    lib = verify(ffi, "test_call_with_zero_length_field", """
+        struct foo { int a; int x[0]; };
+        struct foo f(void) {
+            struct foo s = { 42 };
+            return s;
+        }
+        struct foo g(int a, ...) { }
+    """)
+    assert lib.f().a == 42
+    e = py.test.raises(NotImplementedError, lib.g, 0)
+    assert str(e.value) == (
+        "ctype 'struct foo' not supported as return value.  It is a "
+        "struct with a zero-length array, which libffi does not support."
+        "  Such structs are only supported as return value if the function is "
+        "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()"
+        "+ffibuilder.set_source() and not taking a final '...' argument)")
+
+def test_call_with_union():
+    ffi = FFI()
+    ffi.cdef("""
+        union foo { int a; char b; };
+        union foo f(void);
+        union foo g(int, ...);
+    """)
+    lib = verify(ffi, "test_call_with_union", """
+        union foo { int a; char b; };
+        union foo f(void) {
+            union foo s = { 42 };
+            return s;
+        }
+        union foo g(int a, ...) { }
+    """)
+    assert lib.f().a == 42
+    e = py.test.raises(NotImplementedError, lib.g, 0)
+    assert str(e.value) == (
+        "ctype 'union foo' not supported as return value by libffi.  "
+        "Unions are only supported as return value if the function is "
+        "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()"
+        "+ffibuilder.set_source() and not taking a final '...' argument)")
+
+def test_call_with_packed_struct():
+    if sys.platform == 'win32':
+        py.test.skip("needs a GCC extension")
+    ffi = FFI()
+    ffi.cdef("""
+        struct foo { char y; int x; };
+        struct foo f(void);
+        struct foo g(int, ...);
+    """, packed=True)
+    lib = verify(ffi, "test_call_with_packed_struct", """
+        struct foo { char y; int x; } __attribute__((packed));
+        struct foo f(void) {
+            struct foo s = { 40, 200 };
+            return s;
+        }
+        struct foo g(int a, ...) {
+            struct foo s = { 41, 201 };
+            return s;
+        }
+    """)
+    assert lib.f().y == chr(40)
+    assert lib.f().x == 200
+    e = py.test.raises(NotImplementedError, lib.g, 0)
+    assert str(e.value) == (
+        "ctype 'struct foo' not supported as return value.  It is a "
+        "'packed' structure, with a different layout than expected by libffi."
+        "  Such structs are only supported as return value if the function is "
+        "'API mode' and non-variadic (i.e. declared inside ffibuilder.cdef()"
+        "+ffibuilder.set_source() and not taking a final '...' argument)")
diff --git a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_verify1.py b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_verify1.py
--- a/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_verify1.py
+++ b/pypy/module/test_lib_pypy/cffi_tests/cffi1/test_verify1.py
@@ -1043,9 +1043,13 @@
         int (*foo)(struct foo_s s) = &foo1;
     """)
     e = py.test.raises(NotImplementedError, lib.foo, "?")
-    msg = ("ctype 'struct foo_s' not supported as argument (it is a struct "
-           'declared with "...;", but the C calling convention may depend '
-           'on the missing fields)')
+    msg = ("ctype 'struct foo_s' not supported as argument.  It is a struct "
+           'declared with "...;", but the C calling convention may depend on '
+           "the missing fields; or, it contains anonymous struct/unions.  "
+           "Such structs are only supported as argument "
+           "if the function is 'API mode' and non-variadic (i.e. declared "
+           "inside ffibuilder.cdef()+ffibuilder.set_source() and not taking "
+           "a final '...' argument)")
     assert str(e.value) == msg
 
 def test_func_returns_struct():
@@ -2115,14 +2119,23 @@
     # assert did not crash so far
     e = py.test.raises(NotImplementedError, fooptr, ffi.new("Data *"))
     assert str(e.value) == (
-        "ctype 'Data' (size 4) not supported as argument")
+        "ctype 'Data' not supported as argument by libffi.  Unions are only "
+        "supported as argument if the function is 'API mode' and "
+        "non-variadic (i.e. declared inside ffibuilder.cdef()+"
+        "ffibuilder.set_source() and not taking a final '...' argument)")
     e = py.test.raises(NotImplementedError, bazptr)
     assert str(e.value) == (
-        "ctype 'Data' (size 4) not supported as return value")
+        "ctype 'Data' not supported as return value by libffi.  Unions are "
+        "only supported as return value if the function is 'API mode' and "
+        "non-variadic (i.e. declared inside ffibuilder.cdef()+"
+        "ffibuilder.set_source() and not taking a final '...' argument)")
     e = py.test.raises(NotImplementedError, barptr)
     assert str(e.value) == (
-        "ctype 'MyStr' not supported as return value "
-        "(it is a struct with bit fields)")
+        "ctype 'MyStr' not supported as return value.  It is a struct with "
+        "bit fields, which libffi does not support.  Such structs are only "
+        "supported as return value if the function is 'API mode' and non-"
+        "variadic (i.e. declared inside ffibuilder.cdef()+ffibuilder."
+        "set_source() and not taking a final '...' argument)")
 
 def test_verify_extra_arguments():
     ffi = FFI()


More information about the pypy-commit mailing list