diff --git a/Include/pyport.h b/Include/pyport.h index 8e6763789115ef..6ea2bba232b3dd 100644 --- a/Include/pyport.h +++ b/Include/pyport.h @@ -22,35 +22,33 @@ // _Py_CAST(PyObject*, op) can convert a "const PyObject*" to // "PyObject*". // -// The type argument must not be constant. For example, in C++, -// _Py_CAST(const PyObject*, expr) fails with a compiler error. +// The type argument must not be a constant type. #ifdef __cplusplus # define _Py_STATIC_CAST(type, expr) static_cast(expr) - extern "C++" { -namespace { -template -inline type _Py_reinterpret_cast_impl(expr_type *expr) { - return reinterpret_cast(expr); -} - -template -inline type _Py_reinterpret_cast_impl(expr_type const *expr) { - return reinterpret_cast(const_cast(expr)); -} - -template -inline type _Py_reinterpret_cast_impl(expr_type &expr) { - return static_cast(expr); -} - -template -inline type _Py_reinterpret_cast_impl(expr_type const &expr) { - return static_cast(const_cast(expr)); -} -} // namespace + namespace { + template + inline type _Py_CAST_impl(expr_type *expr) { + return reinterpret_cast(expr); + } + + template + inline type _Py_CAST_impl(expr_type const *expr) { + return reinterpret_cast(const_cast(expr)); + } + + template + inline type _Py_CAST_impl(expr_type &expr) { + return static_cast(expr); + } + + template + inline type _Py_CAST_impl(expr_type const &expr) { + return static_cast(const_cast(expr)); + } + } } -# define _Py_CAST(type, expr) _Py_reinterpret_cast_impl(expr) +# define _Py_CAST(type, expr) _Py_CAST_impl(expr) #else # define _Py_STATIC_CAST(type, expr) ((type)(expr)) diff --git a/Lib/test/_testcppext.cpp b/Lib/test/_testcppext.cpp index f6049eedd00048..eade7ccdaaff7a 100644 --- a/Lib/test/_testcppext.cpp +++ b/Lib/test/_testcppext.cpp @@ -23,6 +23,26 @@ _testcppext_add(PyObject *Py_UNUSED(module), PyObject *args) } +// Class to test operator casting an object to PyObject* +class StrongRef +{ +public: + StrongRef(PyObject *obj) : m_obj(obj) { + Py_INCREF(this->m_obj); + } + + ~StrongRef() { + Py_DECREF(this->m_obj); + } + + // Cast to PyObject*: get a borrowed reference + inline operator PyObject*() const { return this->m_obj; } + +private: + PyObject *m_obj; // Strong reference +}; + + static PyObject * test_api_casts(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) { @@ -30,6 +50,8 @@ test_api_casts(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) if (obj == nullptr) { return nullptr; } + Py_ssize_t refcnt = Py_REFCNT(obj); + assert(refcnt >= 1); // gh-92138: For backward compatibility, functions of Python C API accepts // "const PyObject*". Check that using it does not emit C++ compiler @@ -38,22 +60,20 @@ test_api_casts(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) Py_INCREF(const_obj); Py_DECREF(const_obj); PyTypeObject *type = Py_TYPE(const_obj); - assert(Py_REFCNT(const_obj) >= 1); - - struct PyObjectProxy { - PyObject* obj; - operator PyObject *() { return obj; } - } proxy_obj = { obj }; - Py_INCREF(proxy_obj); - Py_DECREF(proxy_obj); - assert(Py_REFCNT(proxy_obj) >= 1); - - + assert(Py_REFCNT(const_obj) == refcnt); assert(type == &PyTuple_Type); assert(PyTuple_GET_SIZE(const_obj) == 2); PyObject *one = PyTuple_GET_ITEM(const_obj, 0); assert(PyLong_AsLong(one) == 1); + // gh-92898: StrongRef doesn't inherit from PyObject but has an operator to + // cast to PyObject*. + StrongRef strong_ref(obj); + assert(Py_TYPE(strong_ref) == &PyTuple_Type); + assert(Py_REFCNT(strong_ref) == (refcnt + 1)); + Py_INCREF(strong_ref); + Py_DECREF(strong_ref); + Py_DECREF(obj); Py_RETURN_NONE; } diff --git a/Misc/NEWS.d/next/C API/2022-05-23-15-22-18.gh-issue-92898.Qjc9d3.rst b/Misc/NEWS.d/next/C API/2022-05-23-15-22-18.gh-issue-92898.Qjc9d3.rst new file mode 100644 index 00000000000000..01eca1db1f18c9 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2022-05-23-15-22-18.gh-issue-92898.Qjc9d3.rst @@ -0,0 +1,2 @@ +Fix C++ compiler warnings when casting function arguments to ``PyObject*``. +Patch by Serge Guelton.