https://github.com/python/cpython/commit/379352620ce4d77f7248939a4cf211db48fdd241 commit: 379352620ce4d77f7248939a4cf211db48fdd241 branch: main author: Bénédikt Tran <10796600+picn...@users.noreply.github.com> committer: picnixz <10796600+picn...@users.noreply.github.com> 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 -- python-checkins@python.org To unsubscribe send an email to python-checkins-le...@python.org https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: arch...@mail-archive.com