gh-89653: PEP 670: Convert macros to functions (#99843)

Convert macros to static inline functions to avoid macro pitfalls,
like duplication of side effects:

* DK_ENTRIES()
* DK_UNICODE_ENTRIES()
* PyCode_GetNumFree()
* PyFloat_AS_DOUBLE()
* PyInstanceMethod_GET_FUNCTION()
* PyMemoryView_GET_BASE()
* PyMemoryView_GET_BUFFER()
* PyMethod_GET_FUNCTION()
* PyMethod_GET_SELF()
* PySet_GET_SIZE()
* _PyHeapType_GET_MEMBERS()

Changes:

* PyCode_GetNumFree() casts PyCode_GetNumFree.co_nfreevars from int
  to Py_ssize_t to be future proof, and because Py_ssize_t is
  commonly used in the C API.
* PyCode_GetNumFree() doesn't cast its argument: the replaced macro
  already required the exact type PyCodeObject*.
* Add assertions in some functions using "CAST" macros to check
  the arguments type when Python is built with assertions
  (debug build).
* Remove an outdated comment in unicodeobject.h.
This commit is contained in:
Victor Stinner 2022-11-28 16:40:08 +01:00 committed by GitHub
parent 53eef27133
commit 02f72b8b93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 74 additions and 27 deletions

View File

@ -26,12 +26,20 @@ PyAPI_FUNC(PyObject *) PyMethod_New(PyObject *, PyObject *);
PyAPI_FUNC(PyObject *) PyMethod_Function(PyObject *); PyAPI_FUNC(PyObject *) PyMethod_Function(PyObject *);
PyAPI_FUNC(PyObject *) PyMethod_Self(PyObject *); PyAPI_FUNC(PyObject *) PyMethod_Self(PyObject *);
/* Macros for direct access to these values. Type checks are *not* #define _PyMethod_CAST(meth) \
done, so use with care. */ (assert(PyMethod_Check(meth)), _Py_CAST(PyMethodObject*, meth))
#define PyMethod_GET_FUNCTION(meth) \
(((PyMethodObject *)(meth)) -> im_func) /* Static inline functions for direct access to these values.
#define PyMethod_GET_SELF(meth) \ Type checks are *not* done, so use with care. */
(((PyMethodObject *)(meth)) -> im_self) static inline PyObject* PyMethod_GET_FUNCTION(PyObject *meth) {
return _PyMethod_CAST(meth)->im_func;
}
#define PyMethod_GET_FUNCTION(meth) PyMethod_GET_FUNCTION(_PyObject_CAST(meth))
static inline PyObject* PyMethod_GET_SELF(PyObject *meth) {
return _PyMethod_CAST(meth)->im_self;
}
#define PyMethod_GET_SELF(meth) PyMethod_GET_SELF(_PyObject_CAST(meth))
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
@ -45,10 +53,16 @@ PyAPI_DATA(PyTypeObject) PyInstanceMethod_Type;
PyAPI_FUNC(PyObject *) PyInstanceMethod_New(PyObject *); PyAPI_FUNC(PyObject *) PyInstanceMethod_New(PyObject *);
PyAPI_FUNC(PyObject *) PyInstanceMethod_Function(PyObject *); PyAPI_FUNC(PyObject *) PyInstanceMethod_Function(PyObject *);
/* Macros for direct access to these values. Type checks are *not* #define _PyInstanceMethod_CAST(meth) \
done, so use with care. */ (assert(PyInstanceMethod_Check(meth)), \
#define PyInstanceMethod_GET_FUNCTION(meth) \ _Py_CAST(PyInstanceMethodObject*, meth))
(((PyInstanceMethodObject *)(meth)) -> func)
/* Static inline function for direct access to these values.
Type checks are *not* done, so use with care. */
static inline PyObject* PyInstanceMethod_GET_FUNCTION(PyObject *meth) {
return _PyInstanceMethod_CAST(meth)->func;
}
#define PyInstanceMethod_GET_FUNCTION(meth) PyInstanceMethod_GET_FUNCTION(_PyObject_CAST(meth))
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -147,7 +147,12 @@ struct PyCodeObject _PyCode_DEF(1);
PyAPI_DATA(PyTypeObject) PyCode_Type; PyAPI_DATA(PyTypeObject) PyCode_Type;
#define PyCode_Check(op) Py_IS_TYPE((op), &PyCode_Type) #define PyCode_Check(op) Py_IS_TYPE((op), &PyCode_Type)
#define PyCode_GetNumFree(op) ((op)->co_nfreevars)
static inline Py_ssize_t PyCode_GetNumFree(PyCodeObject *op) {
assert(PyCode_Check(op));
return op->co_nfreevars;
}
#define _PyCode_CODE(CO) ((_Py_CODEUNIT *)(CO)->co_code_adaptive) #define _PyCode_CODE(CO) ((_Py_CODEUNIT *)(CO)->co_code_adaptive)
#define _PyCode_NBYTES(CO) (Py_SIZE(CO) * (Py_ssize_t)sizeof(_Py_CODEUNIT)) #define _PyCode_NBYTES(CO) (Py_SIZE(CO) * (Py_ssize_t)sizeof(_Py_CODEUNIT))

View File

@ -7,9 +7,15 @@ typedef struct {
double ob_fval; double ob_fval;
} PyFloatObject; } PyFloatObject;
// Macro version of PyFloat_AsDouble() trading safety for speed. #define _PyFloat_CAST(op) \
(assert(PyFloat_Check(op)), _Py_CAST(PyFloatObject*, op))
// Static inline version of PyFloat_AsDouble() trading safety for speed.
// It doesn't check if op is a double object. // It doesn't check if op is a double object.
#define PyFloat_AS_DOUBLE(op) (((PyFloatObject *)(op))->ob_fval) static inline double PyFloat_AS_DOUBLE(PyObject *op) {
return _PyFloat_CAST(op)->ob_fval;
}
#define PyFloat_AS_DOUBLE(op) PyFloat_AS_DOUBLE(_PyObject_CAST(op))
PyAPI_FUNC(int) PyFloat_Pack2(double x, char *p, int le); PyAPI_FUNC(int) PyFloat_Pack2(double x, char *p, int le);

