Skip to content

Commit

Permalink
gh-89653: PEP 670: Convert macros to functions (#99843)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
vstinner authored Nov 28, 2022
1 parent 53eef27 commit 02f72b8
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 27 deletions.
34 changes: 24 additions & 10 deletions Include/cpython/classobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,20 @@ PyAPI_FUNC(PyObject *) PyMethod_New(PyObject *, PyObject *);
PyAPI_FUNC(PyObject *) PyMethod_Function(PyObject *);
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)
#define PyMethod_GET_SELF(meth) \
(((PyMethodObject *)(meth)) -> im_self)
#define _PyMethod_CAST(meth) \
(assert(PyMethod_Check(meth)), _Py_CAST(PyMethodObject*, meth))

/* Static inline functions for direct access to these values.
Type checks are *not* done, so use with care. */
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 {
PyObject_HEAD
Expand All @@ -45,10 +53,16 @@ PyAPI_DATA(PyTypeObject) PyInstanceMethod_Type;
PyAPI_FUNC(PyObject *) PyInstanceMethod_New(PyObject *);
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)
#define _PyInstanceMethod_CAST(meth) \
(assert(PyInstanceMethod_Check(meth)), \
_Py_CAST(PyInstanceMethodObject*, meth))

/* 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
}
Expand Down
7 changes: 6 additions & 1 deletion Include/cpython/code.h
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,12 @@ struct PyCodeObject _PyCode_DEF(1);
PyAPI_DATA(PyTypeObject) 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_NBYTES(CO) (Py_SIZE(CO) * (Py_ssize_t)sizeof(_Py_CODEUNIT))

Expand Down
10 changes: 8 additions & 2 deletions Include/cpython/floatobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,15 @@ typedef struct {
double ob_fval;
} 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.
#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);
Expand Down
13 changes: 11 additions & 2 deletions Include/cpython/memoryobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,16 @@ typedef struct {
Py_ssize_t ob_array[1]; /* shape, strides, suboffsets */
} PyMemoryViewObject;

#define _PyMemoryView_CAST(op) _Py_CAST(PyMemoryViewObject*, op)

/* 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!). */
#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))
9 changes: 7 additions & 2 deletions Include/cpython/setobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,13 @@ typedef struct {
PyObject *weakreflist; /* List of weak references */
} PySetObject;

#define PySet_GET_SIZE(so) \
(assert(PyAnySet_Check(so)), (((PySetObject *)(so))->used))
#define _PySet_CAST(so) \
(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;

Expand Down
2 changes: 0 additions & 2 deletions Include/cpython/unicodeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,8 +231,6 @@ enum PyUnicode_Kind {
// new compiler warnings on "kind < PyUnicode_KIND(str)" (compare signed and
// unsigned numbers) where kind type is an int or on
// "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)

/* Return a void pointer to the raw unicode buffer. */
Expand Down
21 changes: 15 additions & 6 deletions Include/internal/pycore_dict.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,21 @@ struct _dictvalues {
#else
#define DK_SIZE(dk) (1<<DK_LOG_SIZE(dk))
#endif
#define DK_ENTRIES(dk) \
(assert((dk)->dk_kind == DICT_KEYS_GENERAL), \
(PyDictKeyEntry*)(&((int8_t*)((dk)->dk_indices))[(size_t)1 << (dk)->dk_log2_index_bytes]))
#define DK_UNICODE_ENTRIES(dk) \
(assert((dk)->dk_kind != DICT_KEYS_GENERAL), \
(PyDictUnicodeEntry*)(&((int8_t*)((dk)->dk_indices))[(size_t)1 << (dk)->dk_log2_index_bytes]))

static inline void* _DK_ENTRIES(PyDictKeysObject *dk) {
int8_t *indices = (int8_t*)(dk->dk_indices);
size_t index = (size_t)1 << dk->dk_log2_index_bytes;
return (&indices[index]);
}
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 DICT_VERSION_INCREMENT (1 << DICT_MAX_WATCHERS)
Expand Down
5 changes: 3 additions & 2 deletions Include/internal/pycore_object.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,8 +355,9 @@ extern int _PyType_HasSubclasses(PyTypeObject *);
extern PyObject* _PyType_GetSubclasses(PyTypeObject *);

// Access macro to the members which are floating "behind" the object
#define _PyHeapType_GET_MEMBERS(etype) \
((PyMemberDef *)(((char *)(etype)) + Py_TYPE(etype)->tp_basicsize))
static inline PyMemberDef* _PyHeapType_GET_MEMBERS(PyHeapTypeObject *etype) {
return (PyMemberDef*)((char*)etype + Py_TYPE(etype)->tp_basicsize);
}

PyAPI_FUNC(PyObject *) _PyObject_LookupSpecial(PyObject *, PyObject *);

Expand Down

0 comments on commit 02f72b8

Please sign in to comment.