Three code snippets accepted by clang and rejected by g++

Hello.

While testing a recently written piece of code to see if it worked on linux
I found three differences between clang and g++ that make the first
to compile the code and the second to reject it. I'm talking about the
clang shipped with Xcode 5.1.1, I don't have a working trunk and my
machine is slow to compile, sorry.

Below the three issues:

1) transitive-friend.cpp
class A {
  friend class B;
  int v;
};

struct B {
  A a;
  friend int f(B b) {
     return b.a.v;
  }
};

Clang accept this code because
I access the private field of A in the scope of B
and B is a friend. g++ rejects it:

$ g++-4.9 -std=c++11 -fsyntax-only differences.cpp
differences.cpp: In function 'int f(B)':
differences.cpp:3:7: error: 'int A::v' is private
   int v;
       ^
differences.cpp:9:17: error: within this context
      return b.a.v;
                 ^

2) templatebase.cpp
template<typename T>
struct A {
   template<typename U>
   struct B {

   };

   struct C;
};

template<typename T>
struct A<T>::C : public A<T>::B<int> // public A<T>::template B<int>
{

};

Is the template keyword needed here? Note that if you move out
struct C so that it isn't a member of A anymore, clang correctly
says that I need the template keyword. g++ wants the keyword
in both cases:

$ g++-4.9 -std=c++11 -fsyntax-only templatebase.cpp
templatebase.cpp:12:36: error: 'typename A<T>::B' names 'template<class T> template<class U> struct A<T>::B', which is not a type
struct A<T>::C : public A<T>::B<int> {
                                    ^
templatebase.cpp:12:31: error: 'typename A<T>::B' names 'template<class T> template<class U> struct A<T>::B', which is not a type
struct A<T>::C : public A<T>::B<int> {
                               ^
templatebase.cpp:12:38: error: expected class-name before '{' token
struct A<T>::C : public A<T>::B<int> {
                                      ^
3) tupleconstructor.cpp
#include <tuple>

std::tuple<int, int, int>
func() {
   return { 0, 1, 2 };
}

I don't know how to phrase the situation in standardese terms (is it list-initialization?).
It seems to me that clang picks the forwarding constructor, g++ the explicit initializer-list constructor.

$ g++-4.9 -std=c++11 -fsyntax-only tupleconstructor.cpp
tupleconstructor.cpp: In function 'std::tuple<int, int, int> func()':
tupleconstructor.cpp:5:21: error: converting to 'std::tuple<int, int, int>' from initializer list would use explicit constructor 'constexpr std::tuple< <template-parameter-1-1> >::tuple(_UElements&& ...) [with _UElements = {int, int, int}; <template-parameter-2-2> = void; _Elements = {int, int, int}]'
    return { 0, 1, 2 };
                     ^

In all these three cases, who is right? Are these bugs in clang?
In this case, should I make a separate bug report for each case, right? I've attached the single test cases

Greetings,
Nicola

templatebase.cpp (192 Bytes)

transitive-friend.cpp (107 Bytes)

tupleconstructor.cpp (78 Bytes)

I’d say the first one is a clang bug. Friends are not transitive, the problem is in the implementation of class.protected restriction inside CheckAccess function.

EffectiveContext EC(S.CurContext);

Current context here is class B and that doesn’t seem right, added John as he’s the author.

I'd say the first one is a clang bug.

I agree. This is poor language design — access should really be granted to all lexically nested code, not just members — but it’s what we’ve got.

Friends are not transitive, the problem is in the implementation of class.protected restriction inside CheckAccess function.

