diff --git a/include/pybind11/pybind11.h b/include/pybind11/pybind11.h index cdf5f1a7d..c1bc6732f 100644 --- a/include/pybind11/pybind11.h +++ b/include/pybind11/pybind11.h @@ -318,6 +318,15 @@ protected: m_ptr = rec->sibling.ptr(); inc_ref(); chain_start = chain; + if (chain->is_method != rec->is_method) + pybind11_fail("overloading a method with both static and instance methods is not supported; " + #if defined(NDEBUG) + "compile in debug mode for more details" + #else + "error while attempting to bind " + std::string(rec->is_method ? "instance" : "static") + " method " + + std::string(pybind11::str(rec->scope.attr("__name__"))) + "." + std::string(rec->name) + signature + #endif + ); while (chain->next) chain = chain->next; chain->next = rec; diff --git a/tests/test_methods_and_attributes.cpp b/tests/test_methods_and_attributes.cpp index 8ce25b64a..e11bdf222 100644 --- a/tests/test_methods_and_attributes.cpp +++ b/tests/test_methods_and_attributes.cpp @@ -59,6 +59,8 @@ public: py::str overloaded(int, int) const { return "(int, int) const"; } py::str overloaded(float, float) const { return "(float, float) const"; } + static py::str overloaded() { return "static"; } + int value = 0; }; @@ -206,6 +208,17 @@ test_initializer methods_and_attributes([](py::module &m) { .def("overloaded_const", static_cast(&ExampleMandA::overloaded)) .def("overloaded_const", static_cast(&ExampleMandA::overloaded)) #endif + // Raise error if trying to mix static/non-static overloads on the same name: + .def_static("add_mixed_overloads1", []() { + auto emna = py::reinterpret_borrow>(py::module::import("pybind11_tests").attr("ExampleMandA")); + emna.def ("overload_mixed1", static_cast(&ExampleMandA::overloaded)) + .def_static("overload_mixed1", static_cast(&ExampleMandA::overloaded)); + }) + .def_static("add_mixed_overloads2", []() { + auto emna = py::reinterpret_borrow>(py::module::import("pybind11_tests").attr("ExampleMandA")); + emna.def_static("overload_mixed2", static_cast(&ExampleMandA::overloaded)) + .def ("overload_mixed2", static_cast(&ExampleMandA::overloaded)); + }) .def("__str__", &ExampleMandA::toString) .def_readwrite("value", &ExampleMandA::value); diff --git a/tests/test_methods_and_attributes.py b/tests/test_methods_and_attributes.py index 18bf8d9b3..0eaef9ce7 100644 --- a/tests/test_methods_and_attributes.py +++ b/tests/test_methods_and_attributes.py @@ -148,6 +148,28 @@ def test_metaclass_override(): assert isinstance(MetaclassOverride.__dict__["readonly"], int) +def test_no_mixed_overloads(): + from pybind11_tests import debug_enabled + + with pytest.raises(RuntimeError) as excinfo: + ExampleMandA.add_mixed_overloads1() + assert (str(excinfo.value) == + "overloading a method with both static and instance methods is not supported; " + + ("compile in debug mode for more details" if not debug_enabled else + "error while attempting to bind static method ExampleMandA.overload_mixed1" + "() -> str") + ) + + with pytest.raises(RuntimeError) as excinfo: + ExampleMandA.add_mixed_overloads2() + assert (str(excinfo.value) == + "overloading a method with both static and instance methods is not supported; " + + ("compile in debug mode for more details" if not debug_enabled else + "error while attempting to bind instance method ExampleMandA.overload_mixed2" + "(self: pybind11_tests.ExampleMandA, arg0: int, arg1: int) -> str") + ) + + @pytest.mark.parametrize("access", ["ro", "rw", "static_ro", "static_rw"]) def test_property_return_value_policies(access): from pybind11_tests import TestPropRVP