From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 1506 invoked by alias); 6 Jan 2004 04:28:53 -0000 Mailing-List: contact gdb-patches-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sources.redhat.com Received: (qmail 1498 invoked from network); 6 Jan 2004 04:28:51 -0000 Received: from unknown (HELO tisch.mail.mindspring.net) (207.69.200.157) by sources.redhat.com with SMTP; 6 Jan 2004 04:28:51 -0000 Received: from user-119a90a.biz.mindspring.com ([66.149.36.10] helo=berman.michael-chastain.com) by tisch.mail.mindspring.net with esmtp (Exim 3.33 #1) id 1AdipJ-0006fg-00; Mon, 05 Jan 2004 23:28:22 -0500 Received: by berman.michael-chastain.com (Postfix, from userid 502) id 584854B35A; Mon, 5 Jan 2004 23:28:41 -0500 (EST) To: drow@mvista.com, mec.gnu@mindspring.com Subject: Re: [rfc/cp] method stub assertions Cc: gdb-patches@sources.redhat.com Message-Id: <20040106042841.584854B35A@berman.michael-chastain.com> Date: Tue, 06 Jan 2004 04:28:00 -0000 From: mec.gnu@mindspring.com (Michael Elizabeth Chastain) X-SW-Source: 2004-01/txt/msg00130.txt.bz2 Hi Daniel, I think we both have to stare at the wall and soak in the reference books a bit more. Here's a long message with my understanding. > Those are static methods. Don't confuse them with normal methods! > They're basically just functions that live in a class. Right. There are really three types here: plain function (non-class function or static function) method function pointer to method function > Do > class A { int bar (int); } > and > class B { int baz (int); } > > have the same DNTT type? If they do, then aCC is so hideously busted > that I don't know what to do. I'm pretty sure they don't, because when I step through gdb, I see that the first parameter of A::bar is "class A * this", as it should be. > Wait a sec... this doesn't make sense... if the domain type is only > needed for non-static members we could just get it from the first > argument.... something is wrong here. Yeah. Forget about gdb for a moment. Just think about C++ on the C++ type level. Plain functions: extern int f1 (int); extern int f2 (int); extern int bad3 (double); extern double bad4 (int); int (*pfii) (int); pfii = &f1; // okay pfii = &f2; // okay pfii = &bad3; // BAD pfii = &bad4; // BAD The last two assignments are bad because the call signatures do not match. Now forget about pointer-to-method function for a minute and just try to do this: class A { public: static int f5 (int); int bad6 (int); }; pfii = &A::f5; // okay pfii = &A::bad6; // BAD That last assignment has to fail because the call signatures do not match at all. pfii push int; call; pop int A::bad6 push A&; push int call; pop init Suppose we try: int (*pfiAi) (A&, int); pfiAi = &A::bad6; That would work with gcc (if it were legal code), but only because gcc uses an ABI where the "this" pointer interoperates with the first normal parameter. But imagine an ABI where "this" goes in a register and every other parameter goes on the stack. At the language level, the "this" parameter is not interchangeable with the normal parameters. So now let's re-invent pointer-to-member-function. Start with the right hand side: &A::bad6 The value of &A::bad6 is just like the value of &f1, it's a text address such as 0x80483ac (this is only true because A::bad6 is not virtual, leave virtual pmf's for later). But the type of &A::bad6 is inherently different from the type of &f1. The type of f1 encodes this information: return = {type:int} parameter-list = [ {type:int} ] The type of A::bad6 encodes this information: class = {class:A} return-type = {type:int} parameter-list = [ {type:int} ] And of course, the real way to write this in C++ is: int (A::*pfAii) (int); pfAii = &A::bad6; Okay, let's see what gdb says: (gdb) ptype pfii type = int (*)(int) (gdb) ptype pfiAi type = int (*)(A &, int) (gdb) ptype pfAii type = struct { int (*__pfn)(A *, int); int __delta; } (gdb) ptype A::bad6 type = int (A * const, int) (gdb) ptype &A::bad6 type = int (*)(A * const, int) The first four of those are okay with me. pfAii is not only a different type from a pointer to an ordinary function, but the type has a different size! The last one, &A::bad6, bothers me a lot. The address-of operator applied to a (non-static) member function should *not* return a plain pointer-to-function. Output #5 should *not* look like #2. It should look like: (gdb) ptype &A::bad6 type = int (A::*)(int) I'm willing to slide on the "A * const" hidden parameter, but not on the "A::*" instead of "*". I think that gdb does not distinguish between pointer-to-function and PMF. PMF's always carry a class argument with them, but plain PF's never need them. I suspect gdb is mixing the TYPE_CODE's instead of using separate TYPE_CODE's. The existing scheme is something like: TYPE_CODE_MEMBER TYPE_CODE_METHOD domain type But I want it to be more: TYPE_CODE_MEMBER domain type TYPE_CODE_METHOD Every time you create a TYPE_CODE_MEMBER, you should know what the domain type is, and store it up one node, not down in the TYPE_CODE_METHOD node. And every time you use a TYPE_CODE_MEMBER, you have to use the domain type. And if you're not dealing with pointer-to-member, you don't need the domain type. It's an attribute of the pointer-to-member, not an attribute of the member! Bleagh. I'm probably butchering the actual gdb data structures here. Need to read more. > Do static methods have TYPE_CODE_METHOD, and should they? > That's the question. I think they currently do. And then they have TYPE_FLAG_STATIC to distinguish them from other types. Static methods have to appear with other methods because they participate in overload resolution. I think that when gdb evaluates UNOP_ADDR, evaluate_subexp_for_address needs to be more careful to return the appropriate type. There are four cases: plain function, static member function, non-static member function, virtual member function. They all have different types, and they even have different bit-representations in their values. Right now I don't understand the existing TYPE_CODE_* well enough. I want to stare at the TYPE_CODE_* for a while. I think that if we assign and use them properly that some code will actually get simpler and some of the hp-specific "pointer to member" code will actually just generalize to everybody's "pointer to member". Michael C