It appears to be a specification issue. The part that I have quoted is not in sync with another part which specifies the new correct behaviour followed by Clang (but not GCC nor MSVC surprisingly), [class.access/base-5] (emphasis mine):
A member m is accessible at the point R when named in class N if
- m as a member of N is public, or
- m as a member of N is private, and R occurs in a member or friend of class N, or
- m as a member of N is protected, and R occurs in a member or friend of class N, or in a member of a class P derived from N, where m as a member of P is public, private, or protected, or
- there exists a base class B of N that is accessible at R, and m is accessible at R when named in class B.
Here friend is not mentioned for the derived class. But it used to be. It has been removed in C++ 17 because of defect report CWG 1873 (emphasis mine):
Protected member access from derived class friends
Section: 14.2 [class.access.base] Status: CD4 Submitter:
Richard Smith Date: 2014-02-18
[Moved to DR at the May, 2015
meeting.]
According to 14.2 [class.access.base] paragraph 5,
A member m is accessible at the point R when named in class N if
- …
- m as a member of N is protected, and R occurs in a member or friend of
class N, or in a member or friend of a class P derived from N, where m
as a member of P is public, private, or protected, or
- …
The granting of access via class P is troubling. At the least, there
should be a restriction that P be visible at R. Alternatively, this
portion of the rule could be removed altogether; this provision does
not appear to be widely used in existing code and such references can
be easily converted to use P instead of N for naming the member.
Notes from the June, 2014 meeting:
CWG noted that removing the friend provision would introduce an
undesirable asymmetry between member functions of P and its friends.
Instead, the intent is to require P to be a complete type at R.
Notes from the November, 2014 meeting:
Although the asymmetry is unfortunate, the difference between a
reference in a member function and a reference in a friend is that the
member function concretely determines which P is in view, while the
friend could be befriended by a class that is a specialization of a
class template and thus would require instantiations that would not
otherwise occur. CWG thus decided simply to eliminate the friend case.
Proposed resolution, November, 2014:
Change bullet 5.3 of 14.2 [class.access.base] as follows:
A member m is accessible at the point R when named in class N if
- …
- m as a member of N is protected, and R occurs in a member or friend of
class N, or in a member
or friend of a class P derived from N, where m
as a member of P is public, private, or protected, or
- there exists…
I have filed a pull request on GitHub to correct this inconsistency: https://github.com/cplusplus/draft/pull/3672