Living with Microsoft C++ Compiler Bugs and Ambiguities

It’s no secret that the Microsoft Visual C++ compiler has lots of non-standard behaviors. What’s even more unfortunate is that those behaviors are subsequently used by different libraries. Ultimately, for a tool vendor there’s no option in not supporting all of those peculiarities. Here are a few that we’ve come across:

Eliminated Types

In addition to using C++ macros to generate actual executable code, you can exploit a Microsoft-specific peculiarity to generate comments as well. This is possible thanks to simple concatenation. Define a CONCAT macro as follows:

Now, simply concatenating two slashes together generates a // line comment:

This shouldn’t really work because the result of the ## operator must be a valid token. Similarly, you can make a block comment. However, you cannot terminate a macro comment with a macro call, only with */:

So why is this relevant? Well because, if you look at e.g., the <oaidl.h> header, you will see declarations similar to the following:

Depending on the version of the compiler, _VARIANT_BOOL will either generate a comment or a ‘variant bool’ type:

__VA_ARGS__ Chaos

Owing to a bug in Microsoft compiler’s refusal to expand __VA_ARGS__ into an argument list, the following definition is ambiguous:

Now consider the statement printf("%d\n", A_VA(1, 2));. On GCC, Clang and similar, this would correctly expand to

However, the Microsoft compiler would instead expand it to

Interestingly, Microsoft does not admit this is a bug. I guess it’s easier that way – if it’s not a bug, no need to fix it, right?

On the other hand, the Boost library is aware of this bug, so it checks explicitly whether the compiler is MSVC and, if it is, it tries to do things differently. On the ReSharper end, even though ReSharper C++ is adapted to MSVC (e.g., in terms of MSC_VER and MSC_FULL_VER definitions), we try to handle the VA_ARGS case as standard-compliant compilers which, unfortunately, leads to errors when parsing Boost.

ReSharper C++ meanwhile knows how to handle the MSVC case too, and this is precisely what we do when parsing the Microsoft standard libraries. However, if we adopt the MS-centric approach to all libraries, this would cause ReSharper C++ to fail in all sorts of cross-platform projects which are edited in VS but are built using NMake. And we cannot behave in a Microsoft-compliant way in Boost because errors occur not only in Boost itself but also in locations where those affected constructs are being used. So this is why in some Boost files ReSharper C++ pretends to be Clang and not MSVC.

Binding Rvalues to Lvalue References

This is an error according to the C++ standard, but the Microsoft compiler allows the binding of rvalues to lvalue references via an extension. Here is an example:

The code above will not compile on either GCC or Clang.

So you might think that extending support for this situation in ReSharper is not a problem. Well, it turns out, things are a little bit trickier. Consider the following code:

Which overload does main() call? The C++ standard says that some_func(Y) must be chosen, since the overload binds an rvalue to an lvalue reference, which is not allowed. But if this situation is allowed, some_func(X&) must be chosen instead. The problem is that this would make a standard-compliant program behave differently under MSVC, so in this specific case the conversion is disallowed.

So, after some trial and error, we found a proper way to handle this: we first do standard-compliant overloading, i.e., disallowing the binding of rvalues to lvalue references. Then, if and only if we fail to find a viable function, we check the overload again, this time allowing the binding to happen.

As you can see, the tricky part here is figuring out how various MSVC extensions interact with the C++ standard and the way they are implemented. Expect a blog post on them too, soon!

Dreaded Two-Phase Lookup

The C++ standard states that lookup in template code must happen in two phases:

  • The first phase happens when the code of the template is parsed. All non-dependent names (those that do not depend on template parameters) are looked up straight away.
  • The second phase occurs when the template is being instantiated and this is when all dependent names are being looked up.

So, for example, the following code must produce an error according to the C++ standard.

This code should not compile because the variable a is not a dependent name and should be found at the declaration parsing stage. However, this is not the case with MSVC, which will happily compile the code and complain only when X is instantiated and method() is called, i.e. in something like

But now things get even trickier, because if we declare a after the template declaration but before instantiation, everything works fine in MSVC, but must also fail according to the C++ standard:

