Skip to content

Commit da104a9

Browse files
authored
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 (62311eb). 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_<type_, options...>::def [with type_=test_class::pr4220_tripped_over_this::Empty0, options=<>]" matches the argument list: function template "pybind11::class_<test_class::pr4220_tripped_over_this::Empty0> &pybind11::class_<type_, options...>::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_<test_class::pr4220_tripped_over_this::Empty0> &pybind11::class_<type_, options...>::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], <unknown-type>) object type is: pybind11::class_<test_class::pr4220_tripped_over_this::Empty0> 1 error detected in the compilation of "/mounted_pybind11/tests/test_class.cpp". ```
1 parent 7c6f2f8 commit da104a9

File tree

4 files changed

+32
-19
lines changed

4 files changed

+32
-19
lines changed

include/pybind11/operators.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ struct op_impl {};
8484
/// Operator implementation generator
8585
template <op_id id, op_type ot, typename L, typename R>
8686
struct op_ {
87+
static constexpr bool op_enable_if_hook = true;
8788
template <typename Class, typename... Extra>
8889
void execute(Class &cl, const Extra &...extra) const {
8990
using Base = typename Class::type;

include/pybind11/pybind11.h

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1578,34 +1578,17 @@ class class_ : public detail::generic_type {
15781578
return *this;
15791579
}
15801580

1581-
// Nvidia's NVCC is broken between 11.4.0 and 11.8.0
1582-
// https://github.com/pybind/pybind11/issues/4193
1583-
#if defined(__CUDACC__) && (__CUDACC_VER_MAJOR__ == 11) && (__CUDACC_VER_MINOR__ >= 4) \
1584-
&& (__CUDACC_VER_MINOR__ <= 8)
1585-
template <typename T, typename... Extra>
1581+
template <typename T, typename... Extra, detail::enable_if_t<T::op_enable_if_hook, int> = 0>
15861582
class_ &def(const T &op, const Extra &...extra) {
15871583
op.execute(*this, extra...);
15881584
return *this;
15891585
}
15901586

1591-
template <typename T, typename... Extra>
1587+
template <typename T, typename... Extra, detail::enable_if_t<T::op_enable_if_hook, int> = 0>
15921588
class_ &def_cast(const T &op, const Extra &...extra) {
15931589
op.execute_cast(*this, extra...);
15941590
return *this;
15951591
}
1596-
#else
1597-
template <detail::op_id id, detail::op_type ot, typename L, typename R, typename... Extra>
1598-
class_ &def(const detail::op_<id, ot, L, R> &op, const Extra &...extra) {
1599-
op.execute(*this, extra...);
1600-
return *this;
1601-
}
1602-
1603-
template <detail::op_id id, detail::op_type ot, typename L, typename R, typename... Extra>
1604-
class_ &def_cast(const detail::op_<id, ot, L, R> &op, const Extra &...extra) {
1605-
op.execute_cast(*this, extra...);
1606-
return *this;
1607-
}
1608-
#endif
16091592

16101593
template <typename... Args, typename... Extra>
16111594
class_ &def(const detail::initimpl::constructor<Args...> &init, const Extra &...extra) {

tests/test_class.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,26 @@ struct NoBraceInitialization {
3636
std::vector<int> vec;
3737
};
3838

39+
namespace test_class {
40+
namespace pr4220_tripped_over_this { // PR #4227
41+
42+
template <int>
43+
struct SoEmpty {};
44+
45+
template <typename T>
46+
std::string get_msg(const T &) {
47+
return "This is really only meant to exercise successful compilation.";
48+
}
49+
50+
using Empty0 = SoEmpty<0x0>;
51+
52+
void bind_empty0(py::module_ &m) {
53+
py::class_<Empty0>(m, "Empty0").def(py::init<>()).def("get_msg", get_msg<Empty0>);
54+
}
55+
56+
} // namespace pr4220_tripped_over_this
57+
} // namespace test_class
58+
3959
TEST_SUBMODULE(class_, m) {
4060
// test_instance
4161
struct NoConstructor {
@@ -517,6 +537,8 @@ TEST_SUBMODULE(class_, m) {
517537
py::class_<OtherDuplicateNested>(gt, "OtherDuplicateNested");
518538
py::class_<OtherDuplicateNested>(gt, "YetAnotherDuplicateNested");
519539
});
540+
541+
test_class::pr4220_tripped_over_this::bind_empty0(m);
520542
}
521543

522544
template <int N>

tests/test_class.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,3 +469,10 @@ class ClassScope:
469469
m.register_duplicate_nested_class_type(ClassScope)
470470
expected = 'generic_type: type "YetAnotherDuplicateNested" is already registered!'
471471
assert str(exc_info.value) == expected
472+
473+
474+
def test_pr4220_tripped_over_this():
475+
assert (
476+
m.Empty0().get_msg()
477+
== "This is really only meant to exercise successful compilation."
478+
)

0 commit comments

Comments
 (0)