What Exactly Is "Broken" With Microsoft Visual C++'S Two-Phase Template Instantiation

What exactly is broken with Microsoft Visual C++'s two-phase template instantiation?

I'll just copy an example from my "notebook"

int foo(void*);

template<typename T> struct S {
S() { int i = foo(0); }
// A standard-compliant compiler is supposed to
// resolve the 'foo(0)' call here (i.e. early) and
// bind it to 'foo(void*)'
};

void foo(int);

int main() {
S<int> s;
// VS2005 will resolve the 'foo(0)' call here (i.e.
// late, during instantiation of 'S::S()') and
// bind it to 'foo(int)', reporting an error in the
// initialization of 'i'
}

The above code is supposed to compile in a standard C++ compiler. However, MSVC (2005 as well as 2010 Express) will report an error because of incorrect implementation of two-phase lookup.


And if you look closer, the issue is actually two-layered. At the surface, it is the obvious fact that Microsoft's compiler fails to perform early (first phase) lookup for a non-dependent expression foo(0). But what it does after that does not really behave as a proper implementation of the second lookup phase.

The language specification clearly states that during the second lookup phase only ADL-nominated namespaces get extended with additional declarations accumulated between the point of definition and point of instantiation. Meanwhile, non-ADL lookup (i.e. ordinary unqualified name lookup) is not extended by the second phase - it still sees those and only those declarations that were visible at the first phase.

That means that in the above example the compiler is not supposed to see void foo(int) at the second phase either. In other words, the MSVC's behavior cannot be described by a mere "MSVC postpones all lookup till the second phase". What MSVC implements is not a proper implementation of the second phase either.

To better illustrate the issue, consider the following example

namespace N {
struct S {};
}

void bar(void *) {}

template <typename T> void foo(T *t) {
bar(t);
}

void bar(N::S *s) {}

int main() {
N::S s;
foo(&s);
}

Note that even though bar(t) call inside the template definition is a dependent expression resolved at the second lookup phase, it should still resolve to void bar(void *). In this case ADL does not help the compiler to find void bar(N::S *s), while the regular unqualified lookup is not supposed to get "extended" by the second phase and thus is not supposed to see void bar(N::S *s) either.

Yet, Microsoft's compiler resolves the call to void bar(N::S *s). This is incorrect.

The problem is still present in its original glory in VS2015.

Two phase name lookup for C++ templates - Why?

They could. This is the way most early implementations of templates
worked, and is still the way the Microsoft compiler worked. It was felt
(in the committee) that this was too error prone; it made it too easy to
accidentally hijack a name, with the instantiation in one translation
unit picking up a local name, rather than the desired global symbol. (A
typical translation unit will consist of a sequence of #includes,
declaring the names that everyone should see, followed by implementation
code. At the point of instantiation, everything preceding the point of
instantation is visible, including implementation code.)

The final decision was to classify the symbols in a template into two
categories: dependent and non-dependent, and to insist that the
non-dependent symbols be resolved at the point of definition of the
template, to reduce the risk of them accidentally being bound to some
local implementation symbols. Coupled with the requirement to specify
typename and template when appropriate for dependent symbols, this
also allows parsing and some error checking at the point of definition
of the template, rather than only when the template is instantiated.

Two phase lookup - explanation needed

Templates are compiled (at least) twice:

  1. Without Instantiation the template code itself is checked for syntax.

    Eg: Any syntax errors such as ; etc.

  2. At the time of instantiation(when the exact type is known), the template code is checked again to ensure all calls are valid for that particular type.

    Eg: The template might in turn call to functions which might not be present for that particular type.

This is called as Two Phase Lookup.

VS2017 compiles code that is syntactically incorrect, Intellisence missing

This is a known issue with Visual C++ that has existed for a long time. It does not implement two-phase lookup. It basically just completely skips over templates until they are instantiated. Apparently, they finally fixed it (at least partially).

https://blogs.msdn.microsoft.com/vcblog/2017/09/11/two-phase-name-lookup-support-comes-to-msvc/