View File

@ -36,7 +36,16 @@ typedef struct {
Py_ssize_t ob_array[1]; /* shape, strides, suboffsets */ Py_ssize_t ob_array[1]; /* shape, strides, suboffsets */
} PyMemoryViewObject; } PyMemoryViewObject;
#define _PyMemoryView_CAST(op) _Py_CAST(PyMemoryViewObject*, op)
/* Get a pointer to the memoryview's private copy of the exporter's buffer. */ /* Get a pointer to the memoryview's private copy of the exporter's buffer. */
#define PyMemoryView_GET_BUFFER(op) (&((PyMemoryViewObject *)(op))->view) static inline Py_buffer* PyMemoryView_GET_BUFFER(PyObject *op) {
return (&_PyMemoryView_CAST(op)->view);
}
#define PyMemoryView_GET_BUFFER(op) PyMemoryView_GET_BUFFER(_PyObject_CAST(op))
/* Get a pointer to the exporting object (this may be NULL!). */ /* Get a pointer to the exporting object (this may be NULL!). */
#define PyMemoryView_GET_BASE(op) (((PyMemoryViewObject *)(op))->view.obj) static inline PyObject* PyMemoryView_GET_BASE(PyObject *op) {
return _PyMemoryView_CAST(op)->view.obj;
}
#define PyMemoryView_GET_BASE(op) PyMemoryView_GET_BASE(_PyObject_CAST(op))

View File

@ -58,8 +58,13 @@ typedef struct {
PyObject *weakreflist; /* List of weak references */ PyObject *weakreflist; /* List of weak references */
} PySetObject; } PySetObject;
#define PySet_GET_SIZE(so) \ #define _PySet_CAST(so) \
(assert(PyAnySet_Check(so)), (((PySetObject *)(so))->used)) (assert(PyAnySet_Check(so)), _Py_CAST(PySetObject*, so))
static inline Py_ssize_t PySet_GET_SIZE(PyObject *so) {
return _PySet_CAST(so)->used;
}
#define PySet_GET_SIZE(so) PySet_GET_SIZE(_PyObject_CAST(so))
PyAPI_DATA(PyObject *) _PySet_Dummy; PyAPI_DATA(PyObject *) _PySet_Dummy;

