Skip to content

Commit 42e921e

Browse files
authored
PEP 670: clarify cast; don't change return type (GH-2349)
* Clarify how arguments are cast * Limited C API version 3.11 no longer casts pointer arguments * No longer remove return values * Require to not change the return type * Don't change macros having multiple return types
1 parent de8ca55 commit 42e921e

File tree

1 file changed

+65
-57
lines changed

1 file changed

+65
-57
lines changed

pep-0670.rst

Lines changed: 65 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,8 @@ them usable by Python extensions which cannot use macros or static
2020
inline functions, like extensions written in a programming languages
2121
other than C or C++.
2222

23-
Remove the return value of macros having a return value, whereas they
24-
should not, to aid detecting bugs in C extensions when the C API is
25-
misused.
26-
27-
Some function arguments are still cast to ``PyObject*`` to prevent
28-
emitting new compiler warnings.
23+
Function arguments of pointer types are still cast and return types are
24+
not changed to prevent emitting new compiler warnings.
2925

3026
Macros which can be used as l-value in an assignment are not converted
3127
to functions to avoid introducing incompatible changes.
@@ -177,6 +173,7 @@ The following macros should not be converted:
177173
* Macros which can be used as l-value in an assignment. This change is
178174
an incompatible change and is out of the scope of this PEP.
179175
Example: ``PyBytes_AS_STRING()``.
176+
* Macros having different return types depending on the code path.
180177

181178

182179
Convert static inline functions to regular functions
@@ -196,74 +193,81 @@ Using static inline functions in the internal C API is fine: the
196193
internal C API exposes implementation details by design and should not be
197194
used outside Python.
198195

199-
Cast to PyObject*
200-
-----------------
196+
Cast pointer arguments
197+
----------------------
198+
199+
Existing cast
200+
'''''''''''''
201+
202+
Currently, most macros accepting pointers cast pointer arguments to
203+
their expected types. For example, in Python 3.6, the ``Py_TYPE()``
204+
macro casts its argument to ``PyObject*``::
201205

202-
When a macro is converted to a function and the macro casts its
203-
arguments to ``PyObject*``, the new function comes with a new macro
204-
which cast arguments to ``PyObject*`` to prevent emitting new compiler
205-
warnings. This implies that a converted function will accept pointers to
206-
structures inheriting from ``PyObject`` (ex: ``PyTupleObject``).
206+
#define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
207207

208-
For example, the ``Py_TYPE(obj)`` macro casts its ``obj`` argument to
209-
``PyObject*``::
208+
The ``Py_TYPE()`` macro accepts the ``PyObject*`` type, but also any
209+
pointer types, such as ``PyLongObject*`` and ``PyDictObject*``.
210210

211-
#define _PyObject_CAST_CONST(op) ((const PyObject*)(op))
211+
Add a new macro to keep the cast
212+
''''''''''''''''''''''''''''''''
212213

