Commits

Armin Rigo committed 6372688

Still trying to fix up the ordering of building stuff. One test passes,
but one test still fails.

  • Participants
  • Parent commits ce32e64

Comments (0)

Files changed (3)

         try:
             BType = self._cached_btypes[type]
         except KeyError:
-            BType = type.finish_backend_type(self)
-            BType2 = self._cached_btypes.setdefault(type, BType)
-            assert BType2 is BType
+            finishlist = []
+            BType = type.get_cached_btype(self, finishlist)
+            for type in finishlist:
+                type.finish_backend_type(self, finishlist)
         return BType
 
     def verify(self, source='', tmpdir=None, **kwargs):
     def has_c_name(self):
         return '$' not in self._get_c_name('')
 
+    def get_cached_btype(self, ffi, finishlist, can_delay=False):
+        try:
+            BType = ffi._cached_btypes[self]
+        except KeyError:
+            BType = self.build_backend_type(ffi, finishlist)
+            BType2 = ffi._cached_btypes.setdefault(self, BType)
+            assert BType2 is BType
+        return BType
+
     def __repr__(self):
         return '<%s>' % (self._get_c_name(''),)
 
     def _get_c_name(self, replace_with):
         return 'void' + replace_with
 
-    def finish_backend_type(self, ffi):
+    def build_backend_type(self, ffi, finishlist):
         return global_cache(self, ffi, 'new_void_type')
 
 void_type = VoidType()
     def is_float_type(self):
         return self.name in ('double', 'float')
 
-    def finish_backend_type(self, ffi):
+    def build_backend_type(self, ffi, finishlist):
         return global_cache(self, ffi, 'new_primitive_type', self.name)
 
 
     # a function, but not a pointer-to-function.  The backend has no
     # notion of such a type; it's used temporarily by parsing.
 
-    def finish_backend_type(self, ffi):
+    def build_backend_type(self, ffi, finishlist):
         from . import api
         raise api.CDefError("cannot render the type %r: it is a function "
                             "type, not a pointer-to-function type" % (self,))
     def _get_c_name(self, replace_with):
         return BaseFunctionType._get_c_name(self, '*'+replace_with)
 
-    def finish_backend_type(self, ffi):
-        result = ffi._get_cached_btype(self.result)
+    def build_backend_type(self, ffi, finishlist):
+        result = self.result.get_cached_btype(ffi, finishlist)
         args = []
         for tp in self.args:
-            args.append(ffi._get_cached_btype(tp))
+            args.append(tp.get_cached_btype(ffi, finishlist))
         return global_cache(self, ffi, 'new_function_type',
                             tuple(args), result, self.ellipsis)
 
     def _get_c_name(self, replace_with):
         return self.totype._get_c_name('* ' + replace_with)
 
-    def finish_backend_type(self, ffi):
-        BItem = ffi._get_cached_btype(self.totype)
+    def build_backend_type(self, ffi, finishlist):
+        BItem = self.totype.get_cached_btype(ffi, finishlist, can_delay=True)
         return global_cache(self, ffi, 'new_pointer_type', BItem)
 
 
     def _get_c_name(self, replace_with):
         return self.totype._get_c_name(' const * ' + replace_with)
 
-    def finish_backend_type(self, ffi):
-        BPtr = ffi._get_cached_btype(PointerType(self.totype))
+    def build_backend_type(self, ffi, finishlist):
+        BPtr = PointerType(self.totype).get_cached_btype(ffi, finishlist)
         return BPtr
 
 
             brackets = '[%d]' % self.length
         return self.item._get_c_name(replace_with + brackets)
 
-    def finish_backend_type(self, ffi):
-        BPtrItem = ffi._get_cached_btype(PointerType(self.item))
+    def build_backend_type(self, ffi, finishlist):
+        self.item.get_cached_btype(ffi, finishlist)   # force the item BType
+        BPtrItem = PointerType(self.item).get_cached_btype(ffi, finishlist)
         return global_cache(self, ffi, 'new_array_type', BPtrItem, self.length)
 
 
 
 class StructOrUnion(StructOrUnionOrEnum):
     fixedlayout = None
+    completed = False
 
     def __init__(self, name, fldnames, fldtypes, fldbitsize):
         self.name = name
         self.fldtypes = tuple(types)
         self.fldbitsize = tuple(bitsizes)
 
