Commits

Armin Rigo committed edd85be

Enum types are always 'int' so far. Detect overflows.

Comments (0)

Files changed (3)

c/_cffi_backend.c

 # define PyText_FromString PyUnicode_FromString
 # define PyText_FromStringAndSize PyUnicode_FromStringAndSize
 # define PyText_InternInPlace PyUnicode_InternInPlace
+# define PyIntOrLong_Check PyLong_Check
 #else
 # define STR_OR_BYTES "str"
 # define PyText_Type PyString_Type
 # define PyText_FromString PyString_FromString
 # define PyText_FromStringAndSize PyString_FromStringAndSize
 # define PyText_InternInPlace PyString_InternInPlace
+# define PyIntOrLong_Check(op) (PyInt_Check(op) || PyLong_Check(op))
 #endif 
 
 #if PY_MAJOR_VERSION >= 3
         if (io == NULL)
             return -1;
 
-#if PY_MAJOR_VERSION < 3
-        if (PyInt_Check(io) || PyLong_Check(io)) {
-#else
-        if (PyLong_Check(io)) {
-#endif
+        if (PyIntOrLong_Check(io)) {
             res = _my_PyLong_AsLongLong(io);
         }
         else {
         if (io == NULL)
             return (unsigned PY_LONG_LONG)-1;
 
-#if PY_MAJOR_VERSION < 3
-        if (PyInt_Check(io) || PyLong_Check(io)) {
-#else
-        if (PyLong_Check(io)) {
-#endif
+        if (PyIntOrLong_Check(io)) {
             res = _my_PyLong_AsUnsignedLongLong(io, strict);
         }
         else {
     if (io == NULL)
         return -1;
 
-#if PY_MAJOR_VERSION < 3
-    if (PyInt_Check(io) || PyLong_Check(io) || PyFloat_Check(io)) {
-#else
-    if (PyLong_Check(io) || PyFloat_Check(io)) {
-#endif
+    if (PyIntOrLong_Check(io) || PyFloat_Check(io)) {
         res = _my_PyObject_AsBool(io);
     }
     else {
     if (dict1 == NULL)
         goto error;
     for (i=n; --i >= 0; ) {
-        if (PyDict_SetItem(dict1, PyTuple_GET_ITEM(enumerators, i),
-                                  PyTuple_GET_ITEM(enumvalues, i)) < 0)
+        PyObject *key = PyTuple_GET_ITEM(enumerators, i);
+        PyObject *value = PyTuple_GET_ITEM(enumvalues, i);
+        long lvalue;
+        if (!PyText_Check(key)) {
+            PyErr_SetString(PyExc_TypeError,
+                            "enumerators must be a list of strings");
+            goto error;
+        }
+        lvalue = PyLong_AsLong(value);
+        if ((lvalue == -1 && PyErr_Occurred()) || lvalue != (int)lvalue) {
+            PyErr_Format(PyExc_OverflowError,
+                         "enum '%s' declaration for '%s' does not fit an int",
+                         ename, PyText_AS_UTF8(key));
+            goto error;
+        }
+        if (PyDict_SetItem(dict1, key, value) < 0)
             goto error;
     }
 
     e = py.test.raises(TypeError, newp, BStructPtr, [None])
     assert "must be a str or int, not NoneType" in str(e.value)
 
+def test_enum_overflow():
+    for ovf in (sys.maxint+1, -sys.maxint-2, 2**31, -2**31-1):
+        e = py.test.raises(OverflowError, new_enum_type, "foo", ('a', 'b'),
+                           (5, ovf))
+        assert str(e.value) == (
+            "enum 'foo' declaration for 'b' does not fit an int")
+
 def test_callback_returning_enum():
     BInt = new_primitive_type("int")
     BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20))

doc/source/index.rst

   length 0, allocating a ``char[]`` of the correct size, and casting
   it to a struct pointer)
 
+* Enum types are always ``int``.  GCC supports enums containing
+  larger constants (``unsigned int``, or ``long long``) as an extension
+  to the C standard, but CFFI does not.  Use
+  ``typedef <exact type> my_enum;`` and then some ``#define foo <value>``.
+
 .. versionadded:: 0.4
    Now supported: the common GCC extension of anonymous nested
    structs/unions inside structs/unions.
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.