C++: syntax error detected by MSVC only using Boost::asio

Can you try this->io_service.run()?

MSVC has been challenged with two-phase lookup for a very long time.

  • What exactly is "broken" with Microsoft Visual C++'s two-phase template instantiation?
  • http://blog.llvm.org/2009/12/dreaded-two-phase-name-lookup.html
  • Two-phase name lookup support comes to MSVC (2017)

Perhaps that's it. What this-> makes explicit is that io_service represents a class member. In the lambda shown it shouldn't usually matter (except perhaps if the class is actually declared [as part of] a template?).

Another factor at play could be name shadowing: io_service shadows the name of the type. If that type name is visible without namespace qualification, then it might cause confusion to MSVC.

Instantation lazyness rules for C++ templates

Totally incorrect. There is only one stage of template
instantiation. When (and where, which is an important point)
depends on how it is used. I think you're confounding name
lookup with instantiation.

Name lookup is done in two phases. (At least if the compiler
is conform—VC++ still gets this wrong.) The first occurs
when the compiler parsed the template definition; the second
when the template is instantiated. Whether a symbol is looked
up in the first phase or the second depends on whether it is
dependent or not. The rules determining this are fairly
complicated, but in general:

  • If it is a function name, and one or more of its arguments
    depend on a template argument, the function name is dependent,
    and it will be looked up (and function overload resolution) will
    occur on instantiation, and not before.

  • If the name is qualified by a template argument (e.g.
    T::something, where T is a template argument), it is
    dependent.

  • In a member of a class template, if there is a dependent base
    class (a base class which in some way depends on the template
    argument), anything to the right of this-> is dependent.

(The actual rules are considerably more complicated, but the
above is a rough approximation, and probably sufficient for most
uses.)

EDIT:

Just some examples of the difference:

class MyType {};

void func0( double d )
{
std::cout << "called func0( double )" << std::endl;
}

void func1( double, MyType const& )
{
std::cout << "called func1( double )" << std::endl;
}

template <typename T>
int funcT( T const& param )
{
func0( 42 ); // non-dependent
func1( 42, param ); // dependent
}

void func0( int d )
{
std::cout << "called func0( int )" << std::endl;
}

void func1( int, MyType const& )
{
std::cout << "called func1( int )" << std::endl;
}

int
main()
{
funcT( MyType() );
return 0;
}

The output should be

called func0( double )
called func1( int )

The call to func0 in funcT is non-dependent, so name lookup
occurs only at the point where the template was defined, and
not later, when it is instantiated. At that point, there is
only one func0, so it gets called.

The call to func1 infuncT is dependent (since its second
argument depends on the template argument), so there will be an
additional name lookup at the point of instantiation
(immediately following main, in this case). This additional
lookup only uses ADN, but since one of the arguments is defined
in global namespace, ADN will look in global namespace, and find
the second declaration of func1 as well (which will then be
chosen by overload resolution, because it is a better match).

Note that I can't verify this, because at the moment, I only
have access to VC++11, which is broken. And it is subtle, so
it's quite possible I've missed something. The general rule is
to avoid such ambiguities.

Note that if func0 were not declared before the template
definition, the code should not compile. From experience, when
moving from pre-standard compilers (or VC++), this is the most
common cause of errors.

Another common case where this sometimes surprises:

template <typename Base>
class Derived : public Base
{
public:
Derived()
{
init(); // Non-dependent lookup, will NOT find any
// function init() in Base!!!
this->init() // Dependent lookup, WILL find
// Base::init, if it exists.
Base::init() // Also dependent.
}
};

Typically, if you're using this pattern, and developing on
a pre-standard compiler like VC++, you'll get a number of
compiler errors when you move to a more modern compiler.
They're easily fixed by adding the this->. However, if
there is also a global function init visible when the template
is defined, you will call this, and not the function in the
base class. If you choose meaningful names for your functions,
and put global functions in namespaces, you'll probably not get
hit very often by this silent change of semantics, but bewarned.

Clang vs MSVC: Treatment of template function prototypes

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.



Related Topics



Leave a reply



Submit