  EffectiveContext EC(S.CurContext);

Current context here is class B and that doesn't seem right, added John as he's the author.

That seems wrong. Are you just speculating? The current context should be the declaration of ‘int f(B)’ that’s nested within the class.

I believe the correct fix is to change the special case for friend functions in the EffectiveContext constructor, probably by walking to the semantic context instead of the lexical context. That is, removing the special case completely. However, presumably that special case was added for a reason.

John.

I've submitted a patch before, but it needed work, and I forgot about
it (after summer vacation).
Discussion: http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20130603/080940.html

Ismail

Hello.

While testing a recently written piece of code to see if it worked on linux
I found three differences between clang and g++ that make the first
to compile the code and the second to reject it. I'm talking about the
clang shipped with Xcode 5.1.1, I don't have a working trunk and my
machine is slow to compile, sorry.

Below the three issues:

1) transitive-friend.cpp
class A {
  friend class B;
  int v;
};

struct B {
  A a;
  friend int f(B b) {
     return b.a.v;
  }
};

Clang accept this code because
I access the private field of A in the scope of B
and B is a friend. g++ rejects it:

$ g++-4.9 -std=c++11 -fsyntax-only differences.cpp
differences.cpp: In function 'int f(B)':
differences.cpp:3:7: error: 'int A::v' is private
   int v;
       ^
differences.cpp:9:17: error: within this context
      return b.a.v;
                 ^

Clang's behavior reflects the current intent of the C++ core working group.
This is core issue 1699, and the current resolution to that is to treat
everything that is lexically within a befriended entity as being
befriended. GCC does not yet implement the resolution to core issue 1699
(which is reasonable, since it's not yet even resolved).

2) templatebase.cpp

template<typename T>
struct A {
   template<typename U>
   struct B {

   };

   struct C;
};

template<typename T>
struct A<T>::C : public A<T>::B<int> // public A<T>::template B<int>
{

};

Is the template keyword needed here?

Clang's behavior reflects the current intent of the C++ core working group.
This is core issue 1710 (which Clang only partially follows); the template
keyword is optional in this context.

Note that if you move out
struct C so that it isn't a member of A anymore, clang correctly
says that I need the template keyword. g++ wants the keyword
in both cases:

$ g++-4.9 -std=c++11 -fsyntax-only templatebase.cpp
templatebase.cpp:12:36: error: 'typename A<T>::B' names 'template<class T>
template<class U> struct A<T>::B', which is not a type
struct A<T>::C : public A<T>::B<int> {
                                    ^
templatebase.cpp:12:31: error: 'typename A<T>::B' names 'template<class T>
template<class U> struct A<T>::B', which is not a type
struct A<T>::C : public A<T>::B<int> {
                               ^
templatebase.cpp:12:38: error: expected class-name before '{' token
struct A<T>::C : public A<T>::B<int> {
                                      ^
3) tupleconstructor.cpp
#include <tuple>

std::tuple<int, int, int>
func() {
   return { 0, 1, 2 };
}

I don't know how to phrase the situation in standardese terms (is it
list-initialization?).
It seems to me that clang picks the forwarding constructor, g++ the
explicit initializer-list constructor.

Is this a Clang versus GCC issue, or a libc++ versus libstdc++ issue? What
does Clang say if you use -stdlib=libstdc++? I would not be surprised if
this is a library DR.

Great, this is the best resolution. There are many unfortunate corner cases not caught by the current wording, including nested classes within inline friend functions.

John.

Shouldn't Clang only implement core issues with a special "bleeding edge" flag?

cheers,
--renato

Clang's behavior reflects the current intent of the C++ core working group.
This is core issue 1699, and the current resolution to that is to treat
everything that is lexically within a befriended entity as being befriended.
GCC does not yet implement the resolution to core issue 1699 (which is
reasonable, since it's not yet even resolved).

Shouldn't Clang only implement core issues with a special "bleeding edge" flag?

Thanks everybody for your informative replies.
I was writing, just before Renato's message:
what about a set of warnings for compatibility issues that could arise by relying
on core issues being implemented by clang that other compilers could not have
implemented yet?

cheers,
--renato

Thanks,
Nicola

No, I don't think so -- or at least, not as a general policy. These are bug
fixes in the language, and it's not reasonable to expect us to maintain two
different codepaths with and without the fix. In this particular case,
Clang has (to my knowledge) never implemented anything other than the fixed
version, long before the committee chose to make the fix.

There are a handful of core issues for which we do maintain old and new
versions, controlled by the -std= flag, but those are weird and probably
not a good idea. Likewise, there are some cases where we maintain two
versions for compatibility with other compilers (notably, there are some of
these under -fms-compatibility).