Is clang name lookup too restrictive for dependent base classes?

Hello.

The following example is a simplified version of code occurring in doxygen-1.6.2 (in the qtools module):

g++ is too permissive. This is two-phase name lookup, which Clang is very strict about.

  - Doug

Douglas Gregor wrote:

Hello.

The following example is a simplified version of code occurring in doxygen-1.6.2 (in the qtools module):

[...]

g++ is too permissive. This is two-phase name lookup, which Clang is very strict about.

  - Doug

I see. I just tried it on Comeau:
it gives an error if "Compile in strict C++03 mode" is selected,
whereas it accepts it if "Compile in relaxed mode" is selected.

Is there any plan to increase compatibility with g++ by adding some sort of "relaxed mode" option?

I have another example (extracted from some version of gmpxx.h):

Douglas Gregor wrote:

g++ is too permissive. This is two-phase name lookup, which Clang is very strict about.

Is there any plan to increase compatibility with g++ by adding some sort
of “relaxed mode” option?

No. It’s certainly technically possible, but it’s not a priority for the project, assuming we even want to do it.

The fear is that if we add a relaxed mode switch, everyone will just add -frelaxed-mode or whatever and nobody will get the advantage of all these wonderful diagnostics we can emit in template definitions. :slight_smile:

I checked clang source for a reference to the standard and I found the
following (Sema/SemaTemplate.cpp):

// C++0x [temp.expl.spec]p17:
// A member or a member template may be nested within many
// enclosing class templates. In an explicit specialization for
// such a member, the member declaration shall be preceded by a
// template<> for each enclosing class template that is
// explicitly specialized.
// We interpret this as forbidding typedefs of template
// specializations in the scope specifiers of out-of-line decls.

Both g++ and Comeau will refuse the example above if the “template <>”
was omitted … but I don’t see a strong relation between this behavior
and the presence of a typedef name.

Without the template<>, the compiler doesn’t realize you’re declaring
an explicit specialization. But the design of template parameters in the
language (and Doug can speak more to this) is that parameter clauses
are matched up with argument clauses in the declared name — that is,
you match angle brackets to angle brackets. Using a typedef screws
up this matching because it means that parts of the name without
angle-brackets are supposed to matched against.

That’s why the language requires it. From a user-design point of view,
gcc’s permissiveness on this goes a long way towards explaining why
nobody but language geeks understands how this matching is
supposed to work.

John.

John McCall wrote:

[...]

I checked clang source for a reference to the standard and I found the
following (Sema/SemaTemplate.cpp):

    // C++0x [temp.expl.spec]p17:
    // A member or a member template may be nested within many
    // enclosing class templates. In an explicit specialization for
    // such a member, the member declaration shall be preceded by a
    // template<> for each enclosing class template that is
    // explicitly specialized.
    // We interpret this as forbidding typedefs of template
    // specializations in the scope specifiers of out-of-line decls.

Both g++ and Comeau will refuse the example above if the "template <>"
was omitted ... but I don't see a strong relation between this behavior
and the presence of a typedef name.

Without the template<>, the compiler doesn't realize you're declaring
an explicit specialization. But the design of template parameters in the
language (and Doug can speak more to this) is that parameter clauses
are matched up with argument clauses in the declared name — that is,
you match angle brackets to angle brackets. Using a typedef screws
up this matching because it means that parts of the name without
angle-brackets are supposed to matched against.

That's why the language requires it. From a user-design point of view,
gcc's permissiveness on this goes a long way towards explaining why
nobody but language geeks understands how this matching is
supposed to work.

John.

Unfortunately, I made an error when extracting the testcase from GMP.

The complete testcase showing the problem (that could be found in gmpxx.h, both versions 4.3.2 and 5.0.1) is as follows:

See http://llvm.org/bugs/show_bug.cgi?id=6179 where the same issue is tracked, with another gmpxx-distilled testcase and a similiar quote from the C++0x working draft.
Cornelius
-------- Original-Nachricht --------

John McCall wrote:

[...]

I checked clang source for a reference to the standard and I found the
following (Sema/SemaTemplate.cpp):

   // C++0x [temp.expl.spec]p17:
   // A member or a member template may be nested within many
   // enclosing class templates. In an explicit specialization for
   // such a member, the member declaration shall be preceded by a
   // template<> for each enclosing class template that is
   // explicitly specialized.
   // We interpret this as forbidding typedefs of template
   // specializations in the scope specifiers of out-of-line decls.

Both g++ and Comeau will refuse the example above if the "template <>"
was omitted ... but I don't see a strong relation between this behavior
and the presence of a typedef name.

Without the template<>, the compiler doesn't realize you're declaring
an explicit specialization. But the design of template parameters in the
language (and Doug can speak more to this) is that parameter clauses
are matched up with argument clauses in the declared name — that is,
you match angle brackets to angle brackets. Using a typedef screws
up this matching because it means that parts of the name without
angle-brackets are supposed to matched against.

That's why the language requires it. From a user-design point of view,
gcc's permissiveness on this goes a long way towards explaining why
nobody but language geeks understands how this matching is
supposed to work.

John.

Unfortunately, I made an error when extracting the testcase from GMP.

The complete testcase showing the problem (that could be found in
gmpxx.h, both versions 4.3.2 and 5.0.1) is as follows:

// Declaration of class template (__gmp_expr).
template <typename T>
struct S;

// Definition of explicit specialization (__gmp_expr<mpz_t, mpz_t>).
template <>
struct S<int> {
  S foo(S);
};

// Typedef declaration for explicit specialization (mpz_class).
typedef S<int> SInt;

// Out-of-line definition of member of explicit specialization
// (e.g., mpz_class::operator+=).
SInt SInt::foo(SInt x)
{
  return x;
}

The big difference wrt previous testcase is that the class template
specialization is *defined* before introducing its typedef alias.

Someone amusingly, core issue 529 [*] is likely to change this behavior

  C++ Standard Core Language Active Issues

by treating explicitly-specialized class template specializations the same way as implicitly-instantiated ones. That could require 'template<>' in this case.... at least, it would certainly require 'template<>' in

  template<> S<int> S<int>::foo(S<int> x) { return x; }

[*] This issue was discussed among the committee's core working group members during Monday's teleconference and has been marked as "tentative ready" for the upcoming committee meeting, so it's very likely to make it into C++0x.

Here, clang complains the same way as above:

# clang++ -fsyntax-only test.cc
test.cc:15:1: error: cannot use typedef 'SInt' (aka 'S<int>') in scope
specifier
      for out-of-line declaration
SInt SInt::foo(SInt x)
^
1 diagnostic generated.

This code is accepted by g++ and Comeau and, as far as I can tell, it is
standard compliant. In fact, since we now have the explicit
specialization, C++98 14.7.3 p5 becomes relevant.
Quoting the end of that paragraph:

  "Definitions of members of an explicitly specialized class
   are defined in the same manner as members of normal classes,
   and not using the explicit specialization syntax."

Since these definitions do not use the "template <>" prefixes, there is
no way (or need) to perform the matching you were mentioning above,
hence there is no need to forbid the use of typedef names.

John and I had discussed the use of typedef names within qualified declarator names a while back, and we thought we had support from the standard for diagnosing this as an error. However, I'm finding mostly weak support, so at some point we will fix

  http://llvm.org/bugs/show_bug.cgi?id=6179

  - Doug