{"id":16445,"date":"2017-07-21T14:00:00","date_gmt":"2017-07-21T21:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/vcblog\/?p=16445"},"modified":"2019-08-01T00:58:54","modified_gmt":"2019-08-01T00:58:54","slug":"diagnostic-improvements-in-vs2017-15-3-0","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/cppblog\/diagnostic-improvements-in-vs2017-15-3-0\/","title":{"rendered":"Diagnostic Improvements in Visual Studio 2017 15.3.0"},"content":{"rendered":"<p><em>This post as well as described diagnostics significantly benefited from the feedback by Mark, <a href=\"https:\/\/blogs.msdn.microsoft.com\/xiangfan\/\">Xiang<\/a>, Stephan, Marian, Gabriel, <a href=\"https:\/\/blogs.msdn.microsoft.com\/vcblog\/author\/ulziilive-com\/\">Ulzii<\/a>, Steve and <a href=\"https:\/\/blogs.msdn.microsoft.com\/vcblog\/author\/apardoelive-com\/\">Andrew<\/a>.<\/em><\/p>\n<p><a href=\"https:\/\/www.visualstudio.com\/vs\/preview\/\">Visual Studio 2017 15.3.0<\/a> release comes with a number of improvements to the Microsoft Visual C++ compiler&#8217;s diagnostics. Most of these improvements are in response to the <a href=\"https:\/\/www.reddit.com\/r\/cpp\/comments\/5chnm4\/visual_c_team_is_interested_in_your_feedback_for\/\">diagnostics improvements survey<\/a> we shared with you at the beginning of the 15.3 development cycle. Below you will find some of the new or improved diagnostic messages in the areas of member initialization, enumerations, dealing with precompiled headers, conditionals, and more. We will continue this work throughout VS 2017.<\/p>\n<h3>Order of Members Initialization<\/h3>\n<p>A constructor won&#8217;t initialize members in the order their initializers are listed in code, but in the order the members are declared in class. There are multiple potential problems that can stem from assuming that actual initialization order will match the code. A typical scenario is when the member initialized earlier in the list is used in a later initializer, while the actual initialization happens in the reverse order because of the order of declaration of those members.<\/p>\n<pre class=\"lang:c++ decode:true\" title=\"C5038 Example: order of members initialization\">\/\/ Compile with \/w15038 to enable the warning\r\nstruct B : A\r\n{\r\n    B(int n) : b(n), A(b) {} \/\/ warning C5038: data member 'B::b' will be initialized after base class 'A'\r\n    int b;\r\n};<\/pre>\n<p>The above warning is off-by-default in the current release due to the amount of code it breaks in numerous projects that treat warnings as errors. We plan to enable the warning by default in a subsequent release, so we recommend to try enabling it early.<\/p>\n<h3>Constant Conditionals<\/h3>\n<p>There were a few suggestions to adopt the practice popularized by Clang of suppressing certain warnings when extra parentheses are used. We looked at it in the context of one bug report suggesting that we should suppress &#8220;warning C4127: conditional expression is constant&#8221; when the user puts extra () (note that Clang itself doesn&#8217;t apply the practice to this case). While we discussed the possibility, we decided this would be a disservice to good programming practices in the context of this warning as the language and our implementation now supports the &#8216;if constexpr&#8217; statement. Instead, we now recommend using &#8216;if constexpr&#8217;.<\/p>\n<pre class=\"lang:c++ decode:true \" title=\"C4127 Example: constant conditionals\">if ((sizeof(T) &lt; sizeof(U))) \u2026\r\n\/\/ warning C4127 : conditional expression is constant\r\n\/\/ note : consider using 'if constexpr' statement instead<\/pre>\n<h3>Scoped Enumerations<\/h3>\n<p>One reason scoped enumerations (aka enum classes) are preferred is because they have stricter type-checking rules than unscoped enumerations and thus provide better type safety.\u00a0 We were breaking that type safety in switch statements by allowing developers to accidently mix enumeration types. This often resulted in unexpected runtime behavior:<\/p>\n<pre class=\"lang:c++ decode:true\" title=\"Problem with scoped enumerations\">enum class A { a1, a2 };\r\nenum class B { baz, foo, a2 };\r\nint f(A a) {\r\n    switch (a)\r\n    {\r\n    case B::baz: return 1;\r\n    case B::a2:  return 2;\r\n    }\r\n    return 0;\r\n}<\/pre>\n<p>In <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/reference\/permissive-standards-conformance\">\/permissive-<\/a> mode (again, due to the amount of code this broke) we now emit errors:<\/p>\n<pre class=\"lang:default decode:true \" title=\"C2440 Example: illegal conversion to scoped enum\">error C2440: 'type cast': cannot convert from 'int' to 'A'\r\nnote: Conversion to enumeration type requires an explicit cast (static_cast, C-style cast or function-style cast)\r\nerror C2046: illegal case<\/pre>\n<p>The error will also be emitted on pointer conversions in a switch statement.<\/p>\n<h3>Empty Declarations<\/h3>\n<p>We used to ignore empty declarations without any diagnostics, assuming they were pretty harmless. Then we came across a couple of usage examples where users used empty declarations on templates in some complicated template-metaprogramming code with the assumption that those would lead to instantiations of the type of the empty declaration. This was never the case and thus was worth notifying about. In this update we reused the warning that was already happening in similar contexts, but in the next update we&#8217;ll change it to its own warning.<\/p>\n<pre class=\"lang:c++ decode:true \" title=\"C4091 Example: empty declarations\">struct A { \u2026 };\r\nA; \/\/ warning C4091 : '' : ignored on left of 'A' when no variable is declared<\/pre>\n<h3>Precompiled Headers<\/h3>\n<p>We had a number of issues in large projects arising from the use of precompiled headers on very large projects. The issues weren&#8217;t compiler-specific per se, but rather dependent on processes happening in the operating system. Unfortunately, our one error fits all for this scenario was inadequate for the users to troubleshoot the problem and come up with a suitable workaround. We expanded the information that the errors contained in these cases in order to be better able to identify a specific scenario that could have led to the error and advise users on the ways to address the issue.<\/p>\n<pre class=\"lang:default decode:true\" title=\"C3859 Example: PCH issues\">error C3859: virtual memory range for PCH exceeded; please recompile with a command line option of '-Zm13' or greater\r\nnote: PCH: Unable to get the requested block of memory\r\nnote: System returned code 1455: The paging file is too small for this operation to complete\r\nnote: please visit https:\/\/aka.ms\/pch-help for more details\r\nfatal error C1076: compiler limit: internal heap limit reached; use \/Zm to specify a higher limit<\/pre>\n<p>The broader issue is discussed in greater details in our earlier blog post: <a href=\"https:\/\/blogs.msdn.microsoft.com\/vcblog\/2017\/07\/13\/precompiled-header-pch-issues-and-recommendations\/\">Precompiled Header (PCH) issues and recommendations<\/a><\/p>\n<h3>Conditional Operator<\/h3>\n<p>The last group of new diagnostic messages are all related to our improvements to the conformance of the conditional operator ?:. These changes are also opt-in and are guarded by the switch <a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/reference\/zc-conformance\">\/Zc:ternary<\/a> (implied by \/permissive-) due to the amount of code they broke. In particular, the compiler used to accept arguments in the conditional operator ?: that are considered ambiguous by the standard (see section [expr.cond]). We no longer accept them under \/Zc:ternary or \/permissive- and you might see new errors appearing in source code that compiles clean without these flags.<\/p>\n<p>The typical code pattern this change breaks is when some class U both provides a constructor from another type T and a conversion operator to type T (both non-explicit). In this case both the conversion of the 2nd argument to the type of the 3rd and the conversion of the 3rd argument to the type of the 2nd are valid conversions, which is ambiguous according to the standard.<\/p>\n<pre class=\"lang:c++ decode:true \" title=\"C2445 Example: conditional operator conformance\">struct A\r\n{\r\nA(int);\r\noperator int() const;\r\n};\r\n\r\nA a(42);\r\nauto x = cond ? 7 : a; \/\/ A: old permissive behavior prefers A(7) over (int)a.\r\n\/\/ The non-permissive behavior issues:\r\n\/\/ error C2445: result type of conditional expression is ambiguous: types 'int' and 'A' can be converted to multiple common types\r\n\/\/ note: could be 'int'\r\n\/\/ note: or 'A'<\/pre>\n<p>To fix the code, simply cast one of the arguments explicitly to the type of the other.<\/p>\n<p>There is one important exception to this common pattern when T represents one of the null-terminated string types (e.g. const char*, const char16_t* etc., but you can also reproduce this with array types and the pointer types they decay to) and the actual argument to ?: is a string literal of corresponding type. C++17 has changed the wording, which led to change in semantics from C++14 (see <a href=\"http:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/cwg_defects.html#1805\">CWG defect 1805<\/a>). As a result, the code in the following example is <a href=\"https:\/\/devblogs.microsoft.com\/cppblog\/standards-version-switches-in-the-compiler\/\"> accepted under \/std:c++14 and rejected under \/std:c++17<\/a>:<\/p>\n<pre class=\"lang:c++ decode:true \" title=\"Conditional conformance fix example\">struct MyString\r\n{\r\nMyString(const char* s = \"\") noexcept; \/\/ from const char*\r\noperator const char*() const noexcept; \/\/ to const char*\r\n};\r\nMyString s;\r\nauto x = cond ? \"A\" : s; \/\/ MyString: permissive behavior prefers MyString(\"A\") over (const char*)s<\/pre>\n<p>The fix is again to cast one of the arguments explicitly.<\/p>\n<p>In the original example that triggered our conditional operator conformance work, we were giving an error where the user was not expecting it, without describing why we give an error:<\/p>\n<pre class=\"lang:c++ decode:true\" title=\"C2446 example\">auto p1 = [](int a, int b) { return a &gt; b; };\r\nauto p2 = [](int a, int b) { return a &gt; b; };\r\nauto p3 = x ? p1 : p2; \/\/ This line used to emit an obscure error:<\/pre>\n<pre class=\"lang:default decode:true\" title=\"Obscure diagnostics\">error C2446: ':': no conversion from 'foo::&lt;lambda_f6cd18702c42f6cd636bfee362b37033&gt;' to 'foo::&lt;lambda_717fca3fc65510deea10bc47e2b06be4&gt;'\r\nnote: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called<\/pre>\n<p>With \/Zc:ternary the reason for failure becomes clear even though some people might still not like that we chose not to give preference to any particular (implementation-defined) calling convention on architectures where we support multiple:<\/p>\n<pre class=\"lang:default decode:true  \" title=\"Improved diagnostics\">error C2593: 'operator ?' is ambiguous\r\nnote: could be 'built-in C++ operator?(bool (__cdecl *)(int,int), bool (__cdecl *)(int,int))'\r\nnote: or 'built-in C++ operator?(bool (__stdcall *)(int,int), bool (__stdcall *)(int,int))'\r\nnote: or 'built-in C++ operator?(bool (__fastcall *)(int,int), bool (__fastcall *)(int,int))'\r\nnote: or 'built-in C++ operator?(bool (__vectorcall *)(int,int), bool (__vectorcall *)(int,int))'\r\nnote: while trying to match the argument list '(foo::&lt;lambda_717fca3fc65510deea10bc47e2b06be4&gt;, foo::&lt;lambda_f6cd18702c42f6cd636bfee362b37033&gt;)'<\/pre>\n<p>Another scenario where one would encounter errors under \/Zc:ternary are conditional operators with only one of the arguments being of type void (while the other is not a throw expression). A common use of these in our experience of fixing the source code this change broke was in ASSERT-like macros:<\/p>\n<pre class=\"lang:c++ decode:true \" title=\"C3447 Example\">void myassert(const char* text, const char* file, int line);\r\n#define ASSERT(ex) (void)((ex) ? 0 : myassert(#ex, __FILE__, __LINE__))\r\n\r\nerror C3447: third operand to the conditional operator ?: is of type 'void', but the second operand is neither a throw-expression nor of type 'void'<\/pre>\n<p>The typical solution is to simply replace the non-void argument with void().<\/p>\n<p>A bigger source of problems related to \/Zc:ternary might be coming from the use of the conditional operator in template meta-programming as some of the result types would change under this switch. The following example demonstrates change of conditional expression&#8217;s result type in a non-meta-programming context:<\/p>\n<pre class=\"lang:c++ decode:true \" title=\"Changes due to conditional conformance\">char a = 'A';\r\nconst char b = 'B';\r\ndecltype(auto) x = cond ? a : b; \/\/ char without, const char&amp; with \/Zc:ternary\r\nconst char(&amp;z)[2] = argc &gt; 3 ? \"A\" : \"B\"; \/\/ const char* without \/Zc:ternary<\/pre>\n<p>The typical resolution in such cases would be to apply a std::remove_reference trait on top of the result type where needed in order to preserve the old behavior.<\/p>\n<h3>In Closing<\/h3>\n<p>You can try these improvements today by downloading <a href=\"https:\/\/www.visualstudio.com\/vs\/preview\">Visual Studio 2017 15.3.0 Preview<\/a>. As always, we welcome your feedback \u2013 it helps us prioritize our work as well as the rest of the community is resolving similar issues. Feel free to send any comments through e-mail at visualcpp@microsoft.com, Twitter <a href=\"https:\/\/twitter.com\/visualc\">@visualc<\/a>, or Facebook at <a href=\"https:\/\/www.facebook.com\/Microsoft-Visual-Cpp-222043184527264\/\">Microsoft Visual Cpp<\/a>. If you haven&#8217;t done so yet, please check also our <a href=\"https:\/\/devblogs.microsoft.com\/cppblog\/c-compiler-diagnostics-improvements-in-vs-15-rc\/\">previous post<\/a> in the series documenting our progress on improving compiler diagnostics.<\/p>\n<p>If you encounter other problems with MSVC in VS 2017 please let us know via the <a href=\"https:\/\/docs.microsoft.com\/en-us\/visualstudio\/ide\/how-to-report-a-problem-with-visual-studio-2017\">Report a Problem<\/a> option, either from the installer or the Visual Studio IDE itself. For suggestions, let us know through <a href=\"https:\/\/visualstudio.uservoice.com\/forums\/121579-visual-studio-2015\/category\/30937-languages-c\">UserVoice<\/a>.<\/p>\n<p>Thank you!\nYuriy<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post as well as described diagnostics significantly benefited from the feedback by Mark, Xiang, Stephan, Marian, Gabriel, Ulzii, Steve and Andrew. Visual Studio 2017 15.3.0 release comes with a number of improvements to the Microsoft Visual C++ compiler&#8217;s diagnostics. Most of these improvements are in response to the diagnostics improvements survey we shared with [&hellip;]<\/p>\n","protected":false},"author":6384,"featured_media":35994,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[239,277],"tags":[140,281,194],"class_list":["post-16445","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-diagnostics","category-writing-code","tag-c","tag-conformance","tag-diagnostics"],"acf":[],"blog_post_summary":"<p>This post as well as described diagnostics significantly benefited from the feedback by Mark, Xiang, Stephan, Marian, Gabriel, Ulzii, Steve and Andrew. Visual Studio 2017 15.3.0 release comes with a number of improvements to the Microsoft Visual C++ compiler&#8217;s diagnostics. Most of these improvements are in response to the diagnostics improvements survey we shared with [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/16445","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/users\/6384"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/comments?post=16445"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/posts\/16445\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media\/35994"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/media?parent=16445"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/categories?post=16445"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/cppblog\/wp-json\/wp\/v2\/tags?post=16445"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}