From 027aa211461ec07c29acd8846fcb5393c83e98da Mon Sep 17 00:00:00 2001 From: Jonas Rembser Date: Mon, 24 Nov 2025 19:12:09 +0100 Subject: [PATCH] [CPyCppyy] Correctly handle static method calls via derived instance The mechanism to strip away the "self" argument from the call to a static method checks the type of "self", matching the exact type. However, it should also remove the "self" if it is an instance of a derived type, because in C++ it is also possible to call static methods via instances of derived types. A unit test was also added. Closes #20463. --- .../cppyy/CPyCppyy/src/CPPClassMethod.cxx | 4 +-- .../pyroot/cppyy/cppyy/test/test_overloads.py | 28 +++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/bindings/pyroot/cppyy/CPyCppyy/src/CPPClassMethod.cxx b/bindings/pyroot/cppyy/CPyCppyy/src/CPPClassMethod.cxx index 1ff532d40d659..f55e50f7a3e1a 100644 --- a/bindings/pyroot/cppyy/CPyCppyy/src/CPPClassMethod.cxx +++ b/bindings/pyroot/cppyy/CPyCppyy/src/CPPClassMethod.cxx @@ -34,8 +34,8 @@ PyObject* CPyCppyy::CPPClassMethod::Call(CPPInstance*& int nargs = (int)CPyCppyy_PyArgs_GET_SIZE(args, nargsf); if ((!self || (PyObject*)self == Py_None) && nargs) { PyObject* arg0 = CPyCppyy_PyArgs_GET_ITEM(args, 0); - if ((CPPInstance_Check(arg0) && ((CPPInstance*)arg0)->ObjectIsA() == GetScope()) && \ - (fArgsRequired <= nargs-1)) { + if (CPPInstance_Check(arg0) && fArgsRequired <= nargs - 1 && + Cppyy::IsSubtype(reinterpret_cast(arg0)->ObjectIsA(), GetScope())) { args += 1; // drops first argument nargsf -= 1; } diff --git a/bindings/pyroot/cppyy/cppyy/test/test_overloads.py b/bindings/pyroot/cppyy/cppyy/test/test_overloads.py index 252d296f3e524..30e563ba593fb 100644 --- a/bindings/pyroot/cppyy/cppyy/test/test_overloads.py +++ b/bindings/pyroot/cppyy/cppyy/test/test_overloads.py @@ -377,5 +377,33 @@ def test12_explicit_constructor_in_implicit_conversion(self): assert cppyy.gbl.test12_foo(1) == cppyy.gbl.call_test12_foo() assert cppyy.gbl.test12_bar(1) == cppyy.gbl.call_test12_bar() + + def test13_static_call_from_derived_instance(self): + """Test calling a static member function via a derived instance.""" + + import cppyy + + cppyy.cppdef(""" + class Base { + public: + static int StaticMethod() { + return 42; + } + }; + + class Derived : public Base { + }; + """) + + d = cppyy.gbl.Derived() + + # Call static method through base class directly + result_direct = cppyy.gbl.Base.StaticMethod() + + # Call static method through instance + result_instance = d.StaticMethod() + + assert result_instance == result_direct + if __name__ == "__main__": exit(pytest.main(args=['-sv', '-ra', __file__]))