Commits

Armin Rigo committed 5498063

Improve error reporting for functions that return an opaque type.

Comments (0)

Files changed (4)

c/_cffi_backend.c

     }
     if ((fresult->ct_size < 0 && !(fresult->ct_flags & CT_VOID)) ||
         (fresult->ct_flags & CT_ARRAY)) {
-        PyErr_Format(PyExc_TypeError, "invalid result type: '%s'",
-                     fresult->ct_name);
+        char *msg;
+        if (fresult->ct_flags & CT_IS_OPAQUE)
+            msg = "result type '%s' is opaque";
+        else
+            msg = "invalid result type: '%s'";
+        PyErr_Format(PyExc_TypeError, msg, fresult->ct_name);
         return NULL;
     }
 

cffi/vengine_cpy.py

             tovar, tp.get_c_name(''), errvalue))
         self._prnt('    %s;' % errcode)
 
-    def _convert_expr_from_c(self, tp, var):
+    def _convert_expr_from_c(self, tp, var, where):
         if isinstance(tp, model.PrimitiveType):
             if tp.is_integer_type():
                 if tp.is_signed_type():
             return '_cffi_from_c_deref((char *)%s, _cffi_type(%d))' % (
                 var, self._gettypenum(tp))
         elif isinstance(tp, model.StructType):
+            if tp.fldnames is None:
+                raise TypeError("'%s' is used as %s, but is opaque" % (
+                    tp._get_c_name(''), where))
             return '_cffi_from_c_struct((char *)&%s, _cffi_type(%d))' % (
                 var, self._gettypenum(tp))
         elif isinstance(tp, model.EnumType):
         #
         if result_code:
             prnt('  return %s;' %
-                 self._convert_expr_from_c(tp.result, 'result'))
+                 self._convert_expr_from_c(tp.result, 'result',
+                                           'result of %s()' % name))
         else:
             prnt('  Py_INCREF(Py_None);')
             prnt('  return Py_None;')
             else:
                 realexpr = name
             prnt('  i = (%s);' % (realexpr,))
-            prnt('  o = %s;' % (self._convert_expr_from_c(tp, 'i'),))
+            prnt('  o = %s;' % (self._convert_expr_from_c(tp, 'i',
+                                                'type of %s' % name),))
             assert delayed
         else:
             prnt('  if (LONG_MIN <= (%s) && (%s) <= LONG_MAX)' % (name, name))

cffi/vengine_gen.py

                     indirect_args.append(type)
                 tp = model.FunctionPtrType(tuple(indirect_args),
                                            tp.result, tp.ellipsis)
-            BFunc = self.ffi._get_cached_btype(tp)
+            try:
+                BFunc = self.ffi._get_cached_btype(tp)
+            except TypeError, e:
+                msg = 'function %s(): %s' % (name, e)
+                raise TypeError(msg)
             wrappername = '_cffi_f_%s' % name
             newfunction = module.load_function(BFunc, wrappername)
             for i, type in indirections:

testing/test_verify.py

     ffi.cdef("int fooarray[...];")
     lib = ffi.verify("int fooarray[50];")
     assert repr(lib.fooarray).startswith("<cdata 'int *'")
+
+def test_struct_containing_struct():
+    ffi = FFI()
+    ffi.cdef("struct foo_s { ...; }; struct bar_s { struct foo_s f; ...; };")
+    ffi.verify("struct foo_s { int x; }; struct bar_s { struct foo_s f; };")
+    #
+    ffi = FFI()
+    ffi.cdef("struct foo_s { struct bar_s f; ...; }; struct bar_s { ...; };")
+    ffi.verify("struct bar_s { int x; }; struct foo_s { struct bar_s f; };")
+
+def test_struct_returned_by_func():
+    ffi = FFI()
+    ffi.cdef("typedef ... foo_t; foo_t myfunc(void);")
+    e = py.test.raises(TypeError, ffi.verify,
+                       "typedef struct { int x; } foo_t; "
+                       "foo_t myfunc(void) { foo_t x = { 42 }; return x; }")
+    assert str(e.value) in [
+        "'foo_t' is used as result of myfunc(), but is opaque",
+        "function myfunc(): result type 'struct $foo_t' is opaque"]