Skip to content

Commit 02f72b8

Browse files
authored
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.
1 parent 53eef27 commit 02f72b8

8 files changed

+74
-27
lines changed

Include/cpython/classobject.h

+24-10
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,20 @@ PyAPI_FUNC(PyObject *) PyMethod_New(PyObject *, PyObject *);
2626
PyAPI_FUNC(PyObject *) PyMethod_Function(PyObject *);
2727
PyAPI_FUNC(PyObject *) PyMethod_Self(PyObject *);
2828

29-
/* Macros for direct access to these values. Type checks are *not*
30-
done, so use with care. */
31-
#define PyMethod_GET_FUNCTION(meth) \
32-
(((PyMethodObject *)(meth)) -> im_func)
33-
#define PyMethod_GET_SELF(meth) \
34-
(((PyMethodObject *)(meth)) -> im_self)
29+
#define _PyMethod_CAST(meth) \
30+
(assert(PyMethod_Check(meth)), _Py_CAST(PyMethodObject*, meth))
31+
32+
/* Static inline functions for direct access to these values.
33+
Type checks are *not* done, so use with care. */
34+
static inline PyObject* PyMethod_GET_FUNCTION(PyObject *meth) {
35+
return _PyMethod_CAST(meth)->im_func;
36+
}
37+
#define PyMethod_GET_FUNCTION(meth) PyMethod_GET_FUNCTION(_PyObject_CAST(meth))
38+
39+
static inline PyObject* PyMethod_GET_SELF(PyObject *meth) {
40+
return _PyMethod_CAST(meth)->im_self;
41+
}
42+
#define PyMethod_GET_SELF(meth) PyMethod_GET_SELF(_PyObject_CAST(meth))
3543

3644
typedef struct {
3745
PyObject_HEAD
@@ -45,10 +53,16 @@ PyAPI_DATA(PyTypeObject) PyInstanceMethod_Type;
4553
PyAPI_FUNC(PyObject *) PyInstanceMethod_New(PyObject *);
4654
PyAPI_FUNC(PyObject *) PyInstanceMethod_Function(PyObject *);
4755

48-
/* Macros for direct access to these values. Type checks are *not*
49-
done, so use with care. */
50-
#define PyInstanceMethod_GET_FUNCTION(meth) \
51-
(((PyInstanceMethodObject *)(meth)) -> func)
56+
#define _PyInstanceMethod_CAST(meth) \
57+
(assert(PyInstanceMethod_Check(meth)), \
58+
_Py_CAST(PyInstanceMethodObject*, meth))
59+
60+
/* Static inline function for direct access to these values.
61+
Type checks are *not* done, so use with care. */
62+
static inline PyObject* PyInstanceMethod_GET_FUNCTION(PyObject *meth) {
63+
return _PyInstanceMethod_CAST(meth)->func;
64+
}
65+
#define PyInstanceMethod_GET_FUNCTION(meth) PyInstanceMethod_GET_FUNCTION(_PyObject_CAST(meth))
5266

5367
#ifdef __cplusplus
5468
}

Include/cpython/code.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,12 @@ struct PyCodeObject _PyCode_DEF(1);
147147
PyAPI_DATA(PyTypeObject) PyCode_Type;
148148

149149
#define PyCode_Check(op) Py_IS_TYPE((op), &PyCode_Type)
150-
#define PyCode_GetNumFree(op) ((op)->co_nfreevars)
150+
151+
static inline Py_ssize_t PyCode_GetNumFree(PyCodeObject *op) {
152+
assert(PyCode_Check(op));
153+
return op->co_nfreevars;
154+
}
155+
151156
#define _PyCode_CODE(CO) ((_Py_CODEUNIT *)(CO)->co_code_adaptive)
152157
#define _PyCode_NBYTES(CO) (Py_SIZE(CO) * (Py_ssize_t)sizeof(_Py_CODEUNIT))
153158

Include/cpython/floatobject.h

+8-2
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,15 @@ typedef struct {
77
double ob_fval;
88
} PyFloatObject;
99

10-
// Macro version of PyFloat_AsDouble() trading safety for speed.
10+
#define _PyFloat_CAST(op) \
11+
(assert(PyFloat_Check(op)), _Py_CAST(PyFloatObject*, op))
12+
13+
// Static inline version of PyFloat_AsDouble() trading safety for speed.
1114
// It doesn't check if op is a double object.
12-
#define PyFloat_AS_DOUBLE(op) (((PyFloatObject *)(op))->ob_fval)
15+
static inline double PyFloat_AS_DOUBLE(PyObject *op) {
16+
return _PyFloat_CAST(op)->ob_fval;
17+
}
18+
#define PyFloat_AS_DOUBLE(op) PyFloat_AS_DOUBLE(_PyObject_CAST(op))
1319

1420

1521
PyAPI_FUNC(int) PyFloat_Pack2(double x, char *p, int le);

Include/cpython/memoryobject.h

