From 9c04c7b0f1210a5f6ca7cc2e975606a1eaf21316 Mon Sep 17 00:00:00 2001 From: Aaron Gokaslan Date: Mon, 19 Sep 2022 12:56:31 -0400 Subject: [PATCH 01/15] chore: Delete copy ctor/assign for GIL RAIIs (#4183) * chore: Delete copy ctor/assign for GIL RAIIs * Fix typo * Delete copy ops for local gil scoped acquire --- include/pybind11/detail/internals.h | 2 ++ include/pybind11/gil.h | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 6ca5e1482..86991955b 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -412,6 +412,8 @@ PYBIND11_NOINLINE internals &get_internals() { // Cannot use py::gil_scoped_acquire here since that constructor calls get_internals. struct gil_scoped_acquire_local { gil_scoped_acquire_local() : state(PyGILState_Ensure()) {} + gil_scoped_acquire_local(const gil_scoped_acquire_local &) = delete; + gil_scoped_acquire_local &operator=(const gil_scoped_acquire_local &) = delete; ~gil_scoped_acquire_local() { PyGILState_Release(state); } const PyGILState_STATE state; } gil; diff --git a/include/pybind11/gil.h b/include/pybind11/gil.h index a0b5de151..1ef5f0a8c 100644 --- a/include/pybind11/gil.h +++ b/include/pybind11/gil.h @@ -80,6 +80,9 @@ public: inc_ref(); } + gil_scoped_acquire(const gil_scoped_acquire &) = delete; + gil_scoped_acquire &operator=(const gil_scoped_acquire &) = delete; + void inc_ref() { ++tstate->gilstate_counter; } PYBIND11_NOINLINE void dec_ref() { @@ -144,6 +147,9 @@ public: } } + gil_scoped_release(const gil_scoped_acquire &) = delete; + gil_scoped_release &operator=(const gil_scoped_acquire &) = delete; + /// This method will disable the PyThreadState_DeleteCurrent call and the /// GIL won't be acquired. This method should be used if the interpreter /// could be shutting down when this is called, as thread deletion is not @@ -178,6 +184,8 @@ class gil_scoped_acquire { public: gil_scoped_acquire() { state = PyGILState_Ensure(); } + gil_scoped_acquire(const gil_scoped_acquire &) = delete; + gil_scoped_acquire &operator=(const gil_scoped_acquire &) = delete; ~gil_scoped_acquire() { PyGILState_Release(state); } void disarm() {} }; @@ -187,6 +195,8 @@ class gil_scoped_release { public: gil_scoped_release() { state = PyEval_SaveThread(); } + gil_scoped_release(const gil_scoped_release &) = delete; + gil_scoped_release &operator=(const gil_scoped_acquire &) = delete; ~gil_scoped_release() { PyEval_RestoreThread(state); } void disarm() {} }; From d02f219fb9b9012ce7b27f34c5ee9feb55da08ec Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 19 Sep 2022 21:11:57 -0400 Subject: [PATCH 02/15] [pre-commit.ci] pre-commit autoupdate (#4189) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.37.3 → v2.38.0](https://github.com/asottile/pyupgrade/compare/v2.37.3...v2.38.0) - [github.com/PyCQA/pylint: v2.15.2 → v2.15.3](https://github.com/PyCQA/pylint/compare/v2.15.2...v2.15.3) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5d794a749..508b3ea6e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -35,7 +35,7 @@ repos: # Upgrade old Python syntax - repo: https://github.com/asottile/pyupgrade - rev: "v2.37.3" + rev: "v2.38.0" hooks: - id: pyupgrade args: [--py36-plus] @@ -110,7 +110,7 @@ repos: # PyLint has native support - not always usable, but works for us - repo: https://github.com/PyCQA/pylint - rev: "v2.15.2" + rev: "v2.15.3" hooks: - id: pylint files: ^pybind11 From 424ac4fe1bde46894a56775e78a702199a03137b Mon Sep 17 00:00:00 2001 From: Jan Iwaszkiewicz Date: Tue, 20 Sep 2022 19:03:57 +0200 Subject: [PATCH 03/15] fix: Windows compiler, missing object initializer (#4188) * Fix for windows compiler, missing object initializer * Removal of if-else macro for MSVC --- include/pybind11/detail/common.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/include/pybind11/detail/common.h b/include/pybind11/detail/common.h index 9e6947daa..6da7ef859 100644 --- a/include/pybind11/detail/common.h +++ b/include/pybind11/detail/common.h @@ -1033,12 +1033,7 @@ PYBIND11_NAMESPACE_END(detail) /// - regular: static_cast(&Class::func) /// - sweet: overload_cast(&Class::func) template -# if (defined(_MSC_VER) && _MSC_VER < 1920) /* MSVC 2017 */ \ - || (defined(__clang__) && __clang_major__ == 5) -static constexpr detail::overload_cast_impl overload_cast = {}; -# else -static constexpr detail::overload_cast_impl overload_cast; -# endif +static constexpr detail::overload_cast_impl overload_cast{}; #endif /// Const member function selector for overload_cast From 95d0e71a652992bcdc0c54844c4c6e5282247c6b Mon Sep 17 00:00:00 2001 From: Aaron Gokaslan Date: Wed, 21 Sep 2022 11:20:07 -0400 Subject: [PATCH 04/15] test C++14 on MSVC (#4191) --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 213c69005..fe57e7ce9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -737,6 +737,9 @@ jobs: args: -DCMAKE_CXX_STANDARD=20 - python: 3.8 args: -DCMAKE_CXX_STANDARD=17 + - python: 3.7 + args: -DCMAKE_CXX_STANDARD=14 + name: "🐍 ${{ matrix.python }} • MSVC 2019 • x86 ${{ matrix.args }}" runs-on: windows-2019 From f743bdf8e61b2dc4a8995a0485111bedd9c2cbb1 Mon Sep 17 00:00:00 2001 From: bogdan-lab <60753036+bogdan-lab@users.noreply.github.com> Date: Wed, 21 Sep 2022 21:50:31 +0300 Subject: [PATCH 05/15] Avoid local_internals destruction (#4192) * Avoid local_internals destruction It allows to avoid possible static deinitialization fiasco. * Add link to relevant google style guide discussion --- include/pybind11/detail/internals.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/include/pybind11/detail/internals.h b/include/pybind11/detail/internals.h index 86991955b..d47084e26 100644 --- a/include/pybind11/detail/internals.h +++ b/include/pybind11/detail/internals.h @@ -514,8 +514,13 @@ struct local_internals { /// Works like `get_internals`, but for things which are locally registered. inline local_internals &get_local_internals() { - static local_internals locals; - return locals; + // Current static can be created in the interpreter finalization routine. If the later will be + // destroyed in another static variable destructor, creation of this static there will cause + // static deinitialization fiasco. In order to avoid it we avoid destruction of the + // local_internals static. One can read more about the problem and current solution here: + // https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables + static auto *locals = new local_internals(); + return *locals; } /// Constructs a std::string with the given arguments, stores it in `internals`, and returns its From 5aa0fad5def3fc4d48f29998b6f65e74bacafd19 Mon Sep 17 00:00:00 2001 From: Aaron Gokaslan Date: Sun, 25 Sep 2022 16:10:57 -0400 Subject: [PATCH 06/15] perf: call reserve method in set and map casters (#4194) * Call reserve method in set and map casters too * Refactor template logic into has_reserve_method * Adjust comment for reviews * Rearrange reserve_maybe to not be underneath macro --- include/pybind11/stl.h | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/include/pybind11/stl.h b/include/pybind11/stl.h index 48031c2a6..8f243502e 100644 --- a/include/pybind11/stl.h +++ b/include/pybind11/stl.h @@ -49,17 +49,31 @@ constexpr forwarded_type forward_like(U &&u) { return std::forward>(std::forward(u)); } +// Checks if a container has a STL style reserve method. +// This will only return true for a `reserve()` with a `void` return. +template +using has_reserve_method = std::is_same().reserve(0)), void>; + template struct set_caster { using type = Type; using key_conv = make_caster; +private: + template ::value, int> = 0> + void reserve_maybe(const anyset &s, Type *) { + value.reserve(s.size()); + } + void reserve_maybe(const anyset &, void *) {} + +public: bool load(handle src, bool convert) { if (!isinstance(src)) { return false; } auto s = reinterpret_borrow(src); value.clear(); + reserve_maybe(s, &value); for (auto entry : s) { key_conv conv; if (!conv.load(entry, convert)) { @@ -94,12 +108,21 @@ struct map_caster { using key_conv = make_caster; using value_conv = make_caster; +private: + template ::value, int> = 0> + void reserve_maybe(const dict &d, Type *) { + value.reserve(d.size()); + } + void reserve_maybe(const dict &, void *) {} + +public: bool load(handle src, bool convert) { if (!isinstance(src)) { return false; } auto d = reinterpret_borrow(src); value.clear(); + reserve_maybe(d, &value); for (auto it : d) { key_conv kconv; value_conv vconv; @@ -160,9 +183,7 @@ struct list_caster { } private: - template < - typename T = Type, - enable_if_t().reserve(0)), void>::value, int> = 0> + template ::value, int> = 0> void reserve_maybe(const sequence &s, Type *) { value.reserve(s.size()); } From da8c730a62a7f9654433f5e2206bd1135a8b57d8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 26 Sep 2022 20:44:19 -0400 Subject: [PATCH 07/15] [pre-commit.ci] pre-commit autoupdate (#4197) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.38.0 → v2.38.2](https://github.com/asottile/pyupgrade/compare/v2.38.0...v2.38.2) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 508b3ea6e..2c317ea7a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -35,7 +35,7 @@ repos: # Upgrade old Python syntax - repo: https://github.com/asottile/pyupgrade - rev: "v2.38.0" + rev: "v2.38.2" hooks: - id: pyupgrade args: [--py36-plus] From c78dfe6964579c69692eb8b5e57cbed26e409f6e Mon Sep 17 00:00:00 2001 From: Aaron Gokaslan Date: Mon, 3 Oct 2022 13:44:09 -0400 Subject: [PATCH 08/15] bugfix: Add error checking to list append and insert (#4208) --- include/pybind11/pytypes.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index f4ba459f2..565df4375 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -2022,14 +2022,20 @@ public: detail::list_iterator end() const { return {*this, PyList_GET_SIZE(m_ptr)}; } template void append(T &&val) /* py-non-const */ { - PyList_Append(m_ptr, detail::object_or_cast(std::forward(val)).ptr()); + if (PyList_Append(m_ptr, detail::object_or_cast(std::forward(val)).ptr()) != 0) { + throw error_already_set(); + } } template ::value, int> = 0> void insert(const IdxType &index, ValType &&val) /* py-non-const */ { - PyList_Insert( - m_ptr, ssize_t_cast(index), detail::object_or_cast(std::forward(val)).ptr()); + if (PyList_Insert(m_ptr, + ssize_t_cast(index), + detail::object_or_cast(std::forward(val)).ptr()) + != 0) { + throw error_already_set(); + } } }; From 600d697648f4eda8c88a6884472bd1d2ec0a68ad Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Oct 2022 21:25:46 -0400 Subject: [PATCH 09/15] [pre-commit.ci] pre-commit autoupdate (#4210) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v0.971 → v0.981](https://github.com/pre-commit/mirrors-mypy/compare/v0.971...v0.981) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2c317ea7a..ad07577eb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -126,7 +126,7 @@ repos: # Check static types with mypy - repo: https://github.com/pre-commit/mirrors-mypy - rev: "v0.971" + rev: "v0.981" hooks: - id: mypy args: [] From 8275b769129816f97c7665f64c88a0e05d8987df Mon Sep 17 00:00:00 2001 From: Henry Schreiner Date: Tue, 4 Oct 2022 14:01:26 -0400 Subject: [PATCH 10/15] ci: update pre-commit schedule (#4212) --- .pre-commit-config.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ad07577eb..742e7a30a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,6 +12,12 @@ # # See https://github.com/pre-commit/pre-commit + +ci: + autoupdate_commit_msg: "chore(deps): update pre-commit hooks" + autofix_commit_msg: "style: pre-commit fixes" + autoupdate_schedule: monthly + # third-party content exclude: ^tools/JoinPaths.cmake$ From 864ed1120c704bf27d6ad5ab40645262d0d1e955 Mon Sep 17 00:00:00 2001 From: Aaron Gokaslan Date: Thu, 6 Oct 2022 16:11:34 -0400 Subject: [PATCH 11/15] chore: steal arg_v.value from copied arg in unpacking_collector (#4219) --- include/pybind11/cast.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/pybind11/cast.h b/include/pybind11/cast.h index a0e32281b..b89e4946a 100644 --- a/include/pybind11/cast.h +++ b/include/pybind11/cast.h @@ -1545,7 +1545,7 @@ private: throw cast_error_unable_to_convert_call_arg(a.name, a.type); #endif } - m_kwargs[a.name] = a.value; + m_kwargs[a.name] = std::move(a.value); } void process(list & /*args_list*/, detail::kwargs_proxy kp) { From 6cb214748d6ec3a61fdc35ee27f9a90d170dd242 Mon Sep 17 00:00:00 2001 From: Axel Huebl Date: Thu, 6 Oct 2022 21:02:57 -0700 Subject: [PATCH 12/15] fix: NVCC 11.4.0 - 11.8.0 host bug workaround (#4220) * Work-Around: NVCC 11.4.0 - 11.8.0 Adds a targeted NVCC work around for limited number of CUDA releases. Fixed in NVCC development. * style: pre-commit fixes * CI: Bump CTK Version 11.2 -> 11.7 Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 4 ++-- include/pybind11/pybind11.h | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fe57e7ce9..99caabf09 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -324,8 +324,8 @@ jobs: # Testing NVCC; forces sources to behave like .cu files cuda: runs-on: ubuntu-latest - name: "🐍 3.8 • CUDA 11.2 • Ubuntu 20.04" - container: nvidia/cuda:11.2.2-devel-ubuntu20.04 + name: "🐍 3.10 • CUDA 11.7 • Ubuntu 22.04" + container: nvidia/cuda:11.7.0-devel-ubuntu22.04 steps: - uses: actions/checkout@v3 diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index c889dc416..3f6e27b75 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1578,6 +1578,22 @@ public: return *this; } +// Nvidia's NVCC is broken between 11.4.0 and 11.8.0 +// https://github.com/pybind/pybind11/issues/4193 +#if defined(__CUDACC__) && (__CUDACC_VER_MAJOR__ == 11) && (__CUDACC_VER_MINOR__ >= 4) \ + && (__CUDACC_VER_MINOR__ <= 8) + template + class_ &def(const T &op, const Extra &...extra) { + op.execute(*this, extra...); + return *this; + } + + template + class_ &def_cast(const T &op, const Extra &...extra) { + op.execute_cast(*this, extra...); + return *this; + } +#else template class_ &def(const detail::op_ &op, const Extra &...extra) { op.execute(*this, extra...); @@ -1589,6 +1605,7 @@ public: op.execute_cast(*this, extra...); return *this; } +#endif template class_ &def(const detail::initimpl::constructor &init, const Extra &...extra) { From 4a4215620920ee659145cd34597e14f0553a73f1 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 7 Oct 2022 09:20:38 -0700 Subject: [PATCH 13/15] test_eigen.py test_nonunit_stride_to_python bug fix (ASAN failure) (#4217) * Disable test triggering ASAN failure (to pin-point where the problem is). * Fix unsafe "block" implementation in test_eigen.cpp * Undo changes (i.e. revert back to master). * Detect "type_caster for Eigen::Ref made a copy." This is achieved without * reaching into internals, * making test_eigen.cpp depend on pybind11/numpy.h. * Add comment pointing to PR, for easy reference. --- tests/test_eigen.cpp | 39 ++++++++++++++++++++++++++++++++++----- tests/test_eigen.py | 15 ++++++++++++--- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/tests/test_eigen.cpp b/tests/test_eigen.cpp index 591dacc62..b0c7bdb48 100644 --- a/tests/test_eigen.cpp +++ b/tests/test_eigen.cpp @@ -197,11 +197,40 @@ TEST_SUBMODULE(eigen, m) { // Return a block of a matrix (gives non-standard strides) m.def("block", - [](const Eigen::Ref &x, - int start_row, - int start_col, - int block_rows, - int block_cols) { return x.block(start_row, start_col, block_rows, block_cols); }); + [m](const py::object &x_obj, + int start_row, + int start_col, + int block_rows, + int block_cols) { + return m.attr("_block")(x_obj, x_obj, start_row, start_col, block_rows, block_cols); + }); + + m.def( + "_block", + [](const py::object &x_obj, + const Eigen::Ref &x, + int start_row, + int start_col, + int block_rows, + int block_cols) { + // See PR #4217 for background. This test is a bit over the top, but might be useful + // as a concrete example to point to when explaining the dangling reference trap. + auto i0 = py::make_tuple(0, 0); + auto x0_orig = x_obj[*i0].cast(); + if (x(0, 0) != x0_orig) { + throw std::runtime_error( + "Something in the type_caster for Eigen::Ref is terribly wrong."); + } + double x0_mod = x0_orig + 1; + x_obj[*i0] = x0_mod; + auto copy_detected = (x(0, 0) != x0_mod); + x_obj[*i0] = x0_orig; + if (copy_detected) { + throw std::runtime_error("type_caster for Eigen::Ref made a copy."); + } + return x.block(start_row, start_col, block_rows, block_cols); + }, + py::keep_alive<0, 1>()); // test_eigen_return_references, test_eigen_keepalive // return value referencing/copying tests: diff --git a/tests/test_eigen.py b/tests/test_eigen.py index 9c6485de3..713432a81 100644 --- a/tests/test_eigen.py +++ b/tests/test_eigen.py @@ -217,15 +217,24 @@ def test_negative_stride_from_python(msg): ) +def test_block_runtime_error_type_caster_eigen_ref_made_a_copy(): + with pytest.raises(RuntimeError) as excinfo: + m.block(ref, 0, 0, 0, 0) + assert str(excinfo.value) == "type_caster for Eigen::Ref made a copy." + + def test_nonunit_stride_to_python(): assert np.all(m.diagonal(ref) == ref.diagonal()) assert np.all(m.diagonal_1(ref) == ref.diagonal(1)) for i in range(-5, 7): assert np.all(m.diagonal_n(ref, i) == ref.diagonal(i)), f"m.diagonal_n({i})" - assert np.all(m.block(ref, 2, 1, 3, 3) == ref[2:5, 1:4]) - assert np.all(m.block(ref, 1, 4, 4, 2) == ref[1:, 4:]) - assert np.all(m.block(ref, 1, 4, 3, 2) == ref[1:4, 4:]) + # Must be order="F", otherwise the type_caster will make a copy and + # m.block() will return a dangling reference (heap-use-after-free). + rof = np.asarray(ref, order="F") + assert np.all(m.block(rof, 2, 1, 3, 3) == rof[2:5, 1:4]) + assert np.all(m.block(rof, 1, 4, 4, 2) == rof[1:, 4:]) + assert np.all(m.block(rof, 1, 4, 3, 2) == rof[1:4, 4:]) def test_eigen_ref_to_python(): From 7c6f2f80a73e330642226138ae3ddaf448a4f672 Mon Sep 17 00:00:00 2001 From: Daniel Galvez Date: Fri, 7 Oct 2022 12:27:54 -0700 Subject: [PATCH 14/15] fix: PyCapsule_GetDestructor is allowed to return a nullptr destructor (#4221) * fix: PyCapsule_GetDestructor is allowed to return a nullptr destructor Previously, this code would error out if the destructor happened to be a nullptr. This is incorrect. nullptrs are allowed for capsule destructors. "It is legal for a capsule to have a NULL destructor. This makes a NULL return code somewhat ambiguous; use PyCapsule_IsValid() or PyErr_Occurred() to disambiguate." See: https://docs.python.org/3/c-api/capsule.html#c.PyCapsule_GetDestructor I noticed this while working on a type caster related to #3858 DLPack happens to allow the destructor not to be defined on a capsule, and I encountered such a case. See: https://github.com/dmlc/dlpack/blob/e2bdd3bee8cb6501558042633fa59144cc8b7f5f/include/dlpack/dlpack.h#L219 * Add test for the fix. * Update tests/test_pytypes.cpp I tried this locally and it works! I never knew that there are cases where `reinterpret_cast` does not work but `static_cast` does. Let's see if all compilers are happy with this. Co-authored-by: Aaron Gokaslan * style: pre-commit fixes Co-authored-by: Ralf W. Grosse-Kunstleve Co-authored-by: Ralf W. Grosse-Kunstleve Co-authored-by: Aaron Gokaslan Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- include/pybind11/pytypes.h | 12 ++++++------ tests/test_pytypes.cpp | 6 ++++++ tests/test_pytypes.py | 11 +++++++++++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/include/pybind11/pytypes.h b/include/pybind11/pytypes.h index 565df4375..29506b7ea 100644 --- a/include/pybind11/pytypes.h +++ b/include/pybind11/pytypes.h @@ -1829,18 +1829,18 @@ public: // guard if destructor called while err indicator is set error_scope error_guard; auto destructor = reinterpret_cast(PyCapsule_GetContext(o)); - if (destructor == nullptr) { - if (PyErr_Occurred()) { - throw error_already_set(); - } - pybind11_fail("Unable to get capsule context"); + if (PyErr_Occurred()) { + throw error_already_set(); } const char *name = get_name_in_error_scope(o); void *ptr = PyCapsule_GetPointer(o, name); if (ptr == nullptr) { throw error_already_set(); } - destructor(ptr); + + if (destructor != nullptr) { + destructor(ptr); + } }); if (!m_ptr || PyCapsule_SetContext(m_ptr, (void *) destructor) != 0) { diff --git a/tests/test_pytypes.cpp b/tests/test_pytypes.cpp index da0dc8f6b..c95ff8230 100644 --- a/tests/test_pytypes.cpp +++ b/tests/test_pytypes.cpp @@ -289,6 +289,12 @@ TEST_SUBMODULE(pytypes, m) { return capsule; }); + m.def("return_capsule_with_explicit_nullptr_dtor", []() { + py::print("creating capsule with explicit nullptr dtor"); + return py::capsule(reinterpret_cast(1234), + static_cast(nullptr)); // PR #4221 + }); + // test_accessors m.def("accessor_api", [](const py::object &o) { auto d = py::dict(); diff --git a/tests/test_pytypes.py b/tests/test_pytypes.py index a34eaa59e..070849fc5 100644 --- a/tests/test_pytypes.py +++ b/tests/test_pytypes.py @@ -299,6 +299,17 @@ def test_capsule(capture): """ ) + with capture: + a = m.return_capsule_with_explicit_nullptr_dtor() + del a + pytest.gc_collect() + assert ( + capture.unordered + == """ + creating capsule with explicit nullptr dtor + """ + ) + def test_accessors(): class SubTestObject: From da104a9efd0357b5c144c67eb641ae0b9e23012d Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Sun, 9 Oct 2022 21:50:35 -0700 Subject: [PATCH 15/15] Reproducer and fix for issue encountered in smart_holder update. (#4228) * Reproducer for issue encountered in smart_holder update. * clang-tidy compatibility (untested). * Add `enable_if_t` to workaround. * Bug fix: Move `PYBIND11_USING_WORKAROUND_FOR_CUDA_11_4_THROUGH_8` determination to detail/common.h So that it actually is defined in pybind11.h * Try using the workaround (which is nicer than the original code) universally. * Reduce reproducer for CUDA 11.7 issue encountered in smart_holder update. This commit tested in isolation on top of current master + first version of reproducer (62311eb431849d135a5db84f6c75ec390f2ede7c). Succeeds with Debian Clang 14.0.6 C++17 (and probably all other compilers). Fails for CUDA 11.7: ``` cd /build/tests && /usr/local/cuda/bin/nvcc -forward-unknown-to-host-compiler -Dpybind11_tests_EXPORTS -I/mounted_pybind11/include -isystem=/usr/include/python3.10 -g --generate-code=arch=compute_52,code=[compute_52,sm_52] -Xcompiler=-fPIC -Xcompiler=-fvisibility=hidden -Werror all-warnings -std=c++17 -MD -MT tests/CMakeFiles/pybind11_tests.dir/test_class.cpp.o -MF CMakeFiles/pybind11_tests.dir/test_class.cpp.o.d -x cu -c /mounted_pybind11/tests/test_class.cpp -o CMakeFiles/pybind11_tests.dir/test_class.cpp.o /mounted_pybind11/tests/test_class.cpp(53): error: more than one instance of overloaded function "pybind11::class_::def [with type_=test_class::pr4220_tripped_over_this::Empty0, options=<>]" matches the argument list: function template "pybind11::class_ &pybind11::class_::def(const char *, Func &&, const Extra &...) [with type_=test_class::pr4220_tripped_over_this::Empty0, options=<>]" /mounted_pybind11/include/pybind11/pybind11.h(1557): here function template "pybind11::class_ &pybind11::class_::def(const T &, const Extra &...) [with type_=test_class::pr4220_tripped_over_this::Empty0, options=<>]" /mounted_pybind11/include/pybind11/pybind11.h(1586): here argument types are: (const char [8], ) object type is: pybind11::class_ 1 error detected in the compilation of "/mounted_pybind11/tests/test_class.cpp". ``` --- include/pybind11/operators.h | 1 + include/pybind11/pybind11.h | 21 ++------------------- tests/test_class.cpp | 22 ++++++++++++++++++++++ tests/test_class.py | 7 +++++++ 4 files changed, 32 insertions(+), 19 deletions(-) diff --git a/include/pybind11/operators.h b/include/pybind11/operators.h index a0c3b78d6..16a88ae17 100644 --- a/include/pybind11/operators.h +++ b/include/pybind11/operators.h @@ -84,6 +84,7 @@ struct op_impl {}; /// Operator implementation generator template struct op_ { + static constexpr bool op_enable_if_hook = true; template void execute(Class &cl, const Extra &...extra) const { using Base = typename Class::type; diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index 3f6e27b75..fb4b7578d 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -1578,34 +1578,17 @@ public: return *this; } -// Nvidia's NVCC is broken between 11.4.0 and 11.8.0 -// https://github.com/pybind/pybind11/issues/4193 -#if defined(__CUDACC__) && (__CUDACC_VER_MAJOR__ == 11) && (__CUDACC_VER_MINOR__ >= 4) \ - && (__CUDACC_VER_MINOR__ <= 8) - template + template = 0> class_ &def(const T &op, const Extra &...extra) { op.execute(*this, extra...); return *this; } - template + template = 0> class_ &def_cast(const T &op, const Extra &...extra) { op.execute_cast(*this, extra...); return *this; } -#else - template - class_ &def(const detail::op_ &op, const Extra &...extra) { - op.execute(*this, extra...); - return *this; - } - - template - class_ &def_cast(const detail::op_ &op, const Extra &...extra) { - op.execute_cast(*this, extra...); - return *this; - } -#endif template class_ &def(const detail::initimpl::constructor &init, const Extra &...extra) { diff --git a/tests/test_class.cpp b/tests/test_class.cpp index c8b8071dc..18c8d358b 100644 --- a/tests/test_class.cpp +++ b/tests/test_class.cpp @@ -36,6 +36,26 @@ struct NoBraceInitialization { std::vector vec; }; +namespace test_class { +namespace pr4220_tripped_over_this { // PR #4227 + +template +struct SoEmpty {}; + +template +std::string get_msg(const T &) { + return "This is really only meant to exercise successful compilation."; +} + +using Empty0 = SoEmpty<0x0>; + +void bind_empty0(py::module_ &m) { + py::class_(m, "Empty0").def(py::init<>()).def("get_msg", get_msg); +} + +} // namespace pr4220_tripped_over_this +} // namespace test_class + TEST_SUBMODULE(class_, m) { // test_instance struct NoConstructor { @@ -517,6 +537,8 @@ TEST_SUBMODULE(class_, m) { py::class_(gt, "OtherDuplicateNested"); py::class_(gt, "YetAnotherDuplicateNested"); }); + + test_class::pr4220_tripped_over_this::bind_empty0(m); } template diff --git a/tests/test_class.py b/tests/test_class.py index ff9196f0f..47ba450fc 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -469,3 +469,10 @@ def test_register_duplicate_class(): m.register_duplicate_nested_class_type(ClassScope) expected = 'generic_type: type "YetAnotherDuplicateNested" is already registered!' assert str(exc_info.value) == expected + + +def test_pr4220_tripped_over_this(): + assert ( + m.Empty0().get_msg() + == "This is really only meant to exercise successful compilation." + )