gh-132097: use a macro for semantically casting function pointers (#132406)

This commit is contained in:
Bénédikt Tran 2025-04-18 12:24:34 +02:00 committed by GitHub
parent f3d877a27a
commit 379352620c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 47 additions and 31 deletions

View file

@ -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)

View file

@ -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 *);

View file

@ -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.

View file

@ -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);
}

View file

@ -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 {

View file

@ -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),

View file

@ -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));

View file

@ -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);

View file

@ -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);