This fails because of the two-phase name lookup in C++.
In phase one, when the template is initially parsed, long before it is instantiated, the compiler parses the template and looks up any non-dependent names. S::P
is a non-dependent name, so the compiler tries to look it up, but fails because it is private.
In phase 2, when the template is instantiated, the compiler will lookup any dependent names, which can vary from template to template.
Clang is fairly strictly conforming to the two-phase name lookup. However, MSVC has a template parsing model that delays nearly every lookup to instantiation time, which is part of phase 2. This delay is why your example would compile with MSVC(which is non-conforming) and not in clang. Here is a link with more information:
The Dreaded Two-Phase Name Lookup
Also, here are the sections from the C++ standard where it describes the two-phase lookup.
14.6.8:
When looking for the declaration of a name used in a template
definition, the usual lookup rules (3.4.1, 3.4.2) are used for
non-dependent names. The lookup of names dependent on the template
parameters is postponed until the actual template argument is known.
14.6.9:
If a name does not depend on a template-parameter (as defined in
14.6.2), a declaration (or set of declarations) for that name shall be in scope at the point where the name appears in the template
definition; the name is bound to the declaration (or declarations)
found at that point and this binding is not affected by declarations
that are visible at the point of instantiation.
Then the part of 3.4 Name lookup applicable to you:
The access rules (clause 11) are considered only once name lookup and
function overload resolution (if applicable) have succeeded. Only
after name lookup, function overload resolution (if applicable) and
access checking have succeeded are the attributes introduced by the
name’s declaration used further in expression processing (clause 5).
Its clear from reading these parts that your program is ill-formed. The only thing the standard states that should be postponed until instantiation is the lookup of a dependent name. Non-dependent names go through the usual name lookup, which includes access rules.