Description
There is implementation divergence in this code (Clang accepts, GCC rejects)
class D {
class E {
class F {};
friend void foo1(D::E::F& q);
};
friend void foo1(D::E::F& q);
};
void foo1(D::E::F& q) {}
[class.access.general]/p6 says
All access controls in [class.access] affect the ability to name a class member from the declaration of a particular entity, including parts of the declaration preceding the name of the entity being declared and, if the entity is a class, the definitions of members of the class appearing outside the class's member-specification.
From @opensdh
The interesting citation here is actually /6: are we "nam[ing F] from the declaration" of ::D or of ::foo1? One could argue that the answer is "both", and so both access checks are made, but that doesn't seem very satisfying in the case of the declaration of a nested class that is a friend (since then all its access would be limited to that of its containing class). But neither can the answer simply be "the friend being declared", since of course then you could declare any private member anywhere a friend since it can access itself. If we eliminate those, GCC's interpretation seems correct, but I wouldn't discourage filing a core issue here since that wording is at least imprecise.
It would indeed be useful to clarify and add an example.
Additionally, there is the more design-y question of whether we want to accept this code, which looks reasonable.
Another related example from @t3nsor
class G {
using T = int;
friend void foo(T);
};
class H {
friend void foo(G::T);
}
Brian further suggests we could relax what friends declaration can access, to make both of these examples well-formed
But what if we just carved out a special rule for friend declarations, i.e., a friend declaration gets access to whatever its enclosing scope has access to, plus whatever access was granted to that entity by previous friend declarations of the same entity?
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52212
llvm/llvm-project#12361