Skip to content

bpo-47164: Add _PyCFunction_CAST() macro #32192

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Mar 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Doc/c-api/structures.rst
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,9 @@ There are these calling conventions:
hold a reference to the module or object instance. In all cases the second
parameter will be ``NULL``.

The function must have 2 parameters. Since the second parameter is unused,
:c:macro:`Py_UNUSED` can be used to prevent a compiler warning.


.. data:: METH_O

Expand Down
18 changes: 18 additions & 0 deletions Include/methodobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,24 @@ typedef PyObject *(*_PyCFunctionFastWithKeywords) (PyObject *,
typedef PyObject *(*PyCMethod)(PyObject *, PyTypeObject *, PyObject *const *,
size_t, PyObject *);

// Cast an 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
// calling conventions).
//
// The macro can also be used for METH_FASTCALL and METH_VARARGS|METH_KEYWORDS
// calling conventions to avoid compiler warnings because the function has more
// than 2 parameters. The macro first casts the function to the
// "void func(void)" type to prevent compiler warnings.
//
// If a function is declared with the METH_NOARGS calling convention, it must
// have 2 parameters. Since the second parameter is unused, Py_UNUSED() can be
// 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) ((PyCFunction)(void(*)(void))(func))

PyAPI_FUNC(PyCFunction) PyCFunction_GetFunction(PyObject *);
PyAPI_FUNC(PyObject *) PyCFunction_GetSelf(PyObject *);
PyAPI_FUNC(int) PyCFunction_GetFlags(PyObject *);
Expand Down
6 changes: 3 additions & 3 deletions Python/_warnings.c
Original file line number Diff line number Diff line change
Expand Up @@ -1078,7 +1078,7 @@ warnings_warn_explicit(PyObject *self, PyObject *args, PyObject *kwds)
}

