12

The following is a piece of test code, and I'm comparing the result of compiling this with MSVC and Clang respectively. The output of each compiler is shown below. MSVC pretends that the unused template declaration doesn't even exist. Clang produces an error. The question is, which compiler is most standard conformant here?

I have seen legacy production code that relies on the MSVC behavior, and I'm unsure whether or not it can continue to be relied on.

class S
{
    struct P {};
};

template<typename T>
S::P Bat(T);

Compiles cleanly in MSVC10:

E:\clangbuild\bin\Release>cl /c /nologo test.cpp
test.cpp

Produces an error in Clang:

E:\clangbuild\bin\Release>clang++ test.cpp
test.cpp:9:4: error: 'P' is a private member of 'S'
S::P Bat(T);
   ^
test.cpp:5:9: note: implicitly declared private here
struct P {};
        ^
1 error generated.
brendanw
  • 601
  • 5
  • 10
  • 2
    What, exactly, do you plan to "rely" on? The template cannot be instantiated. – James McNellis Feb 16 '12 at 01:14
  • The code I'm looking at (but did not write) uses the the obscure fact that MSVC allows the bad template to compile to enforce restrictions on types passed into other templates. When a bad type is passed in, an overload using this template is used, causing a compile error. – brendanw Feb 16 '12 at 01:30
  • @brendanw : Even the EDG front end doesn't give any error. – Prasoon Saurav Feb 16 '12 at 08:51

2 Answers2

4

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.

Paul Fultz II
  • 17,682
  • 13
  • 62
  • 59
  • You rooted the cause (+1!) however I do wonder whether both are Standard conformant or not. It seems damning that the Standard would not precise whether this program is ill-formed or not. – Matthieu M. Feb 16 '12 at 07:18
  • Access checking is not part of name lookup. MSVC(2010 at least) does do name lookups on all non-dependent names in template declarations; change S::P to S::A in the posted example and it will throw an error. – Gerald Feb 16 '12 at 08:24
  • @Gerald MSVC will check if the name exist, but name resolution and considering access control is part of name lookup. See: http://stackoverflow.com/a/6273465/375343 – Paul Fultz II Feb 16 '12 at 13:23
  • 1
    Thanks very much for the clear explanation. I'll flag yours as accepted. The incomplete implementation of two-phase lookup in MSVC does seem like it can lead to unexpected build breaks when ill formed templates and specializations are instantiated long after code has been reviewed and checked in. – brendanw Feb 16 '12 at 18:51
  • @Paul no it's not. The link you just posted has nothing to do with access checking. – Gerald Feb 16 '12 at 19:42
  • See the C++11 standard: "Name lookup associates the use of a name with a declaration (3.1) of that name. Name lookup shall find an unambiguous declaration for the name (see 10.2). Name lookup may associate more than one declaration with a name if it finds the name to be a function name; the declarations are said to form a set of overloaded functions (13.1). Overload resolution (13.3) takes place after name lookup has succeeded. The access rules (Clause 11) are considered only once name lookup and function overload resolution (if applicable) have succeeded." – Gerald Feb 16 '12 at 19:43
  • @Gerald According to the standard, name lookup goes through this process: name lookup -> overload resolution -> access check. The only time it should delay this name lookup process is for dependent types, but MSVC delays it for non-dependent types as well. The link was an example of how MSVC delayed the overload resolution stage of the process. If this stage is delayed, then obviously access checks are also delayed and non-conformant as well on MSVC. – Paul Fultz II Feb 16 '12 at 20:01
  • @brendanw It could lead to subtle bugs in MSVC, but so can ADL lookup. The big reason why microsoft does this if for compile-time performance. Delaying lookups until instantiation is much faster, especially since some templates are never instantiated. – Paul Fultz II Feb 16 '12 at 20:11
  • 1
    The standard also says this: "Knowing which names are type names allows the syntax of every template definition to be checked. No diagnostic shall be issued for a template definition for which a valid specialization can be generated. If no valid specialization can be generated for a template definition, and that template is not instantiated, the template definition is ill-formed, no diagnostic required." i.e. the compiler is not required to generate errors on ill-formed templates that are not instantiated, but they can if they want. – Gerald Feb 16 '12 at 20:26
  • @Gerald That has to do with parsing of C++ syntax. And it does not negate the name resolution that is required. Finally, MSVC is non-conforming with regards to two-phase name lookup and there are many examples of that, not just this one. – Paul Fultz II Feb 17 '12 at 01:33
  • @Paul if the question was "is MSVC conformant" then the answer is obviously no. Type names in template DEFINITIONS are not looked up at all until the template is instantiated. But there is a lot of language in the section on templates that says "ill-formed, no diagnostic required" that essentially makes almost all error reporting except for basic syntax checking in the first phase of a two phase lookup optional, since no code is generated. What makes MSVC broken is when it gives errors for legal code, not when it doesn't give errors for uninstantiated templates. – Gerald Feb 17 '12 at 01:58
  • @Gerald No diagnostic is required for syntax errors. These are semantic errors. So I do believe diagnostics are required. And the examples in the standard seem to support that also. – Paul Fultz II Feb 17 '12 at 03:06
  • @Paul... What? How do you evaluate the semantics of an expression with invalid syntax? What examples in the standard bear this out? – Gerald Feb 17 '12 at 06:55
