Skip to content

Commit d355f2f

Browse files
committed
Don't allow mixed static/non-static overloads
We currently fail at runtime when trying to call a method that is overloaded with both static and non-static methods. This is something python won't allow: the object is either a function or an instance, and can't be both.
1 parent 90bac96 commit d355f2f

File tree

3 files changed

+44
-0
lines changed

3 files changed

+44
-0
lines changed

include/pybind11/pybind11.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,15 @@ class cpp_function : public function {
318318
m_ptr = rec->sibling.ptr();
319319
inc_ref();
320320
chain_start = chain;
321+
if (chain->is_method != rec->is_method)
322+
pybind11_fail("overloading a method with both static and instance methods is not supported; "
323+
#if defined(NDEBUG)
324+
"compile in debug mode for more details"
325+
#else
326+
"error while attempting to bind " + std::string(rec->is_method ? "instance" : "static") + " method " +
327+
std::string(pybind11::str(rec->scope.attr("__name__"))) + "." + std::string(rec->name) + signature
328+
#endif
329+
);
321330
while (chain->next)
322331
chain = chain->next;
323332
chain->next = rec;

tests/test_methods_and_attributes.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ class ExampleMandA {
5959
py::str overloaded(int, int) const { return "(int, int) const"; }
6060
py::str overloaded(float, float) const { return "(float, float) const"; }
6161

62+
static py::str overloaded() { return "static"; }
63+
6264
int value = 0;
6365
};
6466

@@ -206,6 +208,17 @@ test_initializer methods_and_attributes([](py::module &m) {
206208
.def("overloaded_const", static_cast<py::str (ExampleMandA::*)(int, int) const>(&ExampleMandA::overloaded))
207209
.def("overloaded_const", static_cast<py::str (ExampleMandA::*)(float, float) const>(&ExampleMandA::overloaded))
208210
#endif
211+
// Raise error if trying to mix static/non-static overloads on the same name:
212+
.def_static("add_mixed_overloads1", []() {
213+
auto emna = py::reinterpret_borrow<py::class_<ExampleMandA>>(py::module::import("pybind11_tests").attr("ExampleMandA"));
214+
emna.def ("overload_mixed1", static_cast<py::str (ExampleMandA::*)(int, int)>(&ExampleMandA::overloaded))
215+
.def_static("overload_mixed1", static_cast<py::str ( *)( )>(&ExampleMandA::overloaded));
216+
})
217+
.def_static("add_mixed_overloads2", []() {
218+
auto emna = py::reinterpret_borrow<py::class_<ExampleMandA>>(py::module::import("pybind11_tests").attr("ExampleMandA"));
219+
emna.def_static("overload_mixed2", static_cast<py::str ( *)( )>(&ExampleMandA::overloaded))
220+
.def ("overload_mixed2", static_cast<py::str (ExampleMandA::*)(int, int)>(&ExampleMandA::overloaded));
221+
})
209222
.def("__str__", &ExampleMandA::toString)
210223
.def_readwrite("value", &ExampleMandA::value);
211224

tests/test_methods_and_attributes.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,28 @@ def test_metaclass_override():
148148
assert isinstance(MetaclassOverride.__dict__["readonly"], int)
149149

150150

151+
def test_no_mixed_overloads():
152+
from pybind11_tests import debug_enabled
153+
154+
with pytest.raises(RuntimeError) as excinfo:
155+
ExampleMandA.add_mixed_overloads1()
156+
assert (str(excinfo.value) ==
157+
"overloading a method with both static and instance methods is not supported; " +
158+
("compile in debug mode for more details" if not debug_enabled else
159+
"error while attempting to bind static method ExampleMandA.overload_mixed1"
160+
"() -> str")
161+
)
162+
163+
with pytest.raises(RuntimeError) as excinfo:
164+
ExampleMandA.add_mixed_overloads2()
165+
assert (str(excinfo.value) ==
166+
"overloading a method with both static and instance methods is not supported; " +
167+
("compile in debug mode for more details" if not debug_enabled else
168+
"error while attempting to bind instance method ExampleMandA.overload_mixed2"
169+
"(self: pybind11_tests.ExampleMandA, arg0: int, arg1: int) -> str")
170+
)
171+
172+
151173
@pytest.mark.parametrize("access", ["ro", "rw", "static_ro", "static_rw"])
152174
def test_property_return_value_policies(access):
153175
from pybind11_tests import TestPropRVP

0 commit comments

Comments
 (0)