static PyObject *
warnings_filters_mutated(PyObject *self, PyObject *args)
warnings_filters_mutated(PyObject *self, PyObject *Py_UNUSED(args))
{
PyInterpreterState *interp = get_current_interp();
if (interp == NULL) {
Expand Down Expand Up @@ -1353,9 +1353,9 @@ PyDoc_STRVAR(warn_explicit_doc,

static PyMethodDef warnings_functions[] = {
WARNINGS_WARN_METHODDEF
{"warn_explicit", (PyCFunction)(void(*)(void))warnings_warn_explicit,
{"warn_explicit", _PyCFunction_CAST(warnings_warn_explicit),
METH_VARARGS | METH_KEYWORDS, warn_explicit_doc},
{"_filters_mutated", (PyCFunction)warnings_filters_mutated, METH_NOARGS,
{"_filters_mutated", _PyCFunction_CAST(warnings_filters_mutated), METH_NOARGS,
NULL},
/* XXX(brett.cannon): add showwarning? */
/* XXX(brett.cannon): Reasonable to add formatwarning? */
Expand Down
26 changes: 13 additions & 13 deletions Python/bltinmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,7 @@ filter_reduce(filterobject *lz, PyObject *Py_UNUSED(ignored))
PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");

static PyMethodDef filter_methods[] = {
{"__reduce__", (PyCFunction)filter_reduce, METH_NOARGS, reduce_doc},
{"__reduce__", _PyCFunction_CAST(filter_reduce), METH_NOARGS, reduce_doc},
{NULL, NULL} /* sentinel */
};

Expand Down Expand Up @@ -1354,7 +1354,7 @@ map_reduce(mapobject *lz, PyObject *Py_UNUSED(ignored))
}

static PyMethodDef map_methods[] = {
{"__reduce__", (PyCFunction)map_reduce, METH_NOARGS, reduce_doc},
{"__reduce__", _PyCFunction_CAST(map_reduce), METH_NOARGS, reduce_doc},
{NULL, NULL} /* sentinel */
};

Expand Down Expand Up @@ -2321,7 +2321,7 @@ PyDoc_STRVAR(builtin_sorted__doc__,
"reverse flag can be set to request the result in descending order.");

#define BUILTIN_SORTED_METHODDEF \
{"sorted", (PyCFunction)(void(*)(void))builtin_sorted, METH_FASTCALL | METH_KEYWORDS, builtin_sorted__doc__},
{"sorted", _PyCFunction_CAST(builtin_sorted), METH_FASTCALL | METH_KEYWORDS, builtin_sorted__doc__},

static PyObject *
builtin_sorted(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
Expand Down Expand Up @@ -2839,8 +2839,8 @@ zip_setstate(zipobject *lz, PyObject *state)
}

static PyMethodDef zip_methods[] = {
{"__reduce__", (PyCFunction)zip_reduce, METH_NOARGS, reduce_doc},
{"__setstate__", (PyCFunction)zip_setstate, METH_O, setstate_doc},
{"__reduce__", _PyCFunction_CAST(zip_reduce), METH_NOARGS, reduce_doc},
{"__setstate__", _PyCFunction_CAST(zip_setstate), METH_O, setstate_doc},
{NULL} /* sentinel */
};

Expand Down Expand Up @@ -2904,25 +2904,25 @@ PyTypeObject PyZip_Type = {


static PyMethodDef builtin_methods[] = {
{"__build_class__", (PyCFunction)(void(*)(void))builtin___build_class__,
{"__build_class__", _PyCFunction_CAST(builtin___build_class__),
METH_FASTCALL | METH_KEYWORDS, build_class_doc},
BUILTIN___IMPORT___METHODDEF
BUILTIN_ABS_METHODDEF
BUILTIN_ALL_METHODDEF
BUILTIN_ANY_METHODDEF
BUILTIN_ASCII_METHODDEF
BUILTIN_BIN_METHODDEF
{"breakpoint", (PyCFunction)(void(*)(void))builtin_breakpoint, METH_FASTCALL | METH_KEYWORDS, breakpoint_doc},
{"breakpoint", _PyCFunction_CAST(builtin_breakpoint), METH_FASTCALL | METH_KEYWORDS, breakpoint_doc},
BUILTIN_CALLABLE_METHODDEF
BUILTIN_CHR_METHODDEF
BUILTIN_COMPILE_METHODDEF
BUILTIN_DELATTR_METHODDEF
{"dir", builtin_dir, METH_VARARGS, dir_doc},
{"dir", builtin_dir, METH_VARARGS, dir_doc},
BUILTIN_DIVMOD_METHODDEF
BUILTIN_EVAL_METHODDEF
BUILTIN_EXEC_METHODDEF
BUILTIN_FORMAT_METHODDEF
{"getattr", (PyCFunction)(void(*)(void))builtin_getattr, METH_FASTCALL, getattr_doc},
{"getattr", _PyCFunction_CAST(builtin_getattr), METH_FASTCALL, getattr_doc},
BUILTIN_GLOBALS_METHODDEF
BUILTIN_HASATTR_METHODDEF
BUILTIN_HASH_METHODDEF
Expand All @@ -2931,13 +2931,13 @@ static PyMethodDef builtin_methods[] = {
BUILTIN_INPUT_METHODDEF
BUILTIN_ISINSTANCE_METHODDEF
BUILTIN_ISSUBCLASS_METHODDEF
{"iter", (PyCFunction)(void(*)(void))builtin_iter, METH_FASTCALL, iter_doc},
{"iter", _PyCFunction_CAST(builtin_iter), METH_FASTCALL, iter_doc},
BUILTIN_AITER_METHODDEF
BUILTIN_LEN_METHODDEF
BUILTIN_LOCALS_METHODDEF
{"max", (PyCFunction)(void(*)(void))builtin_max, METH_VARARGS | METH_KEYWORDS, max_doc},
{"min", (PyCFunction)(void(*)(void))builtin_min, METH_VARARGS | METH_KEYWORDS, min_doc},
{"next", (PyCFunction)(void(*)(void))builtin_next, METH_FASTCALL, next_doc},
{"max", _PyCFunction_CAST(builtin_max), METH_VARARGS | METH_KEYWORDS, max_doc},
{"min", _PyCFunction_CAST(builtin_min), METH_VARARGS | METH_KEYWORDS, min_doc},
{"next", _PyCFunction_CAST(builtin_next), METH_FASTCALL, next_doc},
BUILTIN_ANEXT_METHODDEF
BUILTIN_OCT_METHODDEF
BUILTIN_ORD_METHODDEF
Expand Down
2 changes: 1 addition & 1 deletion Python/context.c
Original file line number Diff line number Diff line change
Expand Up @@ -685,7 +685,7 @@ static PyMethodDef PyContext_methods[] = {
_CONTEXTVARS_CONTEXT_KEYS_METHODDEF
_CONTEXTVARS_CONTEXT_VALUES_METHODDEF
_CONTEXTVARS_CONTEXT_COPY_METHODDEF
{"run", (PyCFunction)(void(*)(void))context_run, METH_FASTCALL | METH_KEYWORDS, NULL},
{"run", _PyCFunction_CAST(context_run), METH_FASTCALL | METH_KEYWORDS, NULL},
{NULL, NULL}
};

Expand Down
18 changes: 9 additions & 9 deletions Python/hamt.c
Original file line number Diff line number Diff line change
Expand Up @@ -2845,29 +2845,29 @@ hamt_py_values(PyHamtObject *self, PyObject *args)
}

static PyObject *
hamt_py_keys(PyHamtObject *self, PyObject *args)
hamt_py_keys(PyHamtObject *self, PyObject *Py_UNUSED(args))
{
return _PyHamt_NewIterKeys(self);
}

#ifdef Py_DEBUG
static PyObject *
hamt_py_dump(PyHamtObject *self, PyObject *args)
hamt_py_dump(PyHamtObject *self, PyObject *Py_UNUSED(args))
{
return hamt_dump(self);
}
#endif


static PyMethodDef PyHamt_methods[] = {
{"set", (PyCFunction)hamt_py_set, METH_VARARGS, NULL},
{"get", (PyCFunction)hamt_py_get, METH_VARARGS, NULL},
{"delete", (PyCFunction)hamt_py_delete, METH_O, NULL},
{"items", (PyCFunction)hamt_py_items, METH_NOARGS, NULL},
{"keys", (PyCFunction)hamt_py_keys, METH_NOARGS, NULL},
{"values", (PyCFunction)hamt_py_values, METH_NOARGS, NULL},
{"set", _PyCFunction_CAST(hamt_py_set), METH_VARARGS, NULL},
{"get", _PyCFunction_CAST(hamt_py_get), METH_VARARGS, NULL},
{"delete", _PyCFunction_CAST(hamt_py_delete), METH_O, NULL},
{"items", _PyCFunction_CAST(hamt_py_items), METH_NOARGS, NULL},
{"keys", _PyCFunction_CAST(hamt_py_keys), METH_NOARGS, NULL},
{"values", _PyCFunction_CAST(hamt_py_values), METH_NOARGS, NULL},
#ifdef Py_DEBUG
{"__dump__", (PyCFunction)hamt_py_dump, METH_NOARGS, NULL},
{"__dump__", _PyCFunction_CAST(hamt_py_dump), METH_NOARGS, NULL},
#endif
{NULL, NULL}
};
Expand Down
18 changes: 9 additions & 9 deletions Python/sysmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1929,8 +1929,8 @@ sys_getandroidapilevel_impl(PyObject *module)
static PyMethodDef sys_methods[] = {
/* Might as well keep this in alphabetic order */
SYS_ADDAUDITHOOK_METHODDEF
{"audit", (PyCFunction)(void(*)(void))sys_audit, METH_FASTCALL, audit_doc },
{"breakpointhook", (PyCFunction)(void(*)(void))sys_breakpointhook,
{"audit", _PyCFunction_CAST(sys_audit), METH_FASTCALL, audit_doc },
{"breakpointhook", _PyCFunction_CAST(sys_breakpointhook),
METH_FASTCALL | METH_KEYWORDS, breakpointhook_doc},
SYS__CLEAR_TYPE_CACHE_METHODDEF
SYS__CURRENT_FRAMES_METHODDEF
Expand All @@ -1944,18 +1944,18 @@ static PyMethodDef sys_methods[] = {
SYS_GETDLOPENFLAGS_METHODDEF
SYS_GETALLOCATEDBLOCKS_METHODDEF
#ifdef Py_STATS
{"getdxp", _Py_GetDXProfile, METH_VARARGS},
{"getdxp", _Py_GetDXProfile, METH_VARARGS},
#endif
SYS_GETFILESYSTEMENCODING_METHODDEF
SYS_GETFILESYSTEMENCODEERRORS_METHODDEF
SYS__GETQUICKENEDCOUNT_METHODDEF
#ifdef Py_TRACE_REFS
{"getobjects", _Py_GetObjects, METH_VARARGS},
{"getobjects", _Py_GetObjects, METH_VARARGS},
#endif
SYS_GETTOTALREFCOUNT_METHODDEF
SYS_GETREFCOUNT_METHODDEF
SYS_GETRECURSIONLIMIT_METHODDEF
{"getsizeof", (PyCFunction)(void(*)(void))sys_getsizeof,
{"getsizeof", _PyCFunction_CAST(sys_getsizeof),
METH_VARARGS | METH_KEYWORDS, getsizeof_doc},
SYS__GETFRAME_METHODDEF
SYS_GETWINDOWSVERSION_METHODDEF
Expand All @@ -1966,21 +1966,21 @@ static PyMethodDef sys_methods[] = {
SYS_SETSWITCHINTERVAL_METHODDEF
SYS_GETSWITCHINTERVAL_METHODDEF
SYS_SETDLOPENFLAGS_METHODDEF
{"setprofile", sys_setprofile, METH_O, setprofile_doc},
{"setprofile", sys_setprofile, METH_O, setprofile_doc},
SYS_GETPROFILE_METHODDEF
SYS_SETRECURSIONLIMIT_METHODDEF
{"settrace", sys_settrace, METH_O, settrace_doc},
{"settrace", sys_settrace, METH_O, settrace_doc},
SYS_GETTRACE_METHODDEF
SYS_CALL_TRACING_METHODDEF
SYS__DEBUGMALLOCSTATS_METHODDEF
SYS_SET_COROUTINE_ORIGIN_TRACKING_DEPTH_METHODDEF
SYS_GET_COROUTINE_ORIGIN_TRACKING_DEPTH_METHODDEF
{"set_asyncgen_hooks", (PyCFunction)(void(*)(void))sys_set_asyncgen_hooks,
{"set_asyncgen_hooks", _PyCFunction_CAST(sys_set_asyncgen_hooks),
METH_VARARGS | METH_KEYWORDS, set_asyncgen_hooks_doc},
SYS_GET_ASYNCGEN_HOOKS_METHODDEF
SYS_GETANDROIDAPILEVEL_METHODDEF
SYS_UNRAISABLEHOOK_METHODDEF
{NULL, NULL} /* sentinel */
{NULL, NULL} // sentinel
};


Expand Down
2 changes: 1 addition & 1 deletion Python/traceback.c
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ tb_next_set(PyTracebackObject *self, PyObject *new_next, void *Py_UNUSED(_))


static PyMethodDef tb_methods[] = {
{"__dir__", (PyCFunction)tb_dir, METH_NOARGS},
{"__dir__", _PyCFunction_CAST(tb_dir), METH_NOARGS},
{NULL, NULL, 0, NULL},
};

Expand Down