Bug 46283 - Strange lookup of p->type::foo expressions
Summary: Strange lookup of p->type::foo expressions
Status: UNCONFIRMED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: unknown
: P3 minor
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: wrong-code
Depends on:
Blocks:
 
Reported: 2010-11-02 23:29 UTC by Tor Myklebust
Modified: 2023-02-14 20:19 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Tor Myklebust 2010-11-02 23:29:41 UTC
This prints 1, thinking that 'foo' means a::foo:

#include <stdio.h>

struct b {
  typedef struct a foo;
  int x() { return 1; }
  void go();
};

struct a : public b {
  typedef b foo;
  int x() { return 2; }
};

void b::go() { printf("%i\n", ((a *)0)->foo::x()); }

int main() {
 b().go();
}

If I trivially parametrise b on the derived type T and turn the cast to a * into a cast to T *, it starts printing 2 as it starts finding the foo from local name lookup:

#include <stdio.h>

template <typename T>
struct b {
  typedef struct a foo;
  int x() { return 1; }
  void go();
};

struct a : public b<a> {
  typedef b<a> foo;
  int x() { return 2; }
};

template <typename T>
void b<T>::go() { printf("%i\n", ((T *)0)->foo::x()); }

int main() {
 b<a>().go();
}

This happens with every version of gcc I've tried (4.1 through 4.5).  Other compilers error on this code, complaining that they find different foo doing name lookup in a or T and doing a normal local name lookup.  I hear the standard agrees with that behaviour.
Comment 1 Andrew Pinski 2012-02-01 02:56:10 UTC
I think the code should be rejected.  Here is what Comeau online says:
"ComeauTest.c", line 14: error: ambiguous class member reference -- type "b::foo"
          (declared at line 4) used in preference to type "a::foo" (declared
          at line 10)
Comment 2 Andrew Pinski 2021-07-24 10:45:02 UTC
((a *)0)->foo::x()

((T *)0)->foo::x()

Both are those are undefined because you are calling a method of a NULL pointer.

(In reply to Andrew Pinski from comment #1)
> I think the code should be rejected.  Here is what Comeau online says:
> "ComeauTest.c", line 14: error: ambiguous class member reference -- type
> "b::foo"
>           (declared at line 4) used in preference to type "a::foo" (declared
>           at line 10)

And clang rejects this:
<source>:14:41: error: lookup of 'foo' in member access expression is ambiguous
void b::go() { printf("%i\n", ((a *)0)->foo::x()); }
                                        ^
<source>:10:13: note: lookup in the object type 'a' refers here
  typedef b foo;
            ^
<source>:4:20: note: lookup from the current scope refers here
  typedef struct a foo;
                   ^
Comment 3 Tor Myklebust 2021-07-24 15:04:28 UTC
On Sat, Jul 24, 2021 at 6:45 AM pinskia at gcc dot gnu.org
<gcc-bugzilla@gcc.gnu.org> wrote:
>
> https://gcc.gnu.org/bugzilla/show_bug.cgi?id=46283
>
> --- Comment #2 from Andrew Pinski <pinskia at gcc dot gnu.org> ---
> ((a *)0)->foo::x()
>
> ((T *)0)->foo::x()
>
> Both are those are undefined because you are calling a method of a NULL
> pointer.

That's true.  In both cases, one can modify this code, for instance by
writing (new a)->foo::x() or (new T)->foo::x(), or by statically
instantiating an object of the appropriate type, without changing the
relevant name lookup behaviour.

> (In reply to Andrew Pinski from comment #1)
> > I think the code should be rejected.  Here is what Comeau online says:
> > "ComeauTest.c", line 14: error: ambiguous class member reference -- type
> > "b::foo"
> >           (declared at line 4) used in preference to type "a::foo" (declared
> >           at line 10)
>
> And clang rejects this:
> <source>:14:41: error: lookup of 'foo' in member access expression is ambiguous
> void b::go() { printf("%i\n", ((a *)0)->foo::x()); }
>                                         ^
> <source>:10:13: note: lookup in the object type 'a' refers here
>   typedef b foo;
>             ^
> <source>:4:20: note: lookup from the current scope refers here
>   typedef struct a foo;
>                    ^

It appears (through playing with Matt Godbolt's Compiler Explorer)
that a change was made between clang 5.0.1 and clang 6.0.0 that made
clang match gcc's behaviour.  5.0.1 gives the errors above, while
6.0.0 compiles the code without warnings or errors.
Comment 4 Andrew Pinski 2021-12-07 12:55:11 UTC
So the problem is which foo is the right foo here should it be looked up not based on a-> but on the current class.

I think the P1787R6 C++ paper clarifies this.