It gets even more confusing when base classes are involved:

This code compiles without problem in MSVC, but should result in an error. The workaround to make the code standard-compliant is to access the variable b as:

So in ReSharper, we use the standard-compliant version of the lookup, so we show an error when accessing b without the this-> qualification:

ReSharper C++ unqualified dependent lookup error

However, a large number of programs written for MSVC adopt this style. And why wouldn’t they? After all, this seems like a very natural way to write code and, without proper compiler diagnostics, they would never have a clue that the code is not standard-compliant.

This is why we show warnings for these cases instead of errors by default. However, this behavior can be changed in ReSharper C++ settings by going into ReSharper settings (Code Editing → C++ → Inspections) and unchecking the appropriate flag:

ReSharper C++ Treat resolve errors as warnings in template code

We highly encourage everyone willing to write standard-compliant code to uncheck this flag!

In Closing

This may have been a bit of a discouraging post with respect to MSVC, but do not worry — in a subsequent post we’ll take a look at MSVC-specific constructs which, while not necessarily standard-compliant, are nevertheless interesting and often quite usable. ■

This entry was posted in ReSharper C++ Tips & Tricks and tagged , . Bookmark the permalink.

11 Responses to Living with Microsoft C++ Compiler Bugs and Ambiguities

  1. Predelnik says:

    I find warnings about templates astonishingly useful while compiling cross-platform code. However I’ve seen some case where Resharper actually agreed with VC saying that template keyword is redudant where it was required by gcc :)

    Also somehow I can’t reproduce your example about overloading with rvalue, for me VC calls ‘some_func(Y)’ fine.

  2. Anon says:

    Something is swallowing your angle brackets, or else your code can’t ever compile.

  3. Ras says:

    Formatting in the “It gets even more confusing when base classes are involved:” code snippet seems off. There’s just “template”, without “typename X”, and there’s a closing “b” tag at the end.

  4. Yetanotherfrank says:

    Regarding two-phase name lookup I am of the unpopular opinion that it is the standard that is broken, and the MS compiler (which existed before the standard) does the meaningful thing.

    Whether a (base) class member can be accessed without this-> or not should not depend on whether the base class has a template parameter or not. Those things have nothing to do with each other. I pray that MS will continue to support their original behavior.

    Currently, to make it build with gcc, I work around two-phase name lookup by macros that simply import all base class members through the using directive, as I really would not want to pollute my code with this-> all over the place. C++ is ugly enough.

  5. FakeUserName says:

    Do you have a cached copy of that bug report regarding __VA_ARGS__? The link that you gave prompts me for creds for login.live.com; when I log in, I get a generic “page not found” page.

  6. Herb Sutter says:

    Thanks for the summary! We (the Visual C++ team) are working our way toward full ISO C++ conformance, and you can find the cheat sheet for what remains to be done here: http://blogs.msdn.com/b/vcblog/archive/2015/06/19/c-11-14-17-features-in-vs-2015-rtm.aspx (as of VS 2015 RTM).

    We’re most of the way there now, but you’re hitting some of the final tail items we still need to do for full conformance. The first two issues are about phases of translation, “preprocessor” on our list. The issues of binding rvalues to lvalue references and two-phase lookup are known pain points that we would have fixed already, but depends on the compiler rejuvenation work. We hope to get to it very soon (but not in VS 2015 Update 1).

    By the way, each of these changes will impact customers who depend on the current behavior, so we’ll also do extra work to migrate them through that transition to the standard behavior as painlessly as possible, using the usual approaches: temporary compatibility switches during migration, making nonstandard constructs emit warnings first then errors in the future, etc.

    • Mikhail Senkov says:

      Hi Herb! Visual C++ compiler has made very impressive improvements in the last few years and we are happy to know
      you are going to fix “binding rvalues to lvalue references” and “two-phase lookup” issues soon.

      Anyway we also need to support old behaviour as we support older versions of msvc.

Leave a Reply

Your email address will not be published. Required fields are marked *