[Python-checkins] gh-92898: Enhance _testcppext test on cast to PyObject* (GH-93111)

miss-islington webhook-mailer at python.org
Thu May 26 19:39:49 EDT 2022


https://github.com/python/cpython/commit/9303a5ac30c3fd4f04881d432b10be894959cfbd
commit: 9303a5ac30c3fd4f04881d432b10be894959cfbd
branch: 3.11
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: miss-islington <31488909+miss-islington at users.noreply.github.com>
date: 2022-05-26T16:39:28-07:00
summary:

gh-92898: Enhance _testcppext test on cast to PyObject* (GH-93111)


* Add StrongRef class.
* Rename and reformat functions of the _Py_CAST() implementation.
(cherry picked from commit 20d30ba2ccf9182e4f08db112f428c909148a40b)

Co-authored-by: Victor Stinner <vstinner at python.org>

files:
A Misc/NEWS.d/next/C API/2022-05-23-15-22-18.gh-issue-92898.Qjc9d3.rst
M Include/pyport.h
M Lib/test/_testcppext.cpp

diff --git a/Include/pyport.h b/Include/pyport.h
index 086ed4204e4ff..5a9adf162bfd9 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<type>(expr)
-
 extern "C++" {
-namespace {
-template <typename type, typename expr_type>
-inline type _Py_reinterpret_cast_impl(expr_type *expr) {
-  return reinterpret_cast<type>(expr);
-}
-
-template <typename type, typename expr_type>
-inline type _Py_reinterpret_cast_impl(expr_type const *expr) {
-  return reinterpret_cast<type>(const_cast<expr_type *>(expr));
-}
-
-template <typename type, typename expr_type>
-inline type _Py_reinterpret_cast_impl(expr_type &expr) {
-  return static_cast<type>(expr);
-}
-
-template <typename type, typename expr_type>
-inline type _Py_reinterpret_cast_impl(expr_type const &expr) {
-  return static_cast<type>(const_cast<expr_type &>(expr));
-}
-} // namespace
+    namespace {
+        template <typename type, typename expr_type>
+            inline type _Py_CAST_impl(expr_type *expr) {
+                return reinterpret_cast<type>(expr);
+            }
+
+        template <typename type, typename expr_type>
+            inline type _Py_CAST_impl(expr_type const *expr) {
+                return reinterpret_cast<type>(const_cast<expr_type *>(expr));
+            }
+
+        template <typename type, typename expr_type>
+            inline type _Py_CAST_impl(expr_type &expr) {
+                return static_cast<type>(expr);
+            }
+
+        template <typename type, typename expr_type>
+            inline type _Py_CAST_impl(expr_type const &expr) {
+                return static_cast<type>(const_cast<expr_type &>(expr));
+            }
+    }
 }
-#  define _Py_CAST(type, expr) _Py_reinterpret_cast_impl<type>(expr)
+#  define _Py_CAST(type, expr) _Py_CAST_impl<type>(expr)
 
 #else
 #  define _Py_STATIC_CAST(type, expr) ((type)(expr))
diff --git a/Lib/test/_testcppext.cpp b/Lib/test/_testcppext.cpp
index f6049eedd0004..eade7ccdaaff7 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 0000000000000..01eca1db1f18c
--- /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.



More information about the Python-checkins mailing list