diff --git a/pep-0670.rst b/pep-0670.rst index 183ee42b629..096c3359a0b 100644 --- a/pep-0670.rst +++ b/pep-0670.rst @@ -15,8 +15,7 @@ Abstract Convert macros to static inline functions or regular functions. Remove the return value of macros having a return value, whereas they -should not, to prevent misusing the C API and to detect bugs in C -extensions. +should not, to detect bugs in C extensions when the C API is misused. Some function arguments are still casted to ``PyObject*`` to prevent emitting new compiler warnings. @@ -31,21 +30,22 @@ known for years, while others have been discovered recently in Python. Working around macro pitfalls makes the macro coder harder to read and to maintain. -Converting macros to static inline functions and regular functions has -multiple advantages: +Converting macros to functions has multiple advantages: * By design, functions don't have macro pitfalls. * Arguments type and return type are well defined. * Debuggers and profilers can retrieve the name of inlined functions. * Debuggers can put breakpoints on inlined functions. * Variables have a well defined scope. -* Code is usually easier to read and maintain than similar macro code. - Functions don't need workaround for macro pitfalls: +* Code is usually easier to read and to maintain than similar macro + code. Functions don't need the following workarounds for macro + pitfalls: - * Adding parentheses around arguments. - * Using line continuation characters if the function is written on + * Add parentheses around arguments. + * Use line continuation characters if the function is written on multiple lines. - * Using ``do { ... } while (0)`` to write multiple statements. + * Add commas to execute multiple expressions. + * Use ``do { ... } while (0)`` to write multiple statements. Macro Pitfalls @@ -64,53 +64,12 @@ common macro pitfalls: - Newlines in arguments. -Macros arguments type and return type are undefined ---------------------------------------------------- - -Many macros cast their arguments to ``PyObject*`` each time the argument -is used. It makes the code less readable. - -The return type of a macro is not defined. The macro code must be read -to guess what is the return type. - - -Macros are hard to read ------------------------ - -Working around macro pitfalls requires to: - -* Add parentheses around arguments -* Add ``do { ... } while (0)`` if there are multiple statements. -* Add commas to execute multiple expressions. - -All these workarounds make the macro code harder to read and to -maintain. - -Use macros in macros --------------------- - -Writing a macro using ``#ifdef`` or using other macros can require -working around preprocessor limitations which imply writing code harder -to read. - -Macro having a return value whereas it should not -------------------------------------------------- - -If a macro is implemented as an expression, it has a return value. In -some cases, the macro must not have a return value and can be misued in -third party C extensions: see `bpo-30459 -`_. - -It is not easy to notice such issue while writing or reviewing a macro -code. - - Performance and inlining ======================== -Static inline functions is a feature added to C99. In 2021, C compilers -can inline them and have efficient heuristics to decide if a function -should be inlined or not. +Static inline functions is a feature added to the C99 standard. In 2021, +C compilers can inline them and have efficient heuristics to decide if a +function should be inlined or not. When a C compiler decides to not inline, there is likely a good reason. For example, inlining would reuse a register which require to @@ -216,32 +175,48 @@ used outside Python. Cast to PyObject* ----------------- -To prevent emitting new compiler warnings, a macro is used to cast some -function arguments to ``PyObject*``, so the converted functions still -accept pointers to other structures which inherit from ``PyObject`` (ex: -``PyTupleObject``). +When a macro is converted to a function and the macro casts its +arguments to ``PyObject*``, the new function comes with a new macro +which cast arguments to ``PyObject*`` to prevent emitting new compiler +warnings. So the converted functions still accept pointers to structures +inheriting from ``PyObject`` (ex: ``PyTupleObject``). For example, the ``Py_TYPE(obj)`` macro casts its ``obj`` argument to -``PyObject*``. +``PyObject*``:: -Later, the cast can be removed on a case by case basic, but it is out of + #define _PyObject_CAST_CONST(op) ((const PyObject*)(op)) + + static inline PyTypeObject* _Py_TYPE(const PyObject *ob) { + return ob->ob_type; + } + #define Py_TYPE(ob) _Py_TYPE(_PyObject_CAST_CONST(ob)) + +The undocumented private ``_Py_TYPE()`` function must not be called +directly. Only the documented public ``Py_TYPE()`` macro must be used. + +Later, the cast can be removed on a case by case basis, but it is out of this PEP scope. Remove the return value ----------------------- -Macros having a return value, whereas they should not, are converted to -static inline functions or regular functions using the ``void`` return -type (no return value) to prevent misusing the C API and to detect bugs -in C extensions. +When a macro is implemented as an expression, it has an implicit return +value. In some cases, the macro must not have a return value and can be +misused in third party C extensions. See `bpo-30459 +`_ for the example of +``PyList_SET_ITEM()`` and ``PyCell_SET()`` macros. It is not easy to +notice this issue while reviewing a macro code. + +These macros are converted to functions using the ``void`` return type +to remove their return value. Removing the return value detects bugs in +C extensions when the C API is misused. Backwards Compatibility ======================= -Converting macros having a return value, whereas they should not, to -functions using the ``void`` return type is an incompatible change made -on purpose: see the `Remove the return value`_ section. +Removing the return value of macros is an incompatible change made on +purpose: see the `Remove the return value`_ section. Rejected Ideas @@ -250,9 +225,10 @@ Rejected Ideas Keep macros, but fix some macro issues -------------------------------------- -The `Macro having a return value whereas it should not`_ issue can be -fixed by casting the macro result to ``void``. For example, the -``PyList_SET_ITEM()`` macro was already fixed like that. +Converting macros to functions is not needed to `remove the return +value`_: casting a macro return value to ``void`` also fix the issue. +For example, the ``PyList_SET_ITEM()`` macro was already fixed like +that. Macros are always "inlined" with any C compiler. @@ -262,7 +238,7 @@ the macro. People using macros should be considered "consenting adults". People who feel unsafe with macros should simply not use them. -Example of macros hard to read +Example of hard to read macros ============================== _Py_NewReference()