-    def finish_backend_type(self, ffi):
-        BType = self.new_btype(ffi)
-        ffi._cached_btypes[self] = BType
+    def get_cached_btype(self, ffi, finishlist, can_delay=False):
+        BType = StructOrUnionOrEnum.get_cached_btype(self, ffi, finishlist,
+                                                     can_delay)
+        if not can_delay:
+            self.finish_backend_type(ffi, finishlist)
+        return BType
+
+    def finish_backend_type(self, ffi, finishlist):
+        if self.completed:
+            if self.completed != 2:
+                raise NotImplementedError("recursive structure declaration "
+                                          "for '%s'" % (self.name,))
+            return
+        BType = ffi._cached_btypes[self]
         if self.fldtypes is None:
-            return BType    # not completing it: it's an opaque struct
+            return    # not completing it: it's an opaque struct
         #
-        fldtypes = tuple(ffi._get_cached_btype(tp) for tp in self.fldtypes)
+        self.completed = 1
+        fldtypes = tuple(tp.get_cached_btype(ffi, finishlist)
+                         for tp in self.fldtypes)
         #
         if self.fixedlayout is None:
             lst = list(zip(self.fldnames, fldtypes, self.fldbitsize))
                 #
                 if isinstance(ftype, ArrayType) and ftype.length is None:
                     # fix the length to match the total size
-                    BItemType = ffi._get_cached_btype(ftype.item)
+                    BItemType = ftype.item.get_cached_btype(ffi, finishlist)
                     nlen, nrest = divmod(fsize, ffi.sizeof(BItemType))
                     if nrest != 0:
                         self._verification_error(
                     ftype = ftype.resolve_length(nlen)
                     self.fldtypes = (self.fldtypes[:i] + (ftype,) +
                                      self.fldtypes[i+1:])
-                    BArrayType = ffi._get_cached_btype(ftype)
+                    BArrayType = ftype.get_cached_btype(ffi, finishlist)
                     fldtypes = (fldtypes[:i] + (BArrayType,) +
                                 fldtypes[i+1:])
                     continue
             lst = list(zip(self.fldnames, fldtypes, self.fldbitsize, fieldofs))
             ffi._backend.complete_struct_or_union(BType, lst, self,
                                                   totalsize, totalalignment)
-        return BType
+        self.completed = 2
 
     def _verification_error(self, msg):
         from .ffiplatform import VerificationError
             from . import ffiplatform
             raise ffiplatform.VerificationMissing(self._get_c_name(''))
 
-    def new_btype(self, ffi):
+    def build_backend_type(self, ffi, finishlist):
         self.check_not_partial()
+        finishlist.append(self)
         return ffi._backend.new_struct_type(self.name)
 
 
 class UnionType(StructOrUnion):
     kind = 'union'
 
-    def new_btype(self, ffi):
+    def build_backend_type(self, ffi, finishlist):
+        finishlist.append(self)
         return ffi._backend.new_union_type(self.name)
 
 
             from . import ffiplatform
             raise ffiplatform.VerificationMissing(self._get_c_name(''))
 
-    def finish_backend_type(self, ffi):
+    def build_backend_type(self, ffi, finishlist):
         self.check_not_partial()
         return ffi._backend.new_enum_type(self.name, self.enumerators,
                                           self.enumvalues)

testing/backend_tests.py

     def test_use_own_bool(self):
         ffi = FFI(backend=self.Backend())
         ffi.cdef("""typedef int bool;""")
+
+    def test_ordering_bug1(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            struct foo_s {
+                struct bar_s *p;
+            };
+            struct bar_s {
+                struct foo_s foo;
+            };
+        """)
+        q = ffi.new("struct foo_s *")
+        bar = ffi.new("struct bar_s *")
+        q.p = bar
+        assert q.p.foo.p == ffi.NULL
+
+    def test_ordering_bug2(self):
+        ffi = FFI(backend=self.Backend())
+        ffi.cdef("""
+            struct foo_s {
+                struct bar_s (*p)[3];
+            };
+            struct bar_s {
+                struct foo_s foo;
+            };
+        """)
+        q = ffi.new("struct foo_s *")
+        bar = ffi.new("struct bar_s *")
+        q.p[1] = bar
+        assert q.p[1].foo.p[1] == ffi.NULL