Author: Armin Rigo <ar...@tunes.org>
Branch: 
Changeset: r2761:58bab5bcadd2
Date: 2016-09-05 23:55 +0200
http://bitbucket.org/cffi/cffi/changeset/58bab5bcadd2/

Log:    Issue #283: initializer for nested anonymous structs inside unions

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -187,10 +187,12 @@
     Py_ssize_t cf_offset;
     short cf_bitshift;   /* >= 0: bitshift; or BS_REGULAR or BS_EMPTY_ARRAY */
     short cf_bitsize;
+    unsigned char cf_flags;   /* BF_... */
     struct cfieldobject_s *cf_next;
 } CFieldObject;
 #define BS_REGULAR     (-1)      /* a regular field, not with bitshift */
 #define BS_EMPTY_ARRAY (-2)      /* a field which is an array 'type[0]' */
+#define BF_IGNORE_IN_CTOR  0x01  /* union field not in the first place */
 
 static PyTypeObject CTypeDescr_Type;
 static PyTypeObject CField_Type;
@@ -657,6 +659,7 @@
     {"offset", T_PYSSIZET, OFF(cf_offset), READONLY},
     {"bitshift", T_SHORT, OFF(cf_bitshift), READONLY},
     {"bitsize", T_SHORT, OFF(cf_bitsize), READONLY},
+    {"flags", T_UBYTE, OFF(cf_flags), READONLY},
     {NULL}      /* Sentinel */
 };
 #undef OFF
@@ -1274,24 +1277,14 @@
         return -1;
     }
 
-    if (ct->ct_flags & CT_UNION) {
-        Py_ssize_t n = PyObject_Size(init);
-        if (n < 0)
-            return -1;
-        if (n > 1) {
-            PyErr_Format(PyExc_ValueError,
-                         "initializer for '%s': %zd items given, but "
-                         "only one supported (use a dict if needed)",
-                         ct->ct_name, n);
-            return -1;
-        }
-    }
     if (PyList_Check(init) || PyTuple_Check(init)) {
         PyObject **items = PySequence_Fast_ITEMS(init);
         Py_ssize_t i, n = PySequence_Fast_GET_SIZE(init);
         CFieldObject *cf = (CFieldObject *)ct->ct_extra;
 
         for (i=0; i<n; i++) {
+            while (cf != NULL && (cf->cf_flags & BF_IGNORE_IN_CTOR))
+                cf = cf->cf_next;
             if (cf == NULL) {
                 PyErr_Format(PyExc_ValueError,
                              "too many initializers for '%s' (got %zd)",
@@ -4085,7 +4078,7 @@
 
 static CFieldObject *
 _add_field(PyObject *interned_fields, PyObject *fname, CTypeDescrObject *ftype,
-           Py_ssize_t offset, int bitshift, int fbitsize)
+           Py_ssize_t offset, int bitshift, int fbitsize, int flags)
 {
     int err;
     Py_ssize_t prev_size;
@@ -4098,6 +4091,7 @@
     cf->cf_offset = offset;
     cf->cf_bitshift = bitshift;
     cf->cf_bitsize = fbitsize;
+    cf->cf_flags = flags;
 
     Py_INCREF(fname);
     PyText_InternInPlace(&fname);
@@ -4184,7 +4178,7 @@
     int totalalignment = -1;
     CFieldObject **previous;
     int prev_bitfield_size, prev_bitfield_free;
-    int sflags = 0;
+    int sflags = 0, fflags;
 
     if (!PyArg_ParseTuple(args, "O!O!|Onii:complete_struct_or_union",
                           &CTypeDescr_Type, &ct,
@@ -4270,6 +4264,8 @@
         if (alignment < falign && do_align)
             alignment = falign;
 
+        fflags = (is_union && i > 0) ? BF_IGNORE_IN_CTOR : 0;
+
         if (fbitsize < 0) {
             /* not a bitfield: common case */
             int bs_flag;
@@ -4305,7 +4301,8 @@
                                            cfsrc->cf_type,
                                            boffset / 8 + cfsrc->cf_offset,
                                            cfsrc->cf_bitshift,
-                                           cfsrc->cf_bitsize);
+                                           cfsrc->cf_bitsize,
+                                           cfsrc->cf_flags | fflags);
                     if (*previous == NULL)
                         goto error;
                     previous = &(*previous)->cf_next;
@@ -4315,7 +4312,7 @@
             }
             else {
                 *previous = _add_field(interned_fields, fname, ftype,
-                                        boffset / 8, bs_flag, -1);
+                                        boffset / 8, bs_flag, -1, fflags);
                 if (*previous == NULL)
                     goto error;
                 previous = &(*previous)->cf_next;
@@ -4445,7 +4442,8 @@
                     bitshift = 8 * ftype->ct_size - fbitsize - bitshift;
 
                 *previous = _add_field(interned_fields, fname, ftype,
-                                       field_offset_bytes, bitshift, fbitsize);
+                                       field_offset_bytes, bitshift, fbitsize,
+                                       fflags);
                 if (*previous == NULL)
                     goto error;
                 previous = &(*previous)->cf_next;
diff --git a/testing/cffi0/backend_tests.py b/testing/cffi0/backend_tests.py
--- a/testing/cffi0/backend_tests.py
+++ b/testing/cffi0/backend_tests.py
@@ -1414,6 +1414,7 @@
         assert p.b == 12
         assert p.c == 14
         assert p.d == 14
+        py.test.raises(ValueError, ffi.new, "struct foo_s *", [0, 0, 0, 0])
 
     def test_nested_field_offset_align(self):
         ffi = FFI(backend=self.Backend())
@@ -1453,14 +1454,42 @@
         assert p.b == 0
         assert p.c == 14
         assert p.d == 14
-        p = ffi.new("union foo_u *", {'b': 12})
-        assert p.a == 0
+        p = ffi.new("union foo_u *", {'a': -63, 'b': 12})
+        assert p.a == -63
         assert p.b == 12
-        assert p.c == 0
-        assert p.d == 0
-        # we cannot specify several items in the dict, even though
-        # in theory in this particular case it would make sense
-        # to give both 'a' and 'b'
+        assert p.c == -63
+        assert p.d == -63
+        p = ffi.new("union foo_u *", [123, 456])
+        assert p.a == 123
+        assert p.b == 456
+        assert p.c == 123
+        assert p.d == 123
+        py.test.raises(ValueError, ffi.new, "union foo_u *", [0, 0, 0])
+
+    def test_nested_anonymous_struct_2(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            struct foo_s {
+                int a;
+                union { int b; union { int c, d; }; };
+                int e;
+            };
+        """)
+        assert ffi.sizeof("struct foo_s") == 3 * SIZE_OF_INT
+        p = ffi.new("struct foo_s *", [11, 22, 33])
+        assert p.a == 11
+        assert p.b == p.c == p.d == 22
+        assert p.e == 33
+        py.test.raises(ValueError, ffi.new, "struct foo_s *", [11, 22, 33, 44])
+        FOO = ffi.typeof("struct foo_s")
+        fields = [(name, fld.offset, fld.flags) for (name, fld) in FOO.fields]
+        assert fields == [
+            ('a', 0 * SIZE_OF_INT, 0),
+            ('b', 1 * SIZE_OF_INT, 0),
+            ('c', 1 * SIZE_OF_INT, 1),
+            ('d', 1 * SIZE_OF_INT, 1),
+            ('e', 2 * SIZE_OF_INT, 0),
+        ]
 
     def test_cast_to_array_type(self):
         ffi = FFI(backend=self.Backend())
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to