213-
static inline PyTypeObject* _Py_TYPE(const PyObject *ob) {
214+
When a macro is converted to a function and the macro casts at least one
215+
of its arguments, a new macro is added to keep the cast. The new macro
216+
and the function have the same name. Example with the ``Py_TYPE()``
217+
macro converted to a static inline function::
218+
219+
static inline PyTypeObject* Py_TYPE(PyObject *ob) {
214220
return ob->ob_type;
215221
}
216-
#define Py_TYPE(ob) _Py_TYPE(_PyObject_CAST_CONST(ob))
222+
#define Py_TYPE(ob) Py_TYPE((PyObject*)(ob))
217223

218-
The undocumented private ``_Py_TYPE()`` function must not be called
219-
directly. Only the documented public ``Py_TYPE()`` macro must be used.
224+
The cast is kept for all pointer types, not only ``PyObject*``.
220225

221-
Later, the cast can be removed on a case by case basis, but that is out
222-
of scope for this PEP.
226+
Removing a cast to ``void*`` would emit a new warning if the function is
227+
called with a variable of ``const void*`` type. For example, the
228+
``PyUnicode_WRITE()`` macro casts its *data* argument to ``void*``, and
229+
so accepts ``const void*`` type, even if it writes into *data*.
223230

224-
Remove the return value
225-
-----------------------
231+
Avoid the cast in the limited C API version 3.11
232+
''''''''''''''''''''''''''''''''''''''''''''''''
226233

227-
When a macro is implemented as an expression, it has an implicit return
228-
value. This return value can be misused in third party C extensions.
229-
See `bpo-30459 <https://bugs.python.org/issue30459>`__ regarding the
230-
misuse of the ``PyList_SET_ITEM()`` and ``PyCell_SET()`` macros.
234+
The cast is removed from the limited C API version 3.11 and newer: the
235+
caller must pass the expected type, or perform the cast. An example with
236+
the ``Py_TYPE()`` function::
231237

232-
Such issue is hard to catch while reviewing macro code. Removing the
233-
return value aids detecting bugs in C extensions when the C API is
234-
misused.
238+
static inline PyTypeObject* Py_TYPE(PyObject *ob) {
239+
return ob->ob_type;
240+
}
241+
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 < 0x030b0000
242+
# define Py_TYPE(ob) Py_TYPE((PyObject*)(ob))
243+
#endif
235244

236-
The issue has already been fixed in public C API macros by the
237-
`bpo-30459 <https://bugs.python.org/issue30459>`__ in Python 3.10: add a
238-
``(void)`` cast to the affected macros. Example of the
239-
``PyTuple_SET_ITEM()`` macro::
240245

241-
#define PyTuple_SET_ITEM(op, i, v) ((void)(_PyTuple_CAST(op)->ob_item[i] = v))
246+
Return type is not changed
247+
--------------------------
242248

243-
Example of macros currently using a ``(void)`` cast to have no return
244-
value:
249+
When a macro is converted to a function, its return type must not change
250+
to prevent emitting new compiler warnings.
245251

246-
* ``PyCell_SET()``
247-
* ``PyList_SET_ITEM()``
248-
* ``PyTuple_SET_ITEM()``
249-
* ``Py_BUILD_ASSERT()``
250-
* ``_PyGCHead_SET_FINALIZED()``
251-
* ``_PyGCHead_SET_NEXT()``
252-
* ``_PyObject_ASSERT_FROM()``
253-
* ``_Py_atomic_signal_fence()``
254-
* ``_Py_atomic_store_64bit()``
255-
* ``asdl_seq_SET()``
256-
* ``asdl_seq_SET_UNTYPED()``
252+
For example, Python 3.7 changed ``PyUnicode_AsUTF8()`` return type from
253+
``char*`` to ``const char*`` (`commit
254+
<https://github.com/python/cpython/commit/2a404b63d48d73bbaa007d89efb7a01048475acd>`__).
255+
The change emitted new compiler warnings when building C extensions
256+
expecting ``char*``. This PEP doesn't change the return type to prevent
257+
this issue.
257258

258259

259260
Backwards Compatibility
260261
=======================
261262

262-
Removing the return value of macros is an incompatible API change made
263-
on purpose: see the `Remove the return value`_ section.
263+
The PEP is designed to avoid C API incompatible changes.
264264

265-
Some function arguments are still cast to ``PyObject*`` to prevent
266-
emitting new compiler warnings.
265+
Only C extensions explicitly targeting the limited C API version 3.11
266+
must now pass the expected types to functions: pointer arguments are no
267+
longer cast to the expected types.
268+
269+
Function arguments of pointer types are still cast and return types are
270+
not changed to prevent emitting new compiler warnings.
267271

268272
Macros which can be used as l-value in an assignment are not modified by
269273
this PEP to avoid incompatible changes.
@@ -275,10 +279,6 @@ Rejected Ideas
275279
Keep macros, but fix some macro issues
276280
--------------------------------------
277281

278-
Converting macros to functions is not needed to `remove the return
279-
value`_: adding a ``(void)`` cast is enough. For example, the
280-
``PyList_SET_ITEM()`` macro was already fixed like that.
281-
282282
Macros are always "inlined" with any C compiler.
283283

284284
The duplication of side effects can be worked around in the caller of
@@ -600,6 +600,14 @@ References
600600
(March 2021).
601601

602602

603+
Version History
604+
===============
605+
606+
* Version 2: No longer remove return values; remove argument casting
607+
from the limited C API.
608+
* Version 1: First public version
609+
610+
603611
Copyright
604612
=========
605613

0 commit comments

Comments
 (0)