Skip to content

[C API] Avoid "const PyObject*" type #91768

Description

@vstinner

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:

staticinlineintvalue(int*p) { return*p; }
#definevalue(p) value((int*)(p))
intmain()
{
intx=1;
constint*p=&x;
returnvalue(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:

staticinlineintPy_IS_TYPE(constPyObject*ob, constPyTypeObject*type) {
returnPy_TYPE(ob) ==type;
}
#definePy_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:

staticinlineintPy_IS_TYPE(constPyObject*ob, constPyTypeObject*type) {
// bpo-44378: Don't use Py_TYPE() since Py_TYPE() requires a non-const// object.returnob->ob_type==type;
}
#definePy_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*.

Metadata

Metadata

Assignees

No one assigned

    Labels

    type-bugAn unexpected behavior, bug, or error

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions