Re: [rfc/cp] method stub assertions

Michael Elizabeth Chastain
2004-01-06T04:28:53Z

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