This commit is contained in:
Victor Stinner 2021-10-20 00:15:07 +02:00 committed by GitHub
parent a0380afe17
commit 401d207bd3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 46 additions and 70 deletions

View File

@ -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
<https://bugs.python.org/issue30459>`_.
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
<https://bugs.python.org/issue30459>`_ 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()