+11-2
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,16 @@ typedef struct {
3636
Py_ssize_t ob_array[1]; /* shape, strides, suboffsets */
3737
} PyMemoryViewObject;
3838

39+
#define _PyMemoryView_CAST(op) _Py_CAST(PyMemoryViewObject*, op)
40+
3941
/* Get a pointer to the memoryview's private copy of the exporter's buffer. */
40-
#define PyMemoryView_GET_BUFFER(op) (&((PyMemoryViewObject *)(op))->view)
42+
static inline Py_buffer* PyMemoryView_GET_BUFFER(PyObject *op) {
43+
return (&_PyMemoryView_CAST(op)->view);
44+
}
45+
#define PyMemoryView_GET_BUFFER(op) PyMemoryView_GET_BUFFER(_PyObject_CAST(op))
46+
4147
/* Get a pointer to the exporting object (this may be NULL!). */
42-
#define PyMemoryView_GET_BASE(op) (((PyMemoryViewObject *)(op))->view.obj)
48+
static inline PyObject* PyMemoryView_GET_BASE(PyObject *op) {
49+
return _PyMemoryView_CAST(op)->view.obj;
50+
}
51+
#define PyMemoryView_GET_BASE(op) PyMemoryView_GET_BASE(_PyObject_CAST(op))

Include/cpython/setobject.h

+7-2
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,13 @@ typedef struct {
5858
PyObject *weakreflist; /* List of weak references */
5959
} PySetObject;
6060

61-
#define PySet_GET_SIZE(so) \
62-
(assert(PyAnySet_Check(so)), (((PySetObject *)(so))->used))
61+
#define _PySet_CAST(so) \
62+
(assert(PyAnySet_Check(so)), _Py_CAST(PySetObject*, so))
63+
64+
static inline Py_ssize_t PySet_GET_SIZE(PyObject *so) {
65+
return _PySet_CAST(so)->used;
66+
}
67+
#define PySet_GET_SIZE(so) PySet_GET_SIZE(_PyObject_CAST(so))
6368

6469
PyAPI_DATA(PyObject *) _PySet_Dummy;
6570

Include/cpython/unicodeobject.h

-2
Original file line numberDiff line numberDiff line change
@@ -231,8 +231,6 @@ enum PyUnicode_Kind {
231231
// new compiler warnings on "kind < PyUnicode_KIND(str)" (compare signed and
232232
// unsigned numbers) where kind type is an int or on
233233
// "unsigned int kind = PyUnicode_KIND(str)" (cast signed to unsigned).
234-
// Only declare the function as static inline function in the limited C API
235-
// version 3.12 which is stricter.
236234
#define PyUnicode_KIND(op) (_PyASCIIObject_CAST(op)->state.kind)
237235

238236
/* Return a void pointer to the raw unicode buffer. */

Include/internal/pycore_dict.h

+15-6
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,21 @@ struct _dictvalues {
128128
#else
129129
#define DK_SIZE(dk) (1<<DK_LOG_SIZE(dk))
130130
#endif
131-
#define DK_ENTRIES(dk) \
132-
(assert((dk)->dk_kind == DICT_KEYS_GENERAL), \
133-
(PyDictKeyEntry*)(&((int8_t*)((dk)->dk_indices))[(size_t)1 << (dk)->dk_log2_index_bytes]))
134-
#define DK_UNICODE_ENTRIES(dk) \
135-
(assert((dk)->dk_kind != DICT_KEYS_GENERAL), \
136-
(PyDictUnicodeEntry*)(&((int8_t*)((dk)->dk_indices))[(size_t)1 << (dk)->dk_log2_index_bytes]))
131+
132+
static inline void* _DK_ENTRIES(PyDictKeysObject *dk) {
133+
int8_t *indices = (int8_t*)(dk->dk_indices);
134+
size_t index = (size_t)1 << dk->dk_log2_index_bytes;
135+
return (&indices[index]);
136+
}
137+
static inline PyDictKeyEntry* DK_ENTRIES(PyDictKeysObject *dk) {
138+
assert(dk->dk_kind == DICT_KEYS_GENERAL);
139+
return (PyDictKeyEntry*)_DK_ENTRIES(dk);
140+
}
141+
static inline PyDictUnicodeEntry* DK_UNICODE_ENTRIES(PyDictKeysObject *dk) {
142+
assert(dk->dk_kind != DICT_KEYS_GENERAL);
143+
return (PyDictUnicodeEntry*)_DK_ENTRIES(dk);
144+
}
145+
137146
#define DK_IS_UNICODE(dk) ((dk)->dk_kind != DICT_KEYS_GENERAL)
138147

139148
#define DICT_VERSION_INCREMENT (1 << DICT_MAX_WATCHERS)

Include/internal/pycore_object.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -355,8 +355,9 @@ extern int _PyType_HasSubclasses(PyTypeObject *);
355355
extern PyObject* _PyType_GetSubclasses(PyTypeObject *);
356356

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

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

0 commit comments

Comments
 (0)