Bug 43282 - GCC looks into dependent bases during unqualified lookup
Summary: GCC looks into dependent bases during unqualified lookup
Status: RESOLVED DUPLICATE of bug 24163
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.4.3
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
Depends on: 15272
  Show dependency treegraph
Reported: 2010-03-07 19:04 UTC by Johannes Schaub
Modified: 2010-11-30 19:14 UTC (History)
3 users (show)

See Also:
Host: i686-pc-linux-gnu
Target: i686-pc-linux-gnu
Known to work:
Known to fail:
Last reconfirmed:


Note You need to log in before you can comment on or make changes to this bug.
Description Johannes Schaub 2010-03-07 19:04:57 UTC
GCC does not accept this code, but is supposed to. "foo" is looked up using unqualified name lookup, during which dependent base classes are ignored. The fact that "foo" is dependent must not influence this.

template<typename T>
struct HasFoo {
  void foo() { }

// dependent "HasFoo<T>" should be ignored during
// lookup of "foo"
template<typename T>
struct Bar : HasFoo<T> {
  void bar() { foo(T()); }

namespace A {
  struct Baz { };
  void foo(Baz); // should be found!

int main() { Bar<A::Baz> b; b.bar(); }
Comment 1 Wolfgang Bangerth 2010-03-07 23:41:08 UTC
The error message I get is this:

g/x> c++ -c x.cc
x.cc: In member function 'void Bar<T>::bar() [with T = A::Baz]':
x.cc:18:   instantiated from here
x.cc:10: error: no matching function for call to 'Bar<A::Baz>::foo(A::Baz)'
x.cc:3: note: candidates are: void HasFoo<T>::foo() [with T = A::Baz]

This error message is given upon instantiation time since the call was
(correctly) considered dependent. At instantiation time, gcc finds the
function in the base class but decides that the arguments don't match --
producing the error. Note that the first scope in which a function is
found terminates the search for possible other candidates, even if the
functions in the first scope in which functions are found don't match.

Consequently the code is rejected. Why do you think this
is not the correct behavior?

Comment 2 Johannes Schaub 2010-03-08 00:01:00 UTC
The point is that the scope of the base class is not examined even during instantiation, so you cannot find the class member function and ADL finds A::foo instead. The Standard says at 14.6.2/3:

In the definition of a class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member *or during an instantiation of the class template or member*. 
Comment 3 Wolfgang Bangerth 2010-03-08 00:19:26 UTC
But that would mean that the following code should be invalid
because the compiler should never find HasFoo<T>::foo even at
instantiation time:
template<typename T>
struct HasFoo {
  void foo(T) { }

template<typename T>
struct Bar : HasFoo<T> {
  void bar() { foo(T()); }

int main() { Bar<int> b; b.bar(); }
I don't think the intent is to make this invalid.

Comment 4 Johannes Schaub 2010-03-08 00:26:07 UTC
Yes, this is the consequence. You have to add "this->" or "Bar::" to use another form of lookup (qualified or class-member access) or use a using-declaration to bring the name in scope ("using HasFoo<T>::foo;"). 

I can't say anything about the intent, but comeau rejects this code, and clang too (it's actually a well known pit-fall in C++). 

See my article here: http://stackoverflow.com/questions/2396019/c-templates-hides-parent-members/2398186#2398186 . 
Comment 5 Wolfgang Bangerth 2010-03-08 00:36:27 UTC
OK, so the question is whether the testcase in comment #3 should be rejected
based on the wording of 14.6.2/3.

Jason, as our resident language lawyer, would you mind commenting?

Comment 6 Andrew Pinski 2010-03-08 01:06:38 UTC
Dup of bug 15272.
Comment 7 Johannes Schaub 2010-03-08 23:41:51 UTC
I've digged this up from an early draft ('96: http://ra.dkuug.dk/JTC1/SC22/WG21/docs/wp/txt/jun96/body.txt), '98 and '03). Each has different rules. In fact, C++98 would accept comment#3's code. 

- '96pre-standard said that: if a base class is dependent, this scope is not examined by lookup (of any kind) until the class is instantiated. However, if a base class is a template parameter, then the names declared in it do not hide names from an enclosing scopes. (So, if we inherit from "T", we first look in the "enclosing" scopes and if we found nothing, we look in dependent base classes). 

- '98 said that: if a base class is dependent and declares a name, that name may not hide a name from the enclosing scopes. I don't think that A::foo is considered to be in an enclosing scope of HasFoo, so C++98 would also reject my code, i think. So C++98 just did away with the difference of inheriting a template-parameter and the general case of dependent bases for the hiding stuff. 

- '03 says that: Unqualified namelookup will not consider dependent bases - even not during instantiation. This was done by DR213: http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#213 . John Spicer sums it up: `What I thought the rule was is, "If a base class is a dependent type a member of that class is not found by unqualified lookup".`. 

According to Erwin Unruh in that DR, unqualified lookup at the definition context can't find members in dependent base classes "because you don't know what base class you have.". I am in the impression that since lookup for dependent names is post-poned until the actual template arguments are known, we will indeed be able to descend into dependent base classes when doing lookup in the definition context, since we know the exact types of these bases by then (and this seems to be what GCC is doing currently, actually). 
Comment 8 Johannes Schaub 2010-06-06 01:01:37 UTC
(In reply to comment #6)
> Dup of bug 15272.

I don't know about the internals of GCC, but from a Standard point of view, the code in that bug shows a different problem than the code in my bug report. 

In my bug report the problem is that dependent base classes are considered during dependent name lookup. But in that other bug report, "f" is not a dependent name at all. It is *made* dependent by the other name in the non-dependent base (i don't know why, but maybe GCC prepends the "implicit this" prematurely, and then makes it dependent that way by thinking the name originally was "this->f" - but this is just a guess!). 

But in any case, my bug-report does not seem to be a duplicate of that one.
Comment 9 Andrew Pinski 2010-11-30 19:14:37 UTC
This is a dup of bug 24163.

*** This bug has been marked as a duplicate of bug 24163 ***