-
-
Notifications
You must be signed in to change notification settings - Fork 33.2k
Description
When I converted Py_REFCNT(), Py_TYPE() and Py_SIZE() macros to static inline functions (issue #83754), I chose const PyObject* for their argument because these macros don't modify any member of the PyObject structure. When the Py_IS_TYPE() function was added, it followed the trend (commit 8767ce9). The _PyObject_CAST_CONST() macro was added to easily convert any pointer to const PyObject* for these macros expecting const PyObject*.
The problem is that passing PyObject* to one of these functions emits a compiler warning using gcc -Wcast-qual. The _PyObject_CAST_CONST() doesn't make the warning quiet.
Example explaining the issue:
static inline int value(int *p) { return *p; }
#define value(p) value((int*)(p))
int main()
{
int x = 1;
const int *p = &x;
return value(p);
}Output:
$ gcc x.c -Wcast-qual -o x && ./x; echo $?
x.c: In function 'main':
x.c:2:24: warning: cast discards 'const' qualifier from pointer target type [-Wcast-qual]
2 | #define value(p) value((int*)(p))
| ^
x.c:8:12: note: in expansion of macro 'value'
8 | return value(p);
| ^~~~~
1In practice, the problem was that building a C extension (which includes <Python.h>) with gcc -Wcast-qual -Werror on Python 3.10 failed with an error on Py_IS_TYPE() implemented as:
static inline int Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) {
return Py_TYPE(ob) == type;
}
#define Py_IS_TYPE(ob, type) Py_IS_TYPE(_PyObject_CAST_CONST(ob), type)See the issue #88544 for the compiler error. I removed the Py_TYPE() call in Py_IS_TYPE() to work around the issue:
static inline int Py_IS_TYPE(const PyObject *ob, const PyTypeObject *type) {
// bpo-44378: Don't use Py_TYPE() since Py_TYPE() requires a non-const
// object.
return ob->ob_type == type;
}
#define Py_IS_TYPE(ob, type) Py_IS_TYPE(_PyObject_CAST_CONST(ob), type)But this problem strikes back when I try to convert unicodeobject.h macros to static inline functions for PEP 670 which removes the cast to PyObject* in the limited C API version 3.11: see PR #91696 and PR #91705. For example, _Py_strhex_with_sep() and _Py_strhex_bytes_with_sep() function get a const PyObject* argument and use PyUnicode C functions like PyUnicode_READY(), PyUnicode_KIND() and PyUnicode_READ_CHAR(), but these PyUnicode functions expect PyObject*: the const qualifier is lost. If Python/pystrhex.c is built with gcc -Wcast-qual, gcc emits warnings on PyUnicode_KIND() and PyUnicode_READ_CHAR() calls. That's just an example of problem which can happen in C extensions as well.
To avoid these problems, I propose to avoid const PyObject* in Python: only use PyObject*.