https://github.com/python/cpython/commit/379352620ce4d77f7248939a4cf211db48fdd241
commit: 379352620ce4d77f7248939a4cf211db48fdd241
branch: main
author: Bénédikt Tran <[email protected]>
committer: picnixz <[email protected]>
date: 2025-04-18T12:24:34+02:00
summary:
gh-132097: use a macro for semantically casting function pointers (#132406)
files:
M Include/internal/pycore_emscripten_trampoline.h
M Include/methodobject.h
M Include/pyport.h
M Objects/descrobject.c
M Objects/methodobject.c
M Objects/typeobject.c
M Python/bytecodes.c
M Python/executor_cases.c.h
M Python/generated_cases.c.h
diff --git a/Include/internal/pycore_emscripten_trampoline.h
b/Include/internal/pycore_emscripten_trampoline.h
index 7946eb5a74e974..16916f1a8eb16c 100644
--- a/Include/internal/pycore_emscripten_trampoline.h
+++ b/Include/internal/pycore_emscripten_trampoline.h
@@ -37,17 +37,18 @@ _PyEM_TrampolineCall(PyCFunctionWithKeywords func,
PyObject* kw);
#define _PyCFunction_TrampolineCall(meth, self, args) \
- _PyEM_TrampolineCall( \
- (*(PyCFunctionWithKeywords)(void(*)(void))(meth)), (self), (args),
NULL)
+ _PyEM_TrampolineCall(*_PyCFunctionWithKeywords_CAST(meth), (self), (args),
NULL)
#define _PyCFunctionWithKeywords_TrampolineCall(meth, self, args, kw) \
_PyEM_TrampolineCall((meth), (self), (args), (kw))
-#define descr_set_trampoline_call(set, obj, value, closure) \
- ((int)_PyEM_TrampolineCall((PyCFunctionWithKeywords)(set), (obj), (value),
(PyObject*)(closure)))
+#define descr_set_trampoline_call(set, obj, value, closure) \
+ ((int)_PyEM_TrampolineCall(_PyCFunctionWithKeywords_CAST(set), (obj), \
+ (value), (PyObject*)(closure)))
-#define descr_get_trampoline_call(get, obj, closure) \
- _PyEM_TrampolineCall((PyCFunctionWithKeywords)(get), (obj),
(PyObject*)(closure), NULL)
+#define descr_get_trampoline_call(get, obj, closure) \
+ _PyEM_TrampolineCall(_PyCFunctionWithKeywords_CAST(get), (obj), \
+ (PyObject*)(closure), NULL)
#else // defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)
diff --git a/Include/methodobject.h b/Include/methodobject.h
index cfff05f803309e..e6ec6421d1e59d 100644
--- a/Include/methodobject.h
+++ b/Include/methodobject.h
@@ -33,7 +33,7 @@ typedef PyObject *(*PyCMethod)(PyObject *, PyTypeObject *,
PyObject *const *,
typedef PyCFunctionFast _PyCFunctionFast;
typedef PyCFunctionFastWithKeywords _PyCFunctionFastWithKeywords;
-// Cast an function to the PyCFunction type to use it with PyMethodDef.
+// Cast a function to the PyCFunction type to use it with PyMethodDef.
//
// This macro can be used to prevent compiler warnings if the first parameter
// uses a different pointer type than PyObject* (ex: METH_VARARGS and METH_O
@@ -49,8 +49,17 @@ typedef PyCFunctionFastWithKeywords
_PyCFunctionFastWithKeywords;
// used to prevent a compiler warning. If the function has a single parameter,
// it triggers an undefined behavior when Python calls it with 2 parameters
// (bpo-33012).
-#define _PyCFunction_CAST(func) \
- _Py_CAST(PyCFunction, _Py_CAST(void(*)(void), (func)))
+#define _PyCFunction_CAST(func) \
+ _Py_FUNC_CAST(PyCFunction, func)
+// The macros below are given for semantic convenience, allowing users
+// to see whether a cast to suppress an undefined behavior is necessary.
+// Note: At runtime, the original function signature must be respected.
+#define _PyCFunctionFast_CAST(func) \
+ _Py_FUNC_CAST(PyCFunctionFast, func)
+#define _PyCFunctionWithKeywords_CAST(func) \
+ _Py_FUNC_CAST(PyCFunctionWithKeywords, func)
+#define _PyCFunctionFastWithKeywords_CAST(func) \
+ _Py_FUNC_CAST(PyCFunctionFastWithKeywords, func)
PyAPI_FUNC(PyCFunction) PyCFunction_GetFunction(PyObject *);
PyAPI_FUNC(PyObject *) PyCFunction_GetSelf(PyObject *);
diff --git a/Include/pyport.h b/Include/pyport.h
index 2a7192c2c55cdd..ebce31f1d14a01 100644
--- a/Include/pyport.h
+++ b/Include/pyport.h
@@ -36,6 +36,16 @@
// Macro to use the more powerful/dangerous C-style cast even in C++.
#define _Py_CAST(type, expr) ((type)(expr))
+// Cast a function to another function type T.
+//
+// The macro first casts the function to the "void func(void)" type
+// to prevent compiler warnings.
+//
+// Note that using this cast only prevents the compiler from emitting
+// warnings, but does not prevent an undefined behavior at runtime if
+// the original function signature is not respected.
+#define _Py_FUNC_CAST(T, func) _Py_CAST(T, _Py_CAST(void(*)(void), (func)))
+
// Static inline functions should use _Py_NULL rather than using directly NULL
// to prevent C++ compiler warnings. On C23 and newer and on C++11 and newer,
// _Py_NULL is defined as nullptr.
diff --git a/Objects/descrobject.c b/Objects/descrobject.c
index 451e07f58e161e..268af0b217cd98 100644
--- a/Objects/descrobject.c
+++ b/Objects/descrobject.c
@@ -519,7 +519,7 @@ wrapperdescr_raw_call(PyWrapperDescrObject *descr, PyObject
*self,
wrapperfunc wrapper = descr->d_base->wrapper;
if (descr->d_base->flags & PyWrapperFlag_KEYWORDS) {
- wrapperfunc_kwds wk = (wrapperfunc_kwds)(void(*)(void))wrapper;
+ wrapperfunc_kwds wk = _Py_FUNC_CAST(wrapperfunc_kwds, wrapper);
return (*wk)(self, args, descr->d_wrapped, kwds);
}
diff --git a/Objects/methodobject.c b/Objects/methodobject.c
index 189b026ab33559..8b28662631b227 100644
--- a/Objects/methodobject.c
+++ b/Objects/methodobject.c
@@ -567,7 +567,7 @@ cfunction_call(PyObject *func, PyObject *args, PyObject
*kwargs)
PyObject *result;
if (flags & METH_KEYWORDS) {
result = _PyCFunctionWithKeywords_TrampolineCall(
- (*(PyCFunctionWithKeywords)(void(*)(void))meth),
+ *_PyCFunctionWithKeywords_CAST(meth),
self, args, kwargs);
}
else {
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 982f41fd47f92c..5663aee3c2e069 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -10807,7 +10807,8 @@ static pytype_slotdef slotdefs[] = {
"__repr__($self, /)\n--\n\nReturn repr(self)."),
TPSLOT(__hash__, tp_hash, slot_tp_hash, wrap_hashfunc,
"__hash__($self, /)\n--\n\nReturn hash(self)."),
- FLSLOT(__call__, tp_call, slot_tp_call,
(wrapperfunc)(void(*)(void))wrap_call,
+ FLSLOT(__call__, tp_call, slot_tp_call,
+ _Py_FUNC_CAST(wrapperfunc, wrap_call),
"__call__($self, /, *args, **kwargs)\n--\n\nCall self as a
function.",
PyWrapperFlag_KEYWORDS),
TPSLOT(__str__, tp_str, slot_tp_str, wrap_unaryfunc,
@@ -10844,7 +10845,8 @@ static pytype_slotdef slotdefs[] = {
TPSLOT(__delete__, tp_descr_set, slot_tp_descr_set,
wrap_descr_delete,
"__delete__($self, instance, /)\n--\n\nDelete an attribute of
instance."),
- FLSLOT(__init__, tp_init, slot_tp_init,
(wrapperfunc)(void(*)(void))wrap_init,
+ FLSLOT(__init__, tp_init, slot_tp_init,
+ _Py_FUNC_CAST(wrapperfunc, wrap_init),
"__init__($self, /, *args, **kwargs)\n--\n\n"
"Initialize self. See help(type(self)) for accurate signature.",
PyWrapperFlag_KEYWORDS),
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index 95786c91371e98..8aa41c2e412932 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -4170,7 +4170,7 @@ dummy_func(
DECREF_INPUTS();
ERROR_IF(true, error);
}
- PyObject *res_o = ((PyCFunctionFast)(void(*)(void))cfunc)(
+ PyObject *res_o = _PyCFunctionFast_CAST(cfunc)(
PyCFunction_GET_SELF(callable_o),
args_o,
total_args);
@@ -4202,8 +4202,7 @@ dummy_func(
STAT_INC(CALL, hit);
/* res = func(self, arguments, nargs, kwnames) */
PyCFunctionFastWithKeywords cfunc =
- (PyCFunctionFastWithKeywords)(void(*)(void))
- PyCFunction_GET_FUNCTION(callable_o);
+
_PyCFunctionFastWithKeywords_CAST(PyCFunction_GET_FUNCTION(callable_o));
STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
if (CONVERSION_FAILED(args_o)) {
@@ -4371,7 +4370,7 @@ dummy_func(
ERROR_IF(true, error);
}
PyCFunctionFastWithKeywords cfunc =
- (PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth;
+ _PyCFunctionFastWithKeywords_CAST(meth->ml_meth);
PyObject *res_o = cfunc(self, (args_o + 1), nargs, NULL);
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
@@ -4450,8 +4449,7 @@ dummy_func(
DECREF_INPUTS();
ERROR_IF(true, error);
}
- PyCFunctionFast cfunc =
- (PyCFunctionFast)(void(*)(void))meth->ml_meth;
+ PyCFunctionFast cfunc = _PyCFunctionFast_CAST(meth->ml_meth);
PyObject *res_o = cfunc(self, (args_o + 1), nargs);
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h
index 9bfb13e2d9773f..3bb1991b526a7a 100644
--- a/Python/executor_cases.c.h
+++ b/Python/executor_cases.c.h
@@ -5507,7 +5507,7 @@
JUMP_TO_ERROR();
}
_PyFrame_SetStackPointer(frame, stack_pointer);
- PyObject *res_o = ((PyCFunctionFast)(void(*)(void))cfunc)(
+ PyObject *res_o = _PyCFunctionFast_CAST(cfunc)(
PyCFunction_GET_SELF(callable_o),
args_o,
total_args);
@@ -5567,8 +5567,7 @@
STAT_INC(CALL, hit);
_PyFrame_SetStackPointer(frame, stack_pointer);
PyCFunctionFastWithKeywords cfunc =
- (PyCFunctionFastWithKeywords)(void(*)(void))
- PyCFunction_GET_FUNCTION(callable_o);
+
_PyCFunctionFastWithKeywords_CAST(PyCFunction_GET_FUNCTION(callable_o));
stack_pointer = _PyFrame_GetStackPointer(frame);
STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
if (CONVERSION_FAILED(args_o)) {
@@ -5918,7 +5917,7 @@
}
_PyFrame_SetStackPointer(frame, stack_pointer);
PyCFunctionFastWithKeywords cfunc =
- (PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth;
+ _PyCFunctionFastWithKeywords_CAST(meth->ml_meth);
PyObject *res_o = cfunc(self, (args_o + 1), nargs, NULL);
stack_pointer = _PyFrame_GetStackPointer(frame);
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
@@ -6073,8 +6072,7 @@
JUMP_TO_ERROR();
}
_PyFrame_SetStackPointer(frame, stack_pointer);
- PyCFunctionFast cfunc =
- (PyCFunctionFast)(void(*)(void))meth->ml_meth;
+ PyCFunctionFast cfunc = _PyCFunctionFast_CAST(meth->ml_meth);
PyObject *res_o = cfunc(self, (args_o + 1), nargs);
stack_pointer = _PyFrame_GetStackPointer(frame);
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 6fe647d6197a07..51845ebeeb2a04 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -2060,7 +2060,7 @@
JUMP_TO_LABEL(error);
}
_PyFrame_SetStackPointer(frame, stack_pointer);
- PyObject *res_o = ((PyCFunctionFast)(void(*)(void))cfunc)(
+ PyObject *res_o = _PyCFunctionFast_CAST(cfunc)(
PyCFunction_GET_SELF(callable_o),
args_o,
total_args);
@@ -2153,8 +2153,7 @@
STAT_INC(CALL, hit);
_PyFrame_SetStackPointer(frame, stack_pointer);
PyCFunctionFastWithKeywords cfunc =
- (PyCFunctionFastWithKeywords)(void(*)(void))
- PyCFunction_GET_FUNCTION(callable_o);
+
_PyCFunctionFastWithKeywords_CAST(PyCFunction_GET_FUNCTION(callable_o));
stack_pointer = _PyFrame_GetStackPointer(frame);
STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
if (CONVERSION_FAILED(args_o)) {
@@ -3377,8 +3376,7 @@
JUMP_TO_LABEL(error);
}
_PyFrame_SetStackPointer(frame, stack_pointer);
- PyCFunctionFast cfunc =
- (PyCFunctionFast)(void(*)(void))meth->ml_meth;
+ PyCFunctionFast cfunc = _PyCFunctionFast_CAST(meth->ml_meth);
PyObject *res_o = cfunc(self, (args_o + 1), nargs);
stack_pointer = _PyFrame_GetStackPointer(frame);
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
@@ -3505,7 +3503,7 @@
}
_PyFrame_SetStackPointer(frame, stack_pointer);
PyCFunctionFastWithKeywords cfunc =
- (PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth;
+ _PyCFunctionFastWithKeywords_CAST(meth->ml_meth);
PyObject *res_o = cfunc(self, (args_o + 1), nargs, NULL);
stack_pointer = _PyFrame_GetStackPointer(frame);
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]