(Unexpected) Method type

classic Classic list List threaded Threaded
8 messages Options
Reply | Threaded
Open this post in threaded view
|

(Unexpected) Method type

Abramo Bagnara
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?

_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: (Unexpected) Method type

Sebastian Redl
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
_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: (Unexpected) Method type

Abramo Bagnara
Il 10/01/2010 16:07, Sebastian Redl ha scritto:

> 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.

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);
  }
};
_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: (Unexpected) Method type

Abramo Bagnara
Il 10/01/2010 18:49, Sean Hunt ha scritto:

> On 01/10/2010 09:10 AM, Abramo Bagnara wrote:
>> template<typename T>
>> void p(T, T);
>>
>> void g();
>>
>> struct c {
>>    void h();
>>    void f() {
>>      __typeof(h) i;
>>      return p(i, g);
>>    }
>> };
>
> clang compiles this correctly. The type of h is a void(). In the
> declaration of i, since it is a function, it is declaring extern void
> ::i(); When i and g are passed to p, they are each subjected to the
> function-to-pointer conversions, and p is called with T being void().

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.


_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: (Unexpected) Method type

Sebastian Redl
In reply to this post by Abramo Bagnara
Abramo Bagnara wrote:

> Il 10/01/2010 16:07, Sebastian Redl ha scritto:
>  
>> 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
_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: (Unexpected) Method type

Sean Hunt
On 01/10/2010 03:39 PM, Sean Hunt wrote:

> On 01/10/2010 01:44 PM, Sebastian Redl wrote:
>> 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
>
> (I sent a message with much the same content to Abramo but forgot to
> send it to the list)
>
> C++0x decltype has no special provisions for member functions.
> ([dcl.type.simple]). clang implements this correctly, both Comeau and
> GCC crash. I suspect that the whole 'type is unknown' bit on __typeof is
> really a GCC bug rather than an intentional design decision, meaning
> that clang is correct in mirroring decltype's behavior.
>
> Sean

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

Sean
_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: (Unexpected) Method type

Douglas Gregor
In reply to this post by Sebastian Redl

On Jan 10, 2010, at 12:44 PM, Sebastian Redl wrote:

> Abramo Bagnara wrote:
>> Il 10/01/2010 16:07, Sebastian Redl ha scritto:
>>
>>> 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.
> }


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
_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: (Unexpected) Method type

Abramo Bagnara
Il 11/01/2010 16:51, Douglas Gregor ha scritto:
>
> 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).

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.
_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-dev