Bug 31081 - XMethods for C++23 multi-dimensional operator[](idx1, idx2, ...) do not work
Summary: XMethods for C++23 multi-dimensional operator[](idx1, idx2, ...) do not work
Status: NEW
Alias: None
Product: gdb
Classification: Unclassified
Component: c++ (show other bugs)
Version: 13.1
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-11-22 08:45 UTC by George Jöngren
Modified: 2023-11-22 17:50 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed: 2023-11-22 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description George Jöngren 2023-11-22 08:45:30 UTC
(Tried using g++ version 12.1 with GDB version 13.2, the latter which is not available in bug reporting drop-down)

Using XMethods, GDB understands syntax for single index operator[](idx1) making it possible to write
(gdb) p mc[1]
where mc is a class instance. This instead of having to type the very cumbersome (which becomes even more inconvenient in the case of nested containers)
(gdb) p mc.operator[](1)

However, GDB seems not to understand the syntax for the corresponding case with C++23's new multi-dimensional operator[](idx1, idx2, ...). 
Hence, this works
(gdb) p mc.operator[](1,2)
but this does not work
(gdb) p mc[1,2]

This is very unfortunate as there is currently then no convenient syntax in GDB for dealing with multi-dimensional matrices (operator()(idx1, idx2, ...)) does not work either since GDB seems to think that mc(1,2) is a call to a function named "mc", c.f. Bug ID 12213), including for C++23's std::mdspan.

Debugging test.cpp below using the Xmethods file custom_xmehods.py give rise to the following gdb output:

> g++ -g -std=c++23 test.cpp -o test.exe
> gdb ./test.exe
(gdb) source custom_xmethods.py
(gdb) py register_custom_xmethods()
(gdb) b 14
(gdb) run


(gdb) p mc[1]
Single index
1
$1 = 1

(gdb) p mc.operator[](1)
Single index
1
$2 = 1

(gdb) p mc.operator[](1,2)
Two indices
1
2
$3 = 3

(gdb) p mc[1,2]
Single index
2
$4 = 2
# Note how the single index operator[](idx1) is erronously picked.
# If MyClass_subscript_x() is removed as an XMethod, then the output is instead:
(gdb) p mc[1,2]
Could not find operator[].
(gdb) p mc.operator[](1,2)
Two indices
1
2
$1 = 3

(gdb) p mc.elem(1,2)
Two indices
1
2
$5 = 3


//=========================
// test.cpp
//=========================
struct MyClass
{
  int operator[](int idx1) { return idx1; }
  int operator[](int idx1, int idx2) { return idx1 + idx2; }
  int elem(int idx1, int idx2) { return idx1 + idx2; }
};

int
main(int argc,
     char * argv[])
{
  MyClass mc;
  return 0;
}

#=========================
# custom_xmethods.py
#=========================
class MyClassWorker_subscript_x(gdb.xmethod.XMethodWorker):
    def get_arg_types(self):
        return gdb.lookup_type("int")

    def get_result_type(self, obj):
        return gdb.lookup_type("int")

    def __call__(self, obj, idx1):
        print("Single index")
        print(idx1)
        return idx1


class MyClassWorker_subscript_xx(gdb.xmethod.XMethodWorker):
    def get_arg_types(self):
        return (gdb.lookup_type("int"), gdb.lookup_type("int"))

    def get_result_type(self, obj):
        return gdb.lookup_type("int")

    def __call__(self, obj, idx1, idx2):
        print("Two indices")
        print(idx1)
        print(idx2)
        return idx1 + idx2


class MyClass_subscript_x(gdb.xmethod.XMethod):
    def __init__(self):
        gdb.xmethod.XMethod.__init__(self, "operator[]")

    def get_worker(self, method_name):
        if method_name == "operator[]":
            return MyClassWorker_subscript_x()


class MyClass_subscript_xx(gdb.xmethod.XMethod):
    def __init__(self):
        gdb.xmethod.XMethod.__init__(self, "operator[] / elem")

    def get_worker(self, method_name):
        if method_name == "operator[]" or method_name == "elem":
            return MyClassWorker_subscript_xx()


class MyClassMatcher(gdb.xmethod.XMethodMatcher):
    def __init__(self):
        gdb.xmethod.XMethodMatcher.__init__(self, "MyClassMatcher")
        self.methods = [
            MyClass_subscript_x(),
            MyClass_subscript_xx()
        ]

    def match(self, class_type, method_name):
        if class_type.tag != "MyClass":
            return None
        workers = []
        for method in self.methods:
            if method.enabled:
                worker = method.get_worker(method_name)
                if worker:
                    workers.append(worker)

        return workers
Comment 1 Tom Tromey 2023-11-22 14:59:51 UTC
gdb's c++ parser doesn't understand the multi-dimensional subscript
operator either, though I'm not sure if this is needed for xmethods
or not.

If the problem is purely in the xmethod implementation (I have no idea)
then that is more of a libstdc++ issue.

There's another bug about being able to have pretty-printers request
multi-dimensional array output, that was never implemented either.