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);
| ^~~~~
1
In 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*
.