3

The compiler is only really required to check for any malformed syntax of uninstantiated template declarations. Any additional semantic evaluation only needs to be done when the template function is instantiated.

Since S::P is indeed a type which is valid to be returned from a function, they are both equally conformant.

Gerald
  • 23,011
  • 10
  • 73
  • 102
  • So, is it fair to say that we're simply in the realm of an arbitrary level of error checking as decided by the compiler author and undefined by the standard? – brendanw Feb 16 '12 at 01:44
  • @Gerald The problem is: where do you read this in the standard? I've been searching for an hour now, without success. – Klaim Feb 16 '12 at 01:50
  • @Brendanw - yes. As far as the C++ standard is concerned, this function doesn't exist. But compilers are free to perform additional error checking. All that really matters as far as the standard is concerned is that when the function template is instantiated, it is invalid. In the scenario you describe in your comment under your question, the function template is being implicitly declared when a bad type is passed in to some other template, which results in an error. That is a standards compliant behavior, the rest is compiler dependent. – Gerald Feb 16 '12 at 01:51
  • So, my understanding would be correct in that SFINAE isn't specified in the C++ standard and fall into "undefined compiler behaviour" land, right? How could Boost even rely on this then? – Klaim Feb 16 '12 at 01:56
  • @Klaim - yes. Boost relies on it because it's a pervasive and convenient principle adopted by most compilers. Those are the parts of Boost that will likely never make their way into the standard library, however, for that reason. – Gerald Feb 16 '12 at 02:25
  • 2
    @Klaim: no. SFINAE is specified in the Standard because it applies to instantiation of templates. – Matthieu M. Feb 16 '12 at 07:14
  • 1
    This is somewhat correct if you go by only normative wording of the spec and ignore the committee's intent (in fact the normative words say that once a temlate is ill-formed, *any* behavior is acceptable for a compiler. Including crashing). The committee's intent is that also syntax errors in templates are permitted to be accepted by compilers. They clearly show that in examples in clause 14. However they never formalized it in normative text unfortunately, so one can claim a lot of things about the spec. Based on whether one takes the normative text or the intent of the committee. – Johannes Schaub - litb Feb 16 '12 at 21:43
  • After speaking to some of the MS developers, it's definitely acknowledged that two-phase lookup is half-baked in MSVC, but there seems to be a general consensus that MSVC is still "technically" compliant according to the standard snippet that Gerald posted in Paul's answer. I did get the feeling that it's more an issue of performance and/or laziness than priciple. +1. – brendanw Feb 17 '12 at 01:29
  • 1
    @brendanw... it's true that MSVC's two-phase lookup is not the greatest. Performance is certainly a consideration, but I don't think it's laziness so much as not having genies on their staff (this stuff is hard, which is why NO compiler is completely compliant yet), as well as Microsoft's need to balance strict compliance with practical needs like not breaking the billions of lines of legacy commercial code that go through their compilers. – Gerald Feb 17 '12 at 02:21