View File

@ -231,8 +231,6 @@ enum PyUnicode_Kind {
// new compiler warnings on "kind < PyUnicode_KIND(str)" (compare signed and // new compiler warnings on "kind < PyUnicode_KIND(str)" (compare signed and
// unsigned numbers) where kind type is an int or on // unsigned numbers) where kind type is an int or on
// "unsigned int kind = PyUnicode_KIND(str)" (cast signed to unsigned). // "unsigned int kind = PyUnicode_KIND(str)" (cast signed to unsigned).
// Only declare the function as static inline function in the limited C API
// version 3.12 which is stricter.
#define PyUnicode_KIND(op) (_PyASCIIObject_CAST(op)->state.kind) #define PyUnicode_KIND(op) (_PyASCIIObject_CAST(op)->state.kind)
/* Return a void pointer to the raw unicode buffer. */ /* Return a void pointer to the raw unicode buffer. */

View File

@ -128,12 +128,21 @@ struct _dictvalues {
#else #else
#define DK_SIZE(dk) (1<<DK_LOG_SIZE(dk)) #define DK_SIZE(dk) (1<<DK_LOG_SIZE(dk))
#endif #endif
#define DK_ENTRIES(dk) \
(assert((dk)->dk_kind == DICT_KEYS_GENERAL), \ static inline void* _DK_ENTRIES(PyDictKeysObject *dk) {
(PyDictKeyEntry*)(&((int8_t*)((dk)->dk_indices))[(size_t)1 << (dk)->dk_log2_index_bytes])) int8_t *indices = (int8_t*)(dk->dk_indices);
#define DK_UNICODE_ENTRIES(dk) \ size_t index = (size_t)1 << dk->dk_log2_index_bytes;
(assert((dk)->dk_kind != DICT_KEYS_GENERAL), \ return (&indices[index]);
(PyDictUnicodeEntry*)(&((int8_t*)((dk)->dk_indices))[(size_t)1 << (dk)->dk_log2_index_bytes])) }
static inline PyDictKeyEntry* DK_ENTRIES(PyDictKeysObject *dk) {
assert(dk->dk_kind == DICT_KEYS_GENERAL);
return (PyDictKeyEntry*)_DK_ENTRIES(dk);
}
static inline PyDictUnicodeEntry* DK_UNICODE_ENTRIES(PyDictKeysObject *dk) {
assert(dk->dk_kind != DICT_KEYS_GENERAL);
return (PyDictUnicodeEntry*)_DK_ENTRIES(dk);
}
#define DK_IS_UNICODE(dk) ((dk)->dk_kind != DICT_KEYS_GENERAL) #define DK_IS_UNICODE(dk) ((dk)->dk_kind != DICT_KEYS_GENERAL)
#define DICT_VERSION_INCREMENT (1 << DICT_MAX_WATCHERS) #define DICT_VERSION_INCREMENT (1 << DICT_MAX_WATCHERS)

View File

@ -355,8 +355,9 @@ extern int _PyType_HasSubclasses(PyTypeObject *);
extern PyObject* _PyType_GetSubclasses(PyTypeObject *); extern PyObject* _PyType_GetSubclasses(PyTypeObject *);
// Access macro to the members which are floating "behind" the object // Access macro to the members which are floating "behind" the object
#define _PyHeapType_GET_MEMBERS(etype) \ static inline PyMemberDef* _PyHeapType_GET_MEMBERS(PyHeapTypeObject *etype) {
((PyMemberDef *)(((char *)(etype)) + Py_TYPE(etype)->tp_basicsize)) return (PyMemberDef*)((char*)etype + Py_TYPE(etype)->tp_basicsize);
}
PyAPI_FUNC(PyObject *) _PyObject_LookupSpecial(PyObject *, PyObject *); PyAPI_FUNC(PyObject *) _PyObject_LookupSpecial(PyObject *, PyObject *);