[pypy-commit] cffi default: Issues 69, 73: add the syntax "typedef int... foo_t; ".

arigo noreply at buildbot.pypy.org
Sat May 30 12:56:22 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r2130:43ee9d0dd03f
Date: 2015-05-30 12:57 +0200
http://bitbucket.org/cffi/cffi/changeset/43ee9d0dd03f/

Log:	Issues 69, 73: add the syntax "typedef int... foo_t;".

diff --git a/cffi/_cffi_include.h b/cffi/_cffi_include.h
--- a/cffi/_cffi_include.h
+++ b/cffi/_cffi_include.h
@@ -208,12 +208,10 @@
 #define _cffi_array_len(array)   (sizeof(array) / sizeof((array)[0]))
 
 #define _cffi_prim_int(size, sign)                                      \
-    ((size) == sizeof(int) ? ((sign) ? _CFFI_PRIM_INT   : _CFFI_PRIM_UINT)   : \
-     (size) == sizeof(long)? ((sign) ? _CFFI_PRIM_LONG  : _CFFI_PRIM_ULONG)  : \
-     (size) == 1           ? ((sign) ? _CFFI_PRIM_INT8  : _CFFI_PRIM_UINT8)  : \
-     (size) == 2           ? ((sign) ? _CFFI_PRIM_INT16 : _CFFI_PRIM_UINT16) : \
-     (size) == 4           ? ((sign) ? _CFFI_PRIM_INT32 : _CFFI_PRIM_UINT32) : \
-     (size) == 8           ? ((sign) ? _CFFI_PRIM_INT64 : _CFFI_PRIM_UINT64) : \
+    ((size) == 1 ? ((sign) ? _CFFI_PRIM_INT8  : _CFFI_PRIM_UINT8)  :    \
+     (size) == 2 ? ((sign) ? _CFFI_PRIM_INT16 : _CFFI_PRIM_UINT16) :    \
+     (size) == 4 ? ((sign) ? _CFFI_PRIM_INT32 : _CFFI_PRIM_UINT32) :    \
+     (size) == 8 ? ((sign) ? _CFFI_PRIM_INT64 : _CFFI_PRIM_UINT64) :    \
      0)
 
 #define _cffi_check_int(got, got_nonpos, expected)      \
diff --git a/cffi/cffi_opcode.py b/cffi/cffi_opcode.py
--- a/cffi/cffi_opcode.py
+++ b/cffi/cffi_opcode.py
@@ -9,7 +9,7 @@
             assert isinstance(self.arg, str)
             return '(_cffi_opcode_t)(%s)' % (self.arg,)
         classname = CLASS_NAME[self.op]
-        return '_CFFI_OP(_CFFI_OP_%s, %d)' % (classname, self.arg)
+        return '_CFFI_OP(_CFFI_OP_%s, %s)' % (classname, self.arg)
 
     def as_python_bytes(self):
         if self.op is None:
diff --git a/cffi/cparser.py b/cffi/cparser.py
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -189,8 +189,8 @@
                         raise api.CDefError("typedef does not declare any name",
                                             decl)
                     if (isinstance(decl.type.type, pycparser.c_ast.IdentifierType)
-                            and decl.type.type.names == ['__dotdotdot__']):
-                        realtype = model.unknown_type(decl.name)
+                            and decl.type.type.names[-1] == '__dotdotdot__'):
+                        realtype = self._get_unknown_type(decl)
                     elif (isinstance(decl.type, pycparser.c_ast.PtrDecl) and
                           isinstance(decl.type.type, pycparser.c_ast.TypeDecl) and
                           isinstance(decl.type.type.type,
@@ -271,14 +271,12 @@
                 if tp.is_raw_function:
                     tp = self._get_type_pointer(tp)
                     self._declare('function ' + decl.name, tp)
-                elif (isinstance(tp, model.PrimitiveType) and
-                        tp.is_integer_type() and
+                elif (tp.is_integer_type() and
                         hasattr(decl, 'init') and
                         hasattr(decl.init, 'value') and
                         _r_int_literal.match(decl.init.value)):
                     self._add_integer_constant(decl.name, decl.init.value)
-                elif (isinstance(tp, model.PrimitiveType) and
-                        tp.is_integer_type() and
+                elif (tp.is_integer_type() and
                         isinstance(decl.init, pycparser.c_ast.UnaryOp) and
                         decl.init.op == '-' and
                         hasattr(decl.init.expr, 'value') and
@@ -641,3 +639,13 @@
                 self._declare(name, tp, included=True)
         for k, v in other._int_constants.items():
             self._add_constants(k, v)
+
+    def _get_unknown_type(self, decl):
+        typenames = decl.type.type.names
+        assert typenames[-1] == '__dotdotdot__'
+        if len(typenames) == 1:
+            return model.unknown_type(decl.name)
+        for t in typenames[:-1]:
+            if t not in ['int', 'short', 'long', 'signed', 'unsigned', 'char']:
+                raise api.FFIError(':%d: bad usage of "..."' % decl.coord.line)
+        return model.UnknownIntegerType(decl.name)
diff --git a/cffi/model.py b/cffi/model.py
--- a/cffi/model.py
+++ b/cffi/model.py
@@ -31,7 +31,10 @@
 
     def has_c_name(self):
         return '$' not in self._get_c_name()
-    
+
+    def is_integer_type(self):
+        return False
+
     def sizeof_enabled(self):
         return False
 
@@ -76,7 +79,12 @@
 void_type = VoidType()
 
 
-class PrimitiveType(BaseType):
+class BasePrimitiveType(BaseType):
+    def sizeof_enabled(self):
+        return True
+
+
+class PrimitiveType(BasePrimitiveType):
     _attrs_ = ('name',)
 
     ALL_PRIMITIVE_TYPES = {
@@ -142,11 +150,23 @@
     def is_float_type(self):
         return self.ALL_PRIMITIVE_TYPES[self.name] == 'f'
 
-    def sizeof_enabled(self):
-        return True
+    def build_backend_type(self, ffi, finishlist):
+        return global_cache(self, ffi, 'new_primitive_type', self.name)
+
+
+class UnknownIntegerType(BasePrimitiveType):
+    _attrs_ = ('name',)
+
+    def __init__(self, name):
+        self.name = name
+        self.c_name_with_marker = name + '&'
+
+    def is_integer_type(self):
+        return True    # for now
 
     def build_backend_type(self, ffi, finishlist):
-        return global_cache(self, ffi, 'new_primitive_type', self.name)
+        raise NotImplementedError("integer type '%s' can only be used after "
+                                  "compilation" % self.name)
 
 
 class BaseFunctionType(BaseType):
diff --git a/cffi/recompiler.py b/cffi/recompiler.py
--- a/cffi/recompiler.py
+++ b/cffi/recompiler.py
@@ -144,7 +144,7 @@
                 self.cffi_types.append(tp)     # placeholder
                 for tp1 in tp.args:
                     assert isinstance(tp1, (model.VoidType,
-                                            model.PrimitiveType,
+                                            model.BasePrimitiveType,
                                             model.PointerType,
                                             model.StructOrUnionOrEnum,
                                             model.FunctionPtrType))
@@ -469,7 +469,7 @@
 
     def _convert_funcarg_to_c(self, tp, fromvar, tovar, errcode):
         extraarg = ''
-        if isinstance(tp, model.PrimitiveType):
+        if isinstance(tp, model.BasePrimitiveType):
             if tp.is_integer_type() and tp.name != '_Bool':
                 converter = '_cffi_to_c_int'
                 extraarg = ', %s' % tp.name
@@ -524,7 +524,7 @@
         self._prnt('  }')
 
     def _convert_expr_from_c(self, tp, var, context):
-        if isinstance(tp, model.PrimitiveType):
+        if isinstance(tp, model.BasePrimitiveType):
             if tp.is_integer_type():
                 return '_cffi_from_c_int(%s, %s)' % (var, tp.name)
             elif tp.name != 'long double':
@@ -773,8 +773,7 @@
         prnt('  (void)p;')
         for fname, ftype, fbitsize in tp.enumfields():
             try:
-                if (isinstance(ftype, model.PrimitiveType)
-                    and ftype.is_integer_type()) or fbitsize >= 0:
+                if ftype.is_integer_type() or fbitsize >= 0:
                     # accept all integers, but complain on float or double
                     prnt('  (void)((p->%s) << 1);' % fname)
                     continue
@@ -967,17 +966,16 @@
         prnt()
 
     def _generate_cpy_constant_collecttype(self, tp, name):
-        is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
+        is_int = tp.is_integer_type()
         if not is_int or self.target_is_python:
             self._do_collect_type(tp)
 
     def _generate_cpy_constant_decl(self, tp, name):
-        is_int = isinstance(tp, model.PrimitiveType) and tp.is_integer_type()
+        is_int = tp.is_integer_type()
         self._generate_cpy_const(is_int, name, tp)
 
     def _generate_cpy_constant_ctx(self, tp, name):
-        if (not self.target_is_python and
-                isinstance(tp, model.PrimitiveType) and tp.is_integer_type()):
+        if not self.target_is_python and tp.is_integer_type():
             type_op = CffiOp(OP_CONSTANT_INT, -1)
         else:
             if not tp.sizeof_enabled():
@@ -1089,6 +1087,10 @@
         prim_index = PRIMITIVE_TO_INDEX[tp.name]
         self.cffi_types[index] = CffiOp(OP_PRIMITIVE, prim_index)
 
+    def _emit_bytecode_UnknownIntegerType(self, tp, index):
+        s = '_cffi_prim_int(sizeof(%s), ((%s)-1) <= 0)' % (tp.name, tp.name)
+        self.cffi_types[index] = CffiOp(OP_PRIMITIVE, s)
+
     def _emit_bytecode_RawFunctionType(self, tp, index):
         self.cffi_types[index] = CffiOp(OP_FUNCTION, self._typesdict[tp.result])
         index += 1
diff --git a/doc/source/cdef.rst b/doc/source/cdef.rst
--- a/doc/source/cdef.rst
+++ b/doc/source/cdef.rst
@@ -373,6 +373,15 @@
    declaration which doesn't use "``...``" is assumed to be exact, but this is
    checked: you get an error if it is not correct.
 
+*  *New in version 1.0.4:* integer types: the syntax "``typedef
+   int... foo_t;``" declares the type ``foo_t`` as an integer type
+   whose exact size and signness is not specified.  The compiler will
+   figure it out.  (Note that this requires ``set_source()``; it does
+   not work with ``verify()``.)  The ``int...`` can be replaced with
+   ``long...`` or ``unsigned long long...`` or any other primitive
+   integer type, with no effect.  The type will always map to one of
+   ``(u)int(8,16,32,64)_t``.
+
 *  unknown types: the syntax "``typedef ... foo_t;``" declares the type
    ``foo_t`` as opaque.  Useful mainly for when the API takes and returns
    ``foo_t *`` without you needing to look inside the ``foo_t``.  Also
@@ -414,7 +423,9 @@
    ``static char *const FOO;``).
 
 Currently, it is not supported to find automatically which of the
-various integer or float types you need at which place.  In the case of
+various integer or float types you need at which place.
+If a type is named, and an integer type, then use ``typedef
+int... the_type_name;``.  In the case of
 function arguments or return type, when it is a simple integer/float
 type, it may be misdeclared (if you misdeclare a function ``void
 f(long)`` as ``void f(int)``, it still works, but you have to call it
diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst
--- a/doc/source/whatsnew.rst
+++ b/doc/source/whatsnew.rst
@@ -6,6 +6,10 @@
 1.0.4
 =====
 
+* Out-of-line API mode: we can now declare integer types with
+  ``typedef int... foo_t;``.  The exact size and signness of ``foo_t``
+  is figured out by the compiler.
+
 * Out-of-line API mode: we can now declare multidimensional arrays
   (as fields or as globals) with ``int n[...][...]``.  Before, only the
   outermost dimension would support the ``...`` syntax.
diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py
--- a/testing/cffi1/test_recompiler.py
+++ b/testing/cffi1/test_recompiler.py
@@ -1,5 +1,5 @@
 import sys, os, py
-from cffi import FFI, VerificationError
+from cffi import FFI, FFIError, VerificationError
 from cffi import recompiler
 from testing.udir import udir
 from testing.support import u
@@ -932,3 +932,52 @@
     py.test.raises(IndexError, 'lib.a[10][0]')
     assert ffi.typeof(lib.a) == ffi.typeof("int[10][8]")
     assert ffi.typeof(lib.a[0]) == ffi.typeof("int[8]")
+
+def test_some_integer_type():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef int... foo_t;
+        typedef unsigned long... bar_t;
+        typedef struct { foo_t a, b; } mystruct_t;
+        foo_t foobar(bar_t, mystruct_t);
+        static const bar_t mu = -20;
+        static const foo_t nu = 20;
+    """)
+    lib = verify(ffi, 'test_some_integer_type', """
+        typedef unsigned long long foo_t;
+        typedef short bar_t;
+        typedef struct { foo_t a, b; } mystruct_t;
+        static foo_t foobar(bar_t x, mystruct_t s) {
+            return (foo_t)x + s.a + s.b;
+        }
+        static const bar_t mu = -20;
+        static const foo_t nu = 20;
+    """)
+    assert ffi.sizeof("foo_t") == ffi.sizeof("unsigned long long")
+    assert ffi.sizeof("bar_t") == ffi.sizeof("short")
+    maxulonglong = 2 ** 64 - 1
+    assert int(ffi.cast("foo_t", -1)) == maxulonglong
+    assert int(ffi.cast("bar_t", -1)) == -1
+    assert lib.foobar(-1, [0, 0]) == maxulonglong
+    assert lib.foobar(2 ** 15 - 1, [0, 0]) == 2 ** 15 - 1
+    assert lib.foobar(10, [20, 31]) == 61
+    assert lib.foobar(0, [0, maxulonglong]) == maxulonglong
+    py.test.raises(OverflowError, lib.foobar, 2 ** 15, [0, 0])
+    py.test.raises(OverflowError, lib.foobar, -(2 ** 15) - 1, [0, 0])
+    py.test.raises(OverflowError, ffi.new, "mystruct_t *", [0, -1])
+    assert lib.mu == -20
+    assert lib.nu == 20
+
+def test_unsupported_some_void_type():
+    ffi = FFI()
+    py.test.raises(FFIError, ffi.cdef, """typedef void... foo_t;""")
+
+def test_some_float_type():
+    py.test.skip("later")
+    ffi = FFI()
+    ffi.cdef("typedef double... foo_t; foo_t sum(foo_t[]);")
+    lib = verify(ffi, 'test_some_float_type', """
+        typedef float foo_t;
+        static foo_t sum(foo_t x[]) { return x[0] + x[1]; }
+    """)
+    assert lib.sum([40.0, 2.25]) == 42.25
diff --git a/testing/cffi1/test_verify1.py b/testing/cffi1/test_verify1.py
--- a/testing/cffi1/test_verify1.py
+++ b/testing/cffi1/test_verify1.py
@@ -2205,3 +2205,27 @@
     e = py.test.raises(ffi.error, "lib.FOO")
     assert str(e.value) == ("the C compiler says 'FOO' is equal to 124 (0x7c),"
                             " but the cdef disagrees")
+
+def test_some_integer_type_for_issue73():
+    ffi = FFI()
+    ffi.cdef("""
+        typedef int... AnIntegerWith32Bits;
+        typedef AnIntegerWith32Bits (*AFunctionReturningInteger) (void);
+        AnIntegerWith32Bits InvokeFunction(AFunctionReturningInteger);
+    """)
+    lib = ffi.verify("""
+        #ifdef __LP64__
+        typedef int AnIntegerWith32Bits;
+        #else
+        typedef long AnIntegerWith32Bits;
+        #endif
+        typedef AnIntegerWith32Bits (*AFunctionReturningInteger) (void);
+        AnIntegerWith32Bits InvokeFunction(AFunctionReturningInteger f) {
+            return f();
+        }
+    """)
+    @ffi.callback("AFunctionReturningInteger")
+    def add():
+        return 3 + 4
+    x = lib.InvokeFunction(add)
+    assert x == 7


More information about the pypy-commit mailing list