diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 8db26cd0126b3a..3c834d0ac47d54 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -675,6 +675,39 @@ Porting to Python 3.11 been included directly, consider including ``Python.h`` instead. (Contributed by Victor Stinner in :issue:`35134`.) +* The following "GET" and "AS" functions can no longer be used as l-value (to + modify a Python object): + + * :c:func:`PyByteArray_GET_SIZE` + * :c:func:`PyBytes_GET_SIZE` + * :c:func:`PyCFunction_GET_CLASS` + * :c:func:`PyCFunction_GET_FLAGS` + * :c:func:`PyCFunction_GET_FUNCTION` + * :c:func:`PyCFunction_GET_SELF` + * :c:func:`PyDict_GET_SIZE` + * :c:func:`PyFloat_AS_DOUBLE` + * :c:func:`PyFunction_GET_ANNOTATIONS` + * :c:func:`PyFunction_GET_CLOSURE` + * :c:func:`PyFunction_GET_CODE` + * :c:func:`PyFunction_GET_DEFAULTS` + * :c:func:`PyFunction_GET_GLOBALS` + * :c:func:`PyFunction_GET_KW_DEFAULTS` + * :c:func:`PyFunction_GET_MODULE` + * :c:func:`PyHeapType_GET_MEMBERS` + * :c:func:`PyInstanceMethod_GET_FUNCTION` + * :c:func:`PyList_GET_SIZE` + * :c:func:`PyMemoryView_GET_BASE` + * :c:func:`PyMemoryView_GET_BUFFER` + * :c:func:`PyMethod_GET_FUNCTION` + * :c:func:`PyMethod_GET_SELF` + * :c:func:`PySet_GET_SIZE` + * :c:func:`PyTuple_GET_SIZE` + * :c:func:`PyWeakref_GET_OBJECT` + + For example, ``PyList_GET_SIZE(list) = 5;`` now fails with a compiler error. + The :c:func:`Py_SET_SIZE` function must be used instead. + (Contributed by Victor Stinner in :issue:`45476`.) + Deprecated ---------- diff --git a/Include/cpython/bytearrayobject.h b/Include/cpython/bytearrayobject.h index 569b0cd0369861..34cfb253738b27 100644 --- a/Include/cpython/bytearrayobject.h +++ b/Include/cpython/bytearrayobject.h @@ -11,10 +11,13 @@ typedef struct { Py_ssize_t ob_exports; /* How many buffer exports */ } PyByteArrayObject; +PyAPI_DATA(char) _PyByteArray_empty_string[]; + /* Macros, trading safety for speed */ +#define _PyByteArray_CAST(op) \ + (assert(PyByteArray_Check(op)), (PyByteArrayObject *)(op)) #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[]; +#define PyByteArray_GET_SIZE(self) \ + _Py_RVALUE(Py_SIZE(_PyByteArray_CAST(self))) diff --git a/Include/cpython/bytesobject.h b/Include/cpython/bytesobject.h index 6b3f55224fc553..eb6129a65b3aa3 100644 --- a/Include/cpython/bytesobject.h +++ b/Include/cpython/bytesobject.h @@ -29,9 +29,9 @@ 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)) +#define _PyBytes_CAST(op) (assert(PyBytes_Check(op)), (PyBytesObject *)(op)) +#define PyBytes_AS_STRING(op) (_PyBytes_CAST(op)->ob_sval) +#define PyBytes_GET_SIZE(op) _Py_RVALUE(Py_SIZE(_PyBytes_CAST(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/classobject.h b/Include/cpython/classobject.h index 80df8842eb4f78..51d0233e704ee5 100644 --- a/Include/cpython/classobject.h +++ b/Include/cpython/classobject.h @@ -29,9 +29,9 @@ PyAPI_FUNC(PyObject *) PyMethod_Self(PyObject *); /* Macros for direct access to these values. Type checks are *not* done, so use with care. */ #define PyMethod_GET_FUNCTION(meth) \ - (((PyMethodObject *)meth) -> im_func) + _Py_RVALUE(((PyMethodObject *)meth) -> im_func) #define PyMethod_GET_SELF(meth) \ - (((PyMethodObject *)meth) -> im_self) + _Py_RVALUE(((PyMethodObject *)meth) -> im_self) typedef struct { PyObject_HEAD @@ -48,7 +48,7 @@ PyAPI_FUNC(PyObject *) PyInstanceMethod_Function(PyObject *); /* Macros for direct access to these values. Type checks are *not* done, so use with care. */ #define PyInstanceMethod_GET_FUNCTION(meth) \ - (((PyInstanceMethodObject *)meth) -> func) + _Py_RVALUE(((PyInstanceMethodObject *)meth) -> func) #ifdef __cplusplus } diff --git a/Include/cpython/dictobject.h b/Include/cpython/dictobject.h index e97969be4de0cc..cbf1e655944c28 100644 --- a/Include/cpython/dictobject.h +++ b/Include/cpython/dictobject.h @@ -46,7 +46,8 @@ PyAPI_FUNC(int) _PyDict_Next( PyObject *mp, Py_ssize_t *pos, PyObject **key, PyObject **value, Py_hash_t *hash); /* Get the number of items of a dictionary. */ -#define PyDict_GET_SIZE(mp) (assert(PyDict_Check(mp)),((PyDictObject *)mp)->ma_used) +#define PyDict_GET_SIZE(mp) \ + _Py_RVALUE((assert(PyDict_Check(mp)), ((PyDictObject *)mp)->ma_used)) PyAPI_FUNC(int) _PyDict_Contains_KnownHash(PyObject *, PyObject *, Py_hash_t); PyAPI_FUNC(int) _PyDict_ContainsId(PyObject *, struct _Py_Identifier *); PyAPI_FUNC(PyObject *) _PyDict_NewPresized(Py_ssize_t minused); diff --git a/Include/cpython/floatobject.h b/Include/cpython/floatobject.h index fffd468690274e..c9c132651dd05a 100644 --- a/Include/cpython/floatobject.h +++ b/Include/cpython/floatobject.h @@ -8,5 +8,6 @@ typedef struct { } 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) +// It only checks if op is a double object in debug mode. +#define _PyFloat_CAST(op) (assert(PyFloat_Check(op)), (PyFloatObject *)(op)) +#define PyFloat_AS_DOUBLE(op) _Py_RVALUE(_PyFloat_CAST(op)->ob_fval) diff --git a/Include/cpython/funcobject.h b/Include/cpython/funcobject.h index 9f0560fb725036..27d9a7c8fbdc40 100644 --- a/Include/cpython/funcobject.h +++ b/Include/cpython/funcobject.h @@ -86,20 +86,21 @@ uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func); /* Macros for direct access to these values. Type checks are *not* done, so use with care. */ +#define _PyFunction_CAST(func) ((PyFunctionObject *)func) #define PyFunction_GET_CODE(func) \ - (((PyFunctionObject *)func) -> func_code) + _Py_RVALUE(_PyFunction_CAST(func)->func_code) #define PyFunction_GET_GLOBALS(func) \ - (((PyFunctionObject *)func) -> func_globals) + _Py_RVALUE(_PyFunction_CAST(func)->func_globals) #define PyFunction_GET_MODULE(func) \ - (((PyFunctionObject *)func) -> func_module) + _Py_RVALUE(_PyFunction_CAST(func)->func_module) #define PyFunction_GET_DEFAULTS(func) \ - (((PyFunctionObject *)func) -> func_defaults) + _Py_RVALUE(_PyFunction_CAST(func)->func_defaults) #define PyFunction_GET_KW_DEFAULTS(func) \ - (((PyFunctionObject *)func) -> func_kwdefaults) + _Py_RVALUE(_PyFunction_CAST(func)->func_kwdefaults) #define PyFunction_GET_CLOSURE(func) \ - (((PyFunctionObject *)func) -> func_closure) + _Py_RVALUE(_PyFunction_CAST(func)->func_closure) #define PyFunction_GET_ANNOTATIONS(func) \ - (((PyFunctionObject *)func) -> func_annotations) + _Py_RVALUE(_PyFunction_CAST(func)->func_annotations) /* The classmethod and staticmethod types lives here, too */ PyAPI_DATA(PyTypeObject) PyClassMethod_Type; diff --git a/Include/cpython/listobject.h b/Include/cpython/listobject.h index 51687d866ce971..c596fa500a6ba4 100644 --- a/Include/cpython/listobject.h +++ b/Include/cpython/listobject.h @@ -31,4 +31,4 @@ PyAPI_FUNC(void) _PyList_DebugMallocStats(FILE *out); #define PyList_GET_ITEM(op, i) (_PyList_CAST(op)->ob_item[i]) #define PyList_SET_ITEM(op, i, v) _Py_RVALUE(_PyList_CAST(op)->ob_item[i] = (v)) -#define PyList_GET_SIZE(op) Py_SIZE(_PyList_CAST(op)) +#define PyList_GET_SIZE(op) _Py_RVALUE(Py_SIZE(_PyList_CAST(op))) diff --git a/Include/cpython/methodobject.h b/Include/cpython/methodobject.h index 7ecbfe3b5e2fe8..3361cbdd238952 100644 --- a/Include/cpython/methodobject.h +++ b/Include/cpython/methodobject.h @@ -9,16 +9,18 @@ PyAPI_DATA(PyTypeObject) PyCMethod_Type; /* Macros for direct access to these values. Type checks are *not* done, so use with care. */ +#define _PyCFunction_CAST(func) ((PyCFunctionObject *)func) +#define _PyCFunction_ML(func) (_PyCFunction_CAST(func)->m_ml) #define PyCFunction_GET_FUNCTION(func) \ - (((PyCFunctionObject *)func) -> m_ml -> ml_meth) + _Py_RVALUE(_PyCFunction_ML(func)->ml_meth) #define PyCFunction_GET_SELF(func) \ - (((PyCFunctionObject *)func) -> m_ml -> ml_flags & METH_STATIC ? \ - NULL : ((PyCFunctionObject *)func) -> m_self) + _Py_RVALUE(_PyCFunction_ML(func)->ml_flags & METH_STATIC ? \ + NULL : _PyCFunction_CAST(func)->m_self) #define PyCFunction_GET_FLAGS(func) \ - (((PyCFunctionObject *)func) -> m_ml -> ml_flags) + _Py_RVALUE(_PyCFunction_ML(func)-> ml_flags) #define PyCFunction_GET_CLASS(func) \ - (((PyCFunctionObject *)func) -> m_ml -> ml_flags & METH_METHOD ? \ - ((PyCMethodObject *)func) -> mm_class : NULL) + _Py_RVALUE(_PyCFunction_ML(func)->ml_flags & METH_METHOD ? \ + ((PyCMethodObject *)func)->mm_class : NULL) typedef struct { PyObject_HEAD diff --git a/Include/cpython/object.h b/Include/cpython/object.h index 3a8a256e3b9ae6..c28f2fd90c61ad 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -296,7 +296,7 @@ typedef struct _heaptypeobject { /* access macro to the members which are floating "behind" the object */ #define PyHeapType_GET_MEMBERS(etype) \ - ((PyMemberDef *)(((char *)etype) + Py_TYPE(etype)->tp_basicsize)) + _Py_RVALUE((PyMemberDef *)(((char *)etype) + Py_TYPE(etype)->tp_basicsize)) PyAPI_FUNC(const char *) _PyType_Name(PyTypeObject *); PyAPI_FUNC(PyObject *) _PyType_Lookup(PyTypeObject *, PyObject *); diff --git a/Include/cpython/tupleobject.h b/Include/cpython/tupleobject.h index fc37c4e6de1cab..56ec8812a32400 100644 --- a/Include/cpython/tupleobject.h +++ b/Include/cpython/tupleobject.h @@ -18,8 +18,11 @@ PyAPI_FUNC(void) _PyTuple_MaybeUntrack(PyObject *); /* Cast argument to PyTupleObject* type. */ #define _PyTuple_CAST(op) (assert(PyTuple_Check(op)), (PyTupleObject *)(op)) -#define PyTuple_GET_SIZE(op) Py_SIZE(_PyTuple_CAST(op)) +#define PyTuple_GET_SIZE(op) _Py_RVALUE(Py_SIZE(_PyTuple_CAST(op))) +// Don't use _Py_RVALUE() for now since many C extensions abuse +// PyTuple_GET_ITEM() to get the PyTuple_GET_ITEM.ob_item array +// using: "&PyTuple_GET_ITEM(tuple, 0)". #define PyTuple_GET_ITEM(op, i) (_PyTuple_CAST(op)->ob_item[i]) /* Macro, *only* to be used to fill in brand new tuples */ diff --git a/Include/cpython/weakrefobject.h b/Include/cpython/weakrefobject.h index 9efcc412df9bed..231dc12ae89d13 100644 --- a/Include/cpython/weakrefobject.h +++ b/Include/cpython/weakrefobject.h @@ -41,7 +41,7 @@ PyAPI_FUNC(void) _PyWeakref_ClearRef(PyWeakReference *self); has dropped to zero. In the meantime, code accessing the weakref will be able to "see" the target object even though it is supposed to be unreachable. See issue #16602. */ -#define PyWeakref_GET_OBJECT(ref) \ - (Py_REFCNT(((PyWeakReference *)(ref))->wr_object) > 0 \ - ? ((PyWeakReference *)(ref))->wr_object \ - : Py_None) +#define PyWeakref_GET_OBJECT(ref) \ + _Py_RVALUE(Py_REFCNT(((PyWeakReference *)(ref))->wr_object) > 0 \ + ? ((PyWeakReference *)(ref))->wr_object \ + : Py_None) diff --git a/Include/memoryobject.h b/Include/memoryobject.h index 306028f4b225d8..a12d5d56878063 100644 --- a/Include/memoryobject.h +++ b/Include/memoryobject.h @@ -15,9 +15,11 @@ PyAPI_DATA(PyTypeObject) PyMemoryView_Type; #ifndef Py_LIMITED_API /* Get a pointer to the memoryview's private copy of the exporter's buffer. */ -#define PyMemoryView_GET_BUFFER(op) (&((PyMemoryViewObject *)(op))->view) +#define PyMemoryView_GET_BUFFER(op) \ + _Py_RVALUE(&((PyMemoryViewObject *)(op))->view) /* Get a pointer to the exporting object (this may be NULL!). */ -#define PyMemoryView_GET_BASE(op) (((PyMemoryViewObject *)(op))->view.obj) +#define PyMemoryView_GET_BASE(op) \ + _Py_RVALUE(((PyMemoryViewObject *)(op))->view.obj) #endif PyAPI_FUNC(PyObject *) PyMemoryView_FromObject(PyObject *base); diff --git a/Include/setobject.h b/Include/setobject.h index 62516be5ab29be..9db68d5dbdabe3 100644 --- a/Include/setobject.h +++ b/Include/setobject.h @@ -64,7 +64,8 @@ typedef struct { PyObject *weakreflist; /* List of weak references */ } PySetObject; -#define PySet_GET_SIZE(so) (assert(PyAnySet_Check(so)),(((PySetObject *)(so))->used)) +#define PySet_GET_SIZE(so) \ + _Py_RVALUE((assert(PyAnySet_Check(so)), ((PySetObject *)(so))->used)) PyAPI_DATA(PyObject *) _PySet_Dummy; diff --git a/Misc/NEWS.d/next/C API/2021-11-30-11-12-53.bpo-45476.IFVjjP.rst b/Misc/NEWS.d/next/C API/2021-11-30-11-12-53.bpo-45476.IFVjjP.rst new file mode 100644 index 00000000000000..1f0f2c8fb19c9f --- /dev/null +++ b/Misc/NEWS.d/next/C API/2021-11-30-11-12-53.bpo-45476.IFVjjP.rst @@ -0,0 +1,33 @@ +The following "GET" and "AS" functions can no longer be used as l-value (to +modify a Python object): + +* :c:func:`PyByteArray_GET_SIZE` +* :c:func:`PyBytes_GET_SIZE` +* :c:func:`PyCFunction_GET_CLASS` +* :c:func:`PyCFunction_GET_FLAGS` +* :c:func:`PyCFunction_GET_FUNCTION` +* :c:func:`PyCFunction_GET_SELF` +* :c:func:`PyDict_GET_SIZE` +* :c:func:`PyFloat_AS_DOUBLE` +* :c:func:`PyFunction_GET_ANNOTATIONS` +* :c:func:`PyFunction_GET_CLOSURE` +* :c:func:`PyFunction_GET_CODE` +* :c:func:`PyFunction_GET_DEFAULTS` +* :c:func:`PyFunction_GET_GLOBALS` +* :c:func:`PyFunction_GET_KW_DEFAULTS` +* :c:func:`PyFunction_GET_MODULE` +* :c:func:`PyHeapType_GET_MEMBERS` +* :c:func:`PyInstanceMethod_GET_FUNCTION` +* :c:func:`PyList_GET_SIZE` +* :c:func:`PyMemoryView_GET_BASE` +* :c:func:`PyMemoryView_GET_BUFFER` +* :c:func:`PyMethod_GET_FUNCTION` +* :c:func:`PyMethod_GET_SELF` +* :c:func:`PySet_GET_SIZE` +* :c:func:`PyTuple_GET_SIZE` +* :c:func:`PyWeakref_GET_OBJECT` + +For example, ``PyList_GET_SIZE(list) = 5;`` now fails with a compiler error. +The :c:func:`Py_SET_SIZE` function must be used instead. + +Patch by Victor Stinner.