[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