diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index a6e30c77c55a0d..c6ea839c098b13 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -572,6 +572,17 @@ Porting to Python 3.11 header provides functions like ``printf()`` and ``fopen()``. (Contributed by Victor Stinner in :issue:`45434`.) +* Convert the following macros to static inline functions to disallow using + them as l-value: + + * :c:func:`PyByteArray_AS_STRING` + * :c:func:`PyBytes_AS_STRING` + * :c:func:`PyFloat_AS_DOUBLE` + + Moreover, :c:func:`PyUnicode_AS_UNICODE` becomes an alias to + :c:func:`PyUnicode_AsUnicode`. + (Contributed by Victor Stinner in :issue:`45476`.) + Deprecated ---------- diff --git a/Include/cpython/bytearrayobject.h b/Include/cpython/bytearrayobject.h index 569b0cd0369861..b30f4a883711d3 100644 --- a/Include/cpython/bytearrayobject.h +++ b/Include/cpython/bytearrayobject.h @@ -11,10 +11,19 @@ typedef struct { Py_ssize_t ob_exports; /* How many buffer exports */ } PyByteArrayObject; -/* Macros, trading safety for speed */ -#define PyByteArray_AS_STRING(self) \ - (assert(PyByteArray_Check(self)), \ - Py_SIZE(self) ? ((PyByteArrayObject *)(self))->ob_start : _PyByteArray_empty_string) -#define PyByteArray_GET_SIZE(self) (assert(PyByteArray_Check(self)), Py_SIZE(self)) - PyAPI_DATA(char) _PyByteArray_empty_string[]; + +// Static inline function, trading safety for speed +static inline char* _PyByteArray_AS_STRING(PyByteArrayObject *self) { + assert(PyByteArray_Check(self)); + if (Py_SIZE(self)) { + return self->ob_start; + } + else { + return _PyByteArray_empty_string; + } +} +#define PyByteArray_AS_STRING(op) _PyByteArray_AS_STRING((PyByteArrayObject *)(op)) + +// Macro, trading safety for speed +#define PyByteArray_GET_SIZE(self) (assert(PyByteArray_Check(self)), Py_SIZE(self)) diff --git a/Include/cpython/bytesobject.h b/Include/cpython/bytesobject.h index 6b3f55224fc553..f573ac9fb59aec 100644 --- a/Include/cpython/bytesobject.h +++ b/Include/cpython/bytesobject.h @@ -28,10 +28,15 @@ PyAPI_FUNC(PyObject*) _PyBytes_FromHex( PyAPI_FUNC(PyObject *) _PyBytes_DecodeEscape(const char *, Py_ssize_t, const char *, const char **); -/* Macro, trading safety for speed */ -#define PyBytes_AS_STRING(op) (assert(PyBytes_Check(op)), \ - (((PyBytesObject *)(op))->ob_sval)) -#define PyBytes_GET_SIZE(op) (assert(PyBytes_Check(op)),Py_SIZE(op)) +// Static inline function, trading safety for speed +static inline char* _PyBytes_AS_STRING(PyBytesObject *op) { + assert(PyBytes_Check(op)); + return op->ob_sval; +} +#define PyBytes_AS_STRING(op) _PyBytes_AS_STRING((PyBytesObject *)(op)) + +// Macro, trading safety for speed +#define PyBytes_GET_SIZE(op) (assert(PyBytes_Check(op)), Py_SIZE(op)) /* _PyBytes_Join(sep, x) is like sep.join(x). sep must be PyBytesObject*, x must be an iterable object. */ diff --git a/Include/cpython/floatobject.h b/Include/cpython/floatobject.h index fffd468690274e..95433ca9f9986d 100644 --- a/Include/cpython/floatobject.h +++ b/Include/cpython/floatobject.h @@ -7,6 +7,10 @@ typedef struct { double ob_fval; } PyFloatObject; -// Macro version of PyFloat_AsDouble() trading safety for speed. -// It doesn't check if op is a double object. -#define PyFloat_AS_DOUBLE(op) (((PyFloatObject *)(op))->ob_fval) +// Static inline version of PyFloat_AsDouble() trading safety for speed. +// It only checks if op is a Python float object in debug mode. +static inline double _PyFloat_AS_DOUBLE(PyFloatObject *op) { + assert(PyFloat_Check(op)); + return op->ob_fval; +} +#define PyFloat_AS_DOUBLE(op) _PyFloat_AS_DOUBLE((PyFloatObject *)(op)) diff --git a/Include/cpython/unicodeobject.h b/Include/cpython/unicodeobject.h index ab4aebf5e70b93..be03c40247c200 100644 --- a/Include/cpython/unicodeobject.h +++ b/Include/cpython/unicodeobject.h @@ -254,16 +254,9 @@ PyAPI_FUNC(int) _PyUnicode_CheckConsistency( #define PyUnicode_GET_DATA_SIZE(op) \ (PyUnicode_GET_SIZE(op) * Py_UNICODE_SIZE) -/* Alias for PyUnicode_AsUnicode(). This will create a wchar_t/Py_UNICODE - representation on demand. Using this macro is very inefficient now, - try to port your code to use the new PyUnicode_*BYTE_DATA() macros or - use PyUnicode_WRITE() and PyUnicode_READ(). */ - +/* Alias for PyUnicode_AsUnicode(). */ /* Py_DEPRECATED(3.3) */ -#define PyUnicode_AS_UNICODE(op) \ - (assert(PyUnicode_Check(op)), \ - (((PyASCIIObject *)(op))->wstr) ? (((PyASCIIObject *)(op))->wstr) : \ - PyUnicode_AsUnicode(_PyObject_CAST(op))) +#define PyUnicode_AS_UNICODE(op) PyUnicode_AsUnicode(_PyObject_CAST(op)) /* Py_DEPRECATED(3.3) */ #define PyUnicode_AS_DATA(op) \ diff --git a/Misc/NEWS.d/next/C API/2021-10-15-01-04-45.bpo-45476.HZmZRH.rst b/Misc/NEWS.d/next/C API/2021-10-15-01-04-45.bpo-45476.HZmZRH.rst new file mode 100644 index 00000000000000..20bf7d52d38183 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2021-10-15-01-04-45.bpo-45476.HZmZRH.rst @@ -0,0 +1,9 @@ +Convert the following macros to static inline functions to disallow using +them as l-value: + +* :c:func:`PyByteArray_AS_STRING` +* :c:func:`PyBytes_AS_STRING` +* :c:func:`PyFloat_AS_DOUBLE` + +Moreover, :c:func:`PyUnicode_AS_UNICODE` becomes an alias to +:c:func:`PyUnicode_AsUnicode`. Patch by Victor Stinner.