New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[C API] PEP 670: Convert macros to functions in the Python C API #89653
Comments
C macros are really cool and useful, but there are a bunch of pitfalls which are better to avoid: Some macros of the Python C API have been converted to static inline functions over the last years. It went smoothly, I am not aware of any major issue with these conversions. This meta issue tracks other issues related to macros and static inline functions. === Return void === One issue is that some macros are treated as an expression and can be reused, whereas it was not intended. For example PyList_SET_ITEM() was implemented as (simplified code): #define PyList_SET_ITEM(op, i, v) (op->ob_item[i] = v) This expression has a value! Two projects used this value by mistake, like: "if (obj == NULL || PyList_SET_ITEM (l, i, obj) < 0)" PyList_SET_ITEM() was fixed by casting the expression to void: #define PyList_SET_ITEM(op, i, v) ((void)(op->ob_item[i] = v)) => bpo-30459 === Abuse macros as an l-value === The Py_TYPE() macro could be used to assign a value: "Py_TYPE(obj) = new_type". The macro was defined as: #define Py_TYPE(ob) (ob->ob_type) It was converted to a static inline function to disallow using it as an l-value and a new Py_SET_TYPE(op, new_type) function was added. These changes give more freedom to other Python implementations to implement "PyObject" and Py_SET_TYPE(). => bpo-45476 "[C API] Disallow using PyFloat_AS_DOUBLE() as l-value" === C API: Macros and embedded Python === Sadly, only symbols exported by libpython are accessible to other programming languages embedding Python. Macros of the Python C API are simply not available to them. Projects embedding Python have to hardcode constants and copy macros to their own language, with the risk of being outdated when Python macros are updated. Even some projects written in C cannot use macros, because they only use libpython symbols. The vim text editor embeds Python this way. Also, macros are simply excluded from the Python stable ABI (PEP-384). === Performance of static inline functions === In bpo-45116, it seems like _PyEval_EvalFrameDefault() reached Visual Studio thresholds and some static inline functions are no longer inlined (Py_INCREF/Py_DECREF). I also noticed that when Python is built in debug mode in Visual Studio, static inline functions are not inlined. Well, the compiler is free to not inline in debug mode. I guess that GCC and clang also skip inlining using -Og and/or -O0 optimization levels. Using __forceinline and __attribute__((always_inline)) on static inline functions (Py_INCREF, Py_TYPE) for debug builds was discussed in bpo-45094, but the idea was rejected. On the other side, sometimes it's better to *disable* inlining on purpose to reduce the stack memory consumption, using the Py_NO_INLINE macro. See recent measurements of the stack memory usage: In #73079, I noticed that converting a static inline function (PyObject_CallOneArg) to a regular function made it faster. I am not really sure, more benchmarks should be run to really what's going on. === Advantages of static inline functions ===
|
Meta comment :-) ... wouldn't it be better to enable the Github wiki feature for |
+1! See also bpo-43502 |
Previous discussion on Discourse: https://discuss.python.org/t/what-to-do-with-unsafe-macros/7771 |
I will use this issue to track changes related to PEP-670. |
I made a list of macros that reuse their argument some time around February/March 2021. (Each macro is squashed into a single line for some reason I can't remember.) See attachment, or check out the gist version: https://gist.github.com/erlend-aasland/a7ca3cff95b136e272ff5b03447aff21 |
Update: the SC approved PEP 670: https://mail.python.org/archives/list/python-dev@python.org/thread/QQFCJ7LR36RUZSC3WI6WZZMQVQ3ZI4MS/ |
…ions * Convert unicodeobject.h macros to static inline functions. * Reorder functions to declare functions before their first usage. * Add "kind" variable to PyUnicode_READ_CHAR() and PyUnicode_MAX_CHAR_VALUE() functions to only call PyUnicode_KIND() once. * PyUnicode_KIND() return type is "unsigned int" (not "enum PyUnicode_Kind"). * Simplify PyUnicode_GET_SIZE(). * PyUnicode_READ_CHAR() now uses PyUnicode_1BYTE_DATA(), PyUnicode_2BYTE_DATA() and PyUnicode_4BYTE_DATA(). Assertions: * Remove redundant PyUnicode_Check() assertions. * Add assertions to PyUnicode_WRITE() on the max value. Part of PEP 670 implementation.
…ions * Convert unicodeobject.h macros to static inline functions. * Reorder functions to declare functions before their first usage. * PyUnicode_READ_CHAR() and PyUnicode_MAX_CHAR_VALUE() now only call PyUnicode_KIND() once. * PyUnicode_KIND() return type is "unsigned int" (not "enum PyUnicode_Kind"). * Simplify PyUnicode_GET_SIZE(). * PyUnicode_READ_CHAR() now uses PyUnicode_1BYTE_DATA(), PyUnicode_2BYTE_DATA() and PyUnicode_4BYTE_DATA(). Assertions: * Remove redundant PyUnicode_Check() assertions. * Add assertions to PyUnicode_WRITE() on the max value. Part of PEP 670 implementation.
…ions * Convert unicodeobject.h macros to static inline functions. * Reorder functions to declare functions before their first usage. * PyUnicode_READ_CHAR() and PyUnicode_MAX_CHAR_VALUE() now only call PyUnicode_KIND() once. * PyUnicode_KIND() return type is "unsigned int" (not "enum PyUnicode_Kind"). * Simplify PyUnicode_GET_SIZE(). * PyUnicode_READ_CHAR() now uses PyUnicode_1BYTE_DATA(), PyUnicode_2BYTE_DATA() and PyUnicode_4BYTE_DATA(). Assertions: * Remove redundant PyUnicode_Check() assertions. * Add assertions to PyUnicode_WRITE() on the max value. Part of PEP 670 implementation.
…ions * Convert unicodeobject.h macros to static inline functions. * Reorder functions to declare functions before their first usage. * PyUnicode_READ_CHAR() and PyUnicode_MAX_CHAR_VALUE() now only call PyUnicode_KIND() once. * Simplify PyUnicode_GET_SIZE(). * PyUnicode_READ_CHAR() now uses PyUnicode_1BYTE_DATA(), PyUnicode_2BYTE_DATA() and PyUnicode_4BYTE_DATA(). * Remove redundant PyUnicode_Check() assertions. PyUnicode_KIND() return type is "unsigned int" rather than "enum PyUnicode_Kind" to prevent introducing new compiler warnings. Part of PEP 670 implementation.
* Convert unicodeobject.h macros to static inline functions. * Reorder functions to declare functions before their first usage. * PyUnicode_READ_CHAR() and PyUnicode_MAX_CHAR_VALUE() now only call PyUnicode_KIND() once. * Simplify PyUnicode_GET_SIZE(). * PyUnicode_READ_CHAR() now uses PyUnicode_1BYTE_DATA(), PyUnicode_2BYTE_DATA() and PyUnicode_4BYTE_DATA(). * Remove redundant PyUnicode_Check() assertions. PyUnicode_KIND() return type is "unsigned int" rather than "enum PyUnicode_Kind" to prevent introducing new compiler warnings.
Convert unicodeobject.h macros to static inline functions: * Reorder functions to declare functions before their first usage. * PyUnicode_READ_CHAR() and PyUnicode_MAX_CHAR_VALUE() now only call PyUnicode_KIND() once. * Simplify PyUnicode_GET_SIZE(). * PyUnicode_READ_CHAR() now uses PyUnicode_1BYTE_DATA(), PyUnicode_2BYTE_DATA() and PyUnicode_4BYTE_DATA(). * Remove redundant PyUnicode_Check() assertions. PyUnicode_KIND() return type is "unsigned int" rather than "enum PyUnicode_Kind" to prevent introducing new compiler warnings.
Convert unicodeobject.h macros to static inline functions: * Reorder functions to declare functions before their first usage. * PyUnicode_READ_CHAR() and PyUnicode_MAX_CHAR_VALUE() now only call PyUnicode_KIND() once. * Simplify PyUnicode_GET_SIZE(). * PyUnicode_READ_CHAR() now uses PyUnicode_1BYTE_DATA(), PyUnicode_2BYTE_DATA() and PyUnicode_4BYTE_DATA(). * Remove redundant PyUnicode_Check() assertions. PyUnicode_KIND() return type is "unsigned int" rather than "enum PyUnicode_Kind" to prevent introducing new compiler warnings.
Convert unicodeobject.h macros to static inline functions: * Reorder functions to declare functions before their first usage. * PyUnicode_READ_CHAR() and PyUnicode_MAX_CHAR_VALUE() now only call PyUnicode_KIND() once. * Simplify PyUnicode_GET_SIZE(). * PyUnicode_READ_CHAR() now uses PyUnicode_1BYTE_DATA(), PyUnicode_2BYTE_DATA() and PyUnicode_4BYTE_DATA(). * Remove redundant PyUnicode_Check() assertions. Static inline functions are wrapped into macros which casts pointer types (PyObject*, void*) to prevent introducing new compiler warnings when passing const pointers (ex: PyUnicode_WRITE). PyUnicode_KIND() return type is "unsigned int" rather than "enum PyUnicode_Kind" to prevent introducing new compiler warnings.
Convert unicodeobject.h macros to static inline functions: * Reorder functions to declare functions before their first usage. * PyUnicode_READ_CHAR() and PyUnicode_MAX_CHAR_VALUE() now only call PyUnicode_KIND() once. * Simplify PyUnicode_GET_SIZE(). * PyUnicode_READ_CHAR() now uses PyUnicode_1BYTE_DATA(), PyUnicode_2BYTE_DATA() and PyUnicode_4BYTE_DATA(). * Remove redundant PyUnicode_Check() assertions. Static inline functions are wrapped into macros which casts pointer types (PyObject*, void*) to prevent introducing new compiler warnings when passing const pointers (ex: PyUnicode_WRITE). PyUnicode_KIND() return type is "unsigned int" rather than "enum PyUnicode_Kind" to prevent introducing new compiler warnings.
In the limited C API version 3.11 and newer, the following functions no longer cast their object pointer argument with _PyObject_CAST() or _PyObject_CAST_CONST(): * Py_REFCNT(), Py_TYPE(), Py_SIZE() * Py_SET_REFCNT(), Py_SET_TYPE(), Py_SET_SIZE() * Py_IS_TYPE() * Py_INCREF(), Py_DECREF() * Py_XINCREF(), Py_XDECREF() * Py_NewRef(), Py_XNewRef() * PyObject_TypeCheck() * PyType_Check() * PyType_CheckExact() Update the xxlimited.c extension, which uses the limited C API version 3.11, to pass PyObject* to these functions.
In the limited C API version 3.11 and newer, the following functions no longer cast their object pointer argument with _PyObject_CAST() or _PyObject_CAST_CONST(): * Py_REFCNT(), Py_TYPE(), Py_SIZE() * Py_SET_REFCNT(), Py_SET_TYPE(), Py_SET_SIZE() * Py_IS_TYPE() * Py_INCREF(), Py_DECREF() * Py_XINCREF(), Py_XDECREF() * Py_NewRef(), Py_XNewRef() * PyObject_TypeCheck() * PyType_Check() * PyType_CheckExact() Split Py_DECREF() implementation in 3 versions to make the code more readable. Update the xxlimited.c extension, which uses the limited C API version 3.11, to pass PyObject* to these functions.
Convert unicodeobject.h macros to static inline functions: * PyUnicode_IS_COMPACT() * PyUnicode_IS_COMPACT_ASCII() * PyUnicode_CHECK_INTERNED() * PyUnicode_IS_READY() Changes: * Reorder functions to declare functions before their first usage. * Remove redundant PyUnicode_Check() assertions. Static inline functions are wrapped into macros which casts pointer types (PyObject*, void*) to prevent introducing new compiler warnings when passing const pointers (ex: PyUnicode_WRITE). PyUnicode_KIND() return type is "unsigned int" rather than "enum PyUnicode_Kind" to prevent introducing new compiler warnings.
Convert unicodeobject.h macros to static inline functions: * PyUnicode_CHECK_INTERNED() * PyUnicode_DATA() * PyUnicode_GET_LENGTH() * PyUnicode_IS_ASCII() * PyUnicode_IS_COMPACT() * PyUnicode_IS_COMPACT_ASCII() * PyUnicode_IS_READY() * _PyUnicode_COMPACT_DATA() * _PyUnicode_NONCOMPACT_DATA() Reorder functions to declare functions before their first usage. Static inline functions are wrapped into macros which casts "PyObject*" with _PyObject_CAST() to prevent introducing new compiler warnings when passing "const PyObject*".
Convert unicodeobject.h macros to static inline functions: * PyUnicode_CHECK_INTERNED() * PyUnicode_DATA(), _PyUnicode_COMPACT_DATA(), _PyUnicode_NONCOMPACT_DATA() * PyUnicode_GET_LENGTH() * PyUnicode_IS_ASCII() * PyUnicode_IS_COMPACT() * PyUnicode_IS_COMPACT_ASCII() * PyUnicode_IS_READY() Reorder functions to declare functions before their first usage. Static inline functions are wrapped into macros which casts "PyObject*" with _PyObject_CAST() to prevent introducing new compiler warnings when passing "const PyObject*".
…-92654) The limited API version 3.11 no longer casts arguments to expected types of functions of functions: * PyList_GET_SIZE(), PyList_SET_ITEM() * PyTuple_GET_SIZE(), PyTuple_SET_ITEM() * PyWeakref_GET_OBJECT() (cherry picked from commit 7d3b469e475e6e52ce4f0bad7198bb05ead77b1d) Co-authored-by: Victor Stinner <vstinner@python.org>
Use the PyObject* type for parameters of static inline functions: * Py_SIZE(): same parameter type than PyObject_Size() * PyList_GET_SIZE(), PyList_SET_ITEM(): same parameter type than PyList_Size() and PyList_SetItem() * PyTuple_GET_SIZE(), PyTuple_SET_ITEM(): same parameter type than PyTuple_Size() and PyTuple_SetItem().
Use the PyObject* type for parameters of static inline functions: * Py_SIZE(): same parameter type than PyObject_Size() * PyList_GET_SIZE(), PyList_SET_ITEM(): same parameter type than PyList_Size() and PyList_SetItem() * PyTuple_GET_SIZE(), PyTuple_SET_ITEM(): same parameter type than PyTuple_Size() and PyTuple_SetItem().
Use the PyObject* type for parameters of static inline functions: * Py_SIZE(): same parameter type than PyObject_Size() * PyList_GET_SIZE(), PyList_SET_ITEM(): same parameter type than PyList_Size() and PyList_SetItem() * PyTuple_GET_SIZE(), PyTuple_SET_ITEM(): same parameter type than PyTuple_Size() and PyTuple_SetItem().
The limited API version 3.11 no longer casts arguments to expected types of functions of functions: * PyList_GET_SIZE(), PyList_SET_ITEM() * PyTuple_GET_SIZE(), PyTuple_SET_ITEM() * PyWeakref_GET_OBJECT() (cherry picked from commit 7d3b469) Co-authored-by: Victor Stinner <vstinner@python.org>
The limited C API version 3.12 no longer casts the argument.
Use _Py_CAST() and _Py_STATIC_CAST() in macros wrapping static inline functions of unicodeobject.h. Change also the kind type from unsigned int to int: same parameter type than PyUnicode_FromKindAndData().
Use _Py_CAST() and _Py_STATIC_CAST() in macros wrapping static inline functions of unicodeobject.h. Change also the kind type from unsigned int to int: same parameter type than PyUnicode_FromKindAndData(). The limited API version 3.11 no longer casts arguments to expected types.
Use _Py_CAST() and _Py_STATIC_CAST() in macros wrapping static inline functions of unicodeobject.h. Change also the kind type from unsigned int to int: same parameter type than PyUnicode_FromKindAndData(). The limited API version 3.11 no longer casts arguments to expected types.
Use the PyObject* type for parameters of static inline functions: * Py_SIZE(): same parameter type than PyObject_Size() * PyList_GET_SIZE(), PyList_SET_ITEM(): same parameter type than PyList_Size() and PyList_SetItem() * PyTuple_GET_SIZE(), PyTuple_SET_ITEM(): same parameter type than PyTuple_Size() and PyTuple_SetItem().
…92694) Use the PyObject* type for parameters of static inline functions: * Py_SIZE(): same parameter type than PyObject_Size() * PyList_GET_SIZE(), PyList_SET_ITEM(): same parameter type than PyList_Size() and PyList_SetItem() * PyTuple_GET_SIZE(), PyTuple_SET_ITEM(): same parameter type than PyTuple_Size() and PyTuple_SetItem(). (cherry picked from commit 6de78ef) Co-authored-by: Victor Stinner <vstinner@python.org>
The limited C API version 3.12 no longer casts the argument.
Use the PyObject* type for parameters of static inline functions: * Py_SIZE(): same parameter type than PyObject_Size() * PyList_GET_SIZE(), PyList_SET_ITEM(): same parameter type than PyList_Size() and PyList_SetItem() * PyTuple_GET_SIZE(), PyTuple_SET_ITEM(): same parameter type than PyTuple_Size() and PyTuple_SetItem(). (cherry picked from commit 6de78ef) Co-authored-by: Victor Stinner <vstinner@python.org>
Use _Py_CAST() and _Py_STATIC_CAST() in macros wrapping static inline functions of unicodeobject.h. Change also the kind type from unsigned int to int: same parameter type than PyUnicode_FromKindAndData(). The limited API version 3.11 no longer casts arguments to expected types.
) Use _Py_CAST() and _Py_STATIC_CAST() in macros wrapping static inline functions of unicodeobject.h. Change also the kind type from unsigned int to int: same parameter type than PyUnicode_FromKindAndData(). The limited API version 3.11 no longer casts arguments to expected types. (cherry picked from commit d0c9353a79c2003385c83892db5dfd4e443474c9) Co-authored-by: Victor Stinner <vstinner@python.org>
Use _Py_CAST() and _Py_STATIC_CAST() in macros wrapping static inline functions of unicodeobject.h. Change also the kind type from unsigned int to int: same parameter type than PyUnicode_FromKindAndData(). The limited API version 3.11 no longer casts arguments to expected types. (cherry picked from commit d0c9353) Co-authored-by: Victor Stinner <vstinner@python.org> Co-authored-by: Victor Stinner <vstinner@python.org>
Use the same type that PyUnicode_FromKindAndData() kind parameter type (public C API): int.
Use the same type that PyUnicode_FromKindAndData() kind parameter type (public C API): int.
In the limited C API version 3.12, PyUnicode_KIND() is now implemented as a static inline function. Keep the macro for the regular C API and for the limited C API version 3.11 and older to prevent introducing new compiler warnings. Update _decimal.c and stringlib/eq.h for stricter PyUnicode_KIND() even if the limited C API is not used by default.
In the limited C API version 3.12, PyUnicode_KIND() is now implemented as a static inline function. Keep the macro for the regular C API and for the limited C API version 3.11 and older to prevent introducing new compiler warnings. Update _decimal.c and stringlib/eq.h for stricter PyUnicode_KIND() even if the limited C API is not used by default.
In the limited C API version 3.12, PyUnicode_KIND() is now implemented as a static inline function. Keep the macro for the regular C API and for the limited C API version 3.11 and older to prevent introducing new compiler warnings. Update _decimal.c and stringlib/eq.h for stricter PyUnicode_KIND() even if the limited C API is not used by default.
In the limited C API version 3.12, PyUnicode_KIND() is now implemented as a static inline function. Keep the macro for the regular C API and for the limited C API version 3.11 and older to prevent introducing new compiler warnings. Update _decimal.c and stringlib/eq.h for stricter PyUnicode_KIND().
In the limited C API version 3.12, PyUnicode_KIND() is now implemented as a static inline function. Keep the macro for the regular C API and for the limited C API version 3.11 and older to prevent introducing new compiler warnings. Update _decimal.c and stringlib/eq.h for PyUnicode_KIND().
Merged commits:
|
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: