(Unexpected) Method type

Analyzing the AST generated for a simple program I've seen a weird
thing: the type of non static methods have the same structure of
ordinary functions.

I've also built a small test case that show this:

$ cat aaa.cc
struct c {
  void h();
  void (*f())() {
    return &h;
  }
};

$ gcc -c aaa.cc
aaa.cc: In member function ‘void (* c::f())()’:
aaa.cc:4: error: ISO C++ forbids taking the address of an unqualified or
parenthesized non-static member function to form a pointer to member
function. Say ‘&c::h’
aaa.cc:4: error: cannot convert ‘void (c::*)()’ to ‘void (*)()’ in return
$ ~/llvm/Debug/bin/clang -W -Wall -c aaa.cc
$

Apart clang behaviour reported above (that I suppose it's definitely a
bug) the choice to not have a MethodType (or MemberFunctionType)
different from FunctionProtoType is deliberate or it's something that
should be saned?

Abramo Bagnara wrote:

Apart clang behaviour reported above (that I suppose it's definitely a
bug)

Yes, but not one in the type system.

the choice to not have a MethodType (or MemberFunctionType)
different from FunctionProtoType is deliberate or it's something that
should be saned?
  

It's deliberate. I saw no reason to give it a distinct type. A
MemberPointerType to FunctionProtoType is a pointer to member function.
The only place where a member function can appear without a pointer to
it is in call expressions. You can't do anything with the function in
these cases except call it, so the type system gets along just fine.

The bug here is that the validation code for & takes a wrong path and
returns a normal PointerType to FunctionProtoType.

Sebastian

To tell you the truth, I'm a bit perplexed: I think that a non static
method type is something very different from an ordinary function type
and that they should not share the same type.

If we use the same type, comparison of canonical type of a non static
method and canonical type of an ordinary function could wrongly succeeds
and I expect other problems following this path.

Also under a C++ standard perspective I don't see how a non static
method could be considered to have the same type of ordinary function,
they don't have neither the same arguments (because implicit "this"
argument).

The following C++ source (wrongly compiled by clang) is another small
snippet that shows the problems of current approach:

template <typename T>
void p(T, T);

void g();

struct c {
  void h();
  void f() {
    __typeof(h) i;
    return p(i, g);
  }
};

This is exactly the point: I don't think that type of i should become
void() as type of h IMO is not void().

Intel CC compiler, gcc and Comeau agree that the code above should not
compile.

Abramo Bagnara wrote:

  

It's deliberate. I saw no reason to give it a distinct type. A
MemberPointerType to FunctionProtoType is a pointer to member function.
The only place where a member function can appear without a pointer to
it is in call expressions. You can't do anything with the function in
these cases except call it, so the type system gets along just fine.

The bug here is that the validation code for & takes a wrong path and
returns a normal PointerType to FunctionProtoType.
    
To tell you the truth, I'm a bit perplexed: I think that a non static
method type is something very different from an ordinary function type
and that they should not share the same type.
  

But they do! Look at this snippet; GCC accepts it:

typedef void FuncType();
FuncType func; // Declare a normal function called func, no return value
or parameters.
void func() { // Define func.
}
struct A {
  FuncType mfunc; // Declare a member function called mfunc, no return
value or parameters.
};
void A::mfunc() { // Define mfunc.
}

If we use the same type, comparison of canonical type of a non static
method and canonical type of an ordinary function could wrongly succeeds
and I expect other problems following this path.
  

There is no way in valid C++ to create a situation where this is
relevant. If we have some bugs in this area, it's because we're not
intercepting invalid code early enough, as your two code examples show.

Also under a C++ standard perspective I don't see how a non static
method could be considered to have the same type of ordinary function,
they don't have neither the same arguments (because implicit "this"
argument).
  

What the standard says is only a minor consideration in the design of
Clang's data structures. More important is that using FunctionProtoType
directly simplifies a lot of code. Also, the implicit this doesn't
matter - take a look at the one important place where it could: the
standard explicitly says that it is ignored. I'm talking about overload
resolution, of course.

struct B {
  static void fn(int);
  void fn(double);
};
void test() {
  B b;
  b.fn(1); // Calls fn(int), ignoring the object argument
  b.fn(1.0); // Calls fn(double), ignoring the object argument
}

The following C++ source (wrongly compiled by clang) is another small
snippet that shows the problems of current approach:

template <typename T>
void p(T, T);

void g();

struct c {
  void h();
  void f() {
    __typeof(h) i;
    return p(i, g);
  }
};
  

But again, this is (or in the case of Clang, should be) rejected far
earlier, namely at __typeof(h). GCC complains that the type of the
member function is unknown. In other words, you're not allowed to apply
typeof to a member function. I'll have to check what C++0x says about
decltype, but I suspect it's the same thing.

Sebastian

And, of course, I then go and send it to the wrong list...

Sean

Template argument deduction is another place where we see that the type of a member function is just a normal function type, since a pointer to member function is really just a pointer to member where the member has function type:

  template<typename T> struct X;
  template<typename T, typename Class> struct X<T Class::*> { };

  struct Y { };

  X<int (Y::*)(int)> x; // instantiates partial specialization with Class=Y, T=int(int).

  - Doug

Now I got it: from the code above it's clear that a method (static or
not) is simply a class member with function type.

I've never interpreted non static method nature in this way: I was
interpreting them as a distinct kind of functions with implicit argument
and I interpreted the & operator applied to member as an ordinary
address-of operator whose type is always T* if member has type T.

Instead it seems that the & operator applied to member does not respect
this assumption.

Now I wonder if it's correct to build the AST UnaryOperator with the
same kind of operator for both non-static members and for other things,
but I believe this is a minor point.

Many thanks to all of you for your explanations.