Skip to content

Commit 3793526

Browse files
authored
gh-132097: use a macro for semantically casting function pointers (#132406)
1 parent f3d877a commit 3793526

9 files changed

+47
-31
lines changed

Diff for: Include/internal/pycore_emscripten_trampoline.h

+7-6
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,18 @@ _PyEM_TrampolineCall(PyCFunctionWithKeywords func,
3737
PyObject* kw);
3838

3939
#define _PyCFunction_TrampolineCall(meth, self, args) \
40-
_PyEM_TrampolineCall( \
41-
(*(PyCFunctionWithKeywords)(void(*)(void))(meth)), (self), (args), NULL)
40+
_PyEM_TrampolineCall(*_PyCFunctionWithKeywords_CAST(meth), (self), (args), NULL)
4241

4342
#define _PyCFunctionWithKeywords_TrampolineCall(meth, self, args, kw) \
4443
_PyEM_TrampolineCall((meth), (self), (args), (kw))
4544

46-
#define descr_set_trampoline_call(set, obj, value, closure) \
47-
((int)_PyEM_TrampolineCall((PyCFunctionWithKeywords)(set), (obj), (value), (PyObject*)(closure)))
45+
#define descr_set_trampoline_call(set, obj, value, closure) \
46+
((int)_PyEM_TrampolineCall(_PyCFunctionWithKeywords_CAST(set), (obj), \
47+
(value), (PyObject*)(closure)))
4848

49-
#define descr_get_trampoline_call(get, obj, closure) \
50-
_PyEM_TrampolineCall((PyCFunctionWithKeywords)(get), (obj), (PyObject*)(closure), NULL)
49+
#define descr_get_trampoline_call(get, obj, closure) \
50+
_PyEM_TrampolineCall(_PyCFunctionWithKeywords_CAST(get), (obj), \
51+
(PyObject*)(closure), NULL)
5152

5253

5354
#else // defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)

Diff for: Include/methodobject.h

+12-3
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ typedef PyObject *(*PyCMethod)(PyObject *, PyTypeObject *, PyObject *const *,
3333
typedef PyCFunctionFast _PyCFunctionFast;
3434
typedef PyCFunctionFastWithKeywords _PyCFunctionFastWithKeywords;
3535

36-
// Cast an function to the PyCFunction type to use it with PyMethodDef.
36+
// Cast a function to the PyCFunction type to use it with PyMethodDef.
3737
//
3838
// This macro can be used to prevent compiler warnings if the first parameter
3939
// uses a different pointer type than PyObject* (ex: METH_VARARGS and METH_O
@@ -49,8 +49,17 @@ typedef PyCFunctionFastWithKeywords _PyCFunctionFastWithKeywords;
4949
// used to prevent a compiler warning. If the function has a single parameter,
5050
// it triggers an undefined behavior when Python calls it with 2 parameters
5151
// (bpo-33012).
52-
#define _PyCFunction_CAST(func) \
53-
_Py_CAST(PyCFunction, _Py_CAST(void(*)(void), (func)))
52+
#define _PyCFunction_CAST(func) \
53+
_Py_FUNC_CAST(PyCFunction, func)
54+
// The macros below are given for semantic convenience, allowing users
55+
// to see whether a cast to suppress an undefined behavior is necessary.
56+
// Note: At runtime, the original function signature must be respected.
57+
#define _PyCFunctionFast_CAST(func) \
58+
_Py_FUNC_CAST(PyCFunctionFast, func)
59+
#define _PyCFunctionWithKeywords_CAST(func) \
60+
_Py_FUNC_CAST(PyCFunctionWithKeywords, func)
61+
#define _PyCFunctionFastWithKeywords_CAST(func) \
62+
_Py_FUNC_CAST(PyCFunctionFastWithKeywords, func)
5463

5564
PyAPI_FUNC(PyCFunction) PyCFunction_GetFunction(PyObject *);
5665
PyAPI_FUNC(PyObject *) PyCFunction_GetSelf(PyObject *);

Diff for: Include/pyport.h

+10
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@
3636
// Macro to use the more powerful/dangerous C-style cast even in C++.
3737
#define _Py_CAST(type, expr) ((type)(expr))
3838

39+
// Cast a function to another function type T.
40+
//
41+
// The macro first casts the function to the "void func(void)" type
42+
// to prevent compiler warnings.
43+
//
44+
// Note that using this cast only prevents the compiler from emitting
45+
// warnings, but does not prevent an undefined behavior at runtime if
46+
// the original function signature is not respected.
47+
#define _Py_FUNC_CAST(T, func) _Py_CAST(T, _Py_CAST(void(*)(void), (func)))
48+
3949
// Static inline functions should use _Py_NULL rather than using directly NULL
4050
// to prevent C++ compiler warnings. On C23 and newer and on C++11 and newer,
4151
// _Py_NULL is defined as nullptr.

Diff for: Objects/descrobject.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ wrapperdescr_raw_call(PyWrapperDescrObject *descr, PyObject *self,
519519
wrapperfunc wrapper = descr->d_base->wrapper;
520520

521521
if (descr->d_base->flags & PyWrapperFlag_KEYWORDS) {
522-
wrapperfunc_kwds wk = (wrapperfunc_kwds)(void(*)(void))wrapper;
522+
wrapperfunc_kwds wk = _Py_FUNC_CAST(wrapperfunc_kwds, wrapper);
523523
return (*wk)(self, args, descr->d_wrapped, kwds);
524524
}
525525

Diff for: Objects/methodobject.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ cfunction_call(PyObject *func, PyObject *args, PyObject *kwargs)
567567
PyObject *result;
568568
if (flags & METH_KEYWORDS) {
569569
result = _PyCFunctionWithKeywords_TrampolineCall(
570-
(*(PyCFunctionWithKeywords)(void(*)(void))meth),
570+
*_PyCFunctionWithKeywords_CAST(meth),
571571
self, args, kwargs);
572572
}
573573
else {

Diff for: Objects/typeobject.c

+4-2
Original file line numberDiff line numberDiff line change
@@ -10807,7 +10807,8 @@ static pytype_slotdef slotdefs[] = {
1080710807
"__repr__($self, /)\n--\n\nReturn repr(self)."),
1080810808
TPSLOT(__hash__, tp_hash, slot_tp_hash, wrap_hashfunc,
1080910809
"__hash__($self, /)\n--\n\nReturn hash(self)."),
10810-
FLSLOT(__call__, tp_call, slot_tp_call, (wrapperfunc)(void(*)(void))wrap_call,
10810+
FLSLOT(__call__, tp_call, slot_tp_call,
10811+
_Py_FUNC_CAST(wrapperfunc, wrap_call),
1081110812
"__call__($self, /, *args, **kwargs)\n--\n\nCall self as a function.",
1081210813
PyWrapperFlag_KEYWORDS),
1081310814
TPSLOT(__str__, tp_str, slot_tp_str, wrap_unaryfunc,
@@ -10844,7 +10845,8 @@ static pytype_slotdef slotdefs[] = {
1084410845
TPSLOT(__delete__, tp_descr_set, slot_tp_descr_set,
1084510846
wrap_descr_delete,
1084610847
"__delete__($self, instance, /)\n--\n\nDelete an attribute of instance."),
10847-
FLSLOT(__init__, tp_init, slot_tp_init, (wrapperfunc)(void(*)(void))wrap_init,
10848+
FLSLOT(__init__, tp_init, slot_tp_init,
10849+
_Py_FUNC_CAST(wrapperfunc, wrap_init),
1084810850
"__init__($self, /, *args, **kwargs)\n--\n\n"
1084910851
"Initialize self. See help(type(self)) for accurate signature.",
1085010852
PyWrapperFlag_KEYWORDS),

Diff for: Python/bytecodes.c

+4-6
Original file line numberDiff line numberDiff line change
@@ -4170,7 +4170,7 @@ dummy_func(
41704170
DECREF_INPUTS();
41714171
ERROR_IF(true, error);
41724172
}
4173-
PyObject *res_o = ((PyCFunctionFast)(void(*)(void))cfunc)(
4173+
PyObject *res_o = _PyCFunctionFast_CAST(cfunc)(
41744174
PyCFunction_GET_SELF(callable_o),
41754175
args_o,
41764176
total_args);
@@ -4202,8 +4202,7 @@ dummy_func(
42024202
STAT_INC(CALL, hit);
42034203
/* res = func(self, arguments, nargs, kwnames) */
42044204
PyCFunctionFastWithKeywords cfunc =
4205-
(PyCFunctionFastWithKeywords)(void(*)(void))
4206-
PyCFunction_GET_FUNCTION(callable_o);
4205+
_PyCFunctionFastWithKeywords_CAST(PyCFunction_GET_FUNCTION(callable_o));
42074206

42084207
STACKREFS_TO_PYOBJECTS(arguments, total_args, args_o);
42094208
if (CONVERSION_FAILED(args_o)) {
@@ -4371,7 +4370,7 @@ dummy_func(
43714370
ERROR_IF(true, error);
43724371
}
43734372
PyCFunctionFastWithKeywords cfunc =
4374-
(PyCFunctionFastWithKeywords)(void(*)(void))meth->ml_meth;
4373+
_PyCFunctionFastWithKeywords_CAST(meth->ml_meth);
43754374
PyObject *res_o = cfunc(self, (args_o + 1), nargs, NULL);
43764375
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
43774376
assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL));
@@ -4450,8 +4449,7 @@ dummy_func(
44504449
DECREF_INPUTS();
44514450
ERROR_IF(true, error);
44524451
}
4453-
PyCFunctionFast cfunc =
4454-
(PyCFunctionFast)(void(*)(void))meth->ml_meth;
4452+
PyCFunctionFast cfunc = _PyCFunctionFast_CAST(meth->ml_meth);
44554453
PyObject *res_o = cfunc(self, (args_o + 1), nargs);
44564454
STACKREFS_TO_PYOBJECTS_CLEANUP(args_o);
44574455
assert((res_o != NULL) ^ (_PyErr_Occurred(tstate) != NULL));

Diff for: Python/executor_cases.c.h

+4-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: Python/generated_cases.c.h

+4-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)