Quantcast
Channel: Category Name
Viewing all articles
Browse latest Browse all 5971

Give Visual C++ a Switch to Standard Conformance

$
0
0

This post was written by Gabriel Dos Reis, Phil Christensen, and Andrew Pardoe

The Visual C++ Team is excited to announce that the compiler in Visual Studio 2017 RC will feature a mode much closer to ISO C++ standards conformance than any time in its history. This represents a major milestone in our journey to full ISO C++ conformance.  The mode is available now as an opt-in via the /permissive- switch but will one day become the default mode for the Visual C++ compiler.

Got a minute? Try Visual C++ conformance mode

The Visual C++ Team is previewing a compiler mode whereby longstanding non-conforming C++ constructs are rejected.  This includes fixes to pre-C++11 non-conformance bugs that affect a significant amount of existing code.  Examples are as simple as

typedef int default;  // error: ‘default’ is a keyword, not permitted here

or as advanced as the following.  Consider:

template
struct B {
    int f();
};

template
struct D : B {
    int g();
};

template
int D::g() {
    return f();  // error: should be ‘this->f()’
}

In the definition of D::g, the symbol f is from the dependent base class B but standard C++ does not permit examining dependent base classes when looking for declarations that satisfy the use of f. That is an error in the source code that Visual C++ has long failed to diagnose. The fix is to prefix the call with this->. This fix works in both non-conformance and conformance modes.

The issue in this code sample might sound like a “two-phase name lookup” problem, but it is not quite that. In fact, two phase name lookup is the big missing piece from the first iteration of this standard conformance mode.  Visual C++ does not support two-phase name lookup in VS 2017 RC but we expect to complete it early next year. When we do, the definition of /permissive- will change to include two-phase name lookup. This is an important point: /permissive- is not a completed feature in VS 2017 RC. You will have to keep your codebase clean as we complete our conformance work in Visual C++.

Opting into the conforming mode, /permissive-, during the series of VS 2017 update is a commitment to keeping your code base clean and to fixing non-conforming constructs we fix conformance issues in Visual C++. If you make your code compile with /permissive- today you may still have to make changes when VS 2017 releases, and again with updates to VS 2017 because we may have added more bug fixes for non-conforming behaviors.

The good news is this first release contains most of the big issues. With the exception of two-phase name lookup we don’t expect any major changes. See How to fix your code for a complete list of non-conforming constructs implemented under /permissive- today and how to fix them. We’ll update this list as we complete the changes.

From the Visual Studio IDE

In Visual Studio 2017 RC there is no setting in the project properties page for /permissive-. You’ll need to enter the switch manually under “Configuration -> C/C++ -> Additional Options”.

permissive_settings

When compiling with /permissive-, the IDE compiler disables non-conforming Visual C++-specific behaviors and interprets your code following the C++ standard. You’ll notice that these language conformance features are now reflected in the areas of IDE such as IntelliSense and browsing features. In this example, setting /permissive- causes IntelliSense to flag default as an illegal identifier.

permissive1

There are currently a few places where the IntelliSense in the IDE will conform better to the C++ standard than the Visual C++ compiler.  For example, when relying on two-phase name lookup during overload resolution the compiler will emit an error.  IntelliSense, however, will show that the code is conforming and will not give any red squiggles in the editor.  You can see that happening in the screenshot below:

permissive2

Why is that? You may know that Visual C++ uses a separate compiler for IDE productivity features. Since around 2009 we’ve used a compiler front-end from the Edison Design Group (EDG) to drive IntelliSense and many other features. EDG does a fantastic job at emulating other compilers–if you use Clang in Visual Studio (either through Clang/C2 or  when targeting Android or iOS) we put EDG into “Clang mode” where it emulates Clang’s bugs and vendor-specific behaviors. Likewise, when using Visual C++, we put EDG into “Visual C++” mode.

While EDG emulates most VC++ features and bugs faithfully, there still might be some gaps due to the implementation being separate or how we integrated their compiler in the IDE.  For example, we’ve implemented some features such as ignoring names from dependent base classes, but we haven’t yet implemented others, such as two-phase name lookup (“two-phase overload resolution” on this page) that are both implemented in EDG. Thus EDG shows the “complete” results IntelliSense even though the Visual C++ compiler doesn’t compile the code correctly.

Any differences you see in the IDE and the compiler are temporary. As we complete our conformance work the IDE and the compiler behaviors will match on all code.

Is there any relation to /Za?

The compiler switch /Za was an effort started decades ago to carve out a strictly portable behavior across several C++ compilers. The effort was stalled and we no longer recommend it for new projects. The switch /Za does not support certain key Microsoft SDK header files. By contrast /permissive- offers a useful conformance mode where input C++ code is interpreted according to ISO C++ rules but also allows conforming extensions necessary to compile C++ on targets supported by Visual C++. For example, you can use /permissive- with C++/CLI. The compiler switch /Za rejects a few non-conforming constructs; however the compiler switch /permissive- is our recommendation going forward for conforming code.

The road ahead

We know that conformance changes can be impactful to your code. In order to provide the best experience we’re splitting the development of /permissive- into three stages: the development stage, the adoption stage, on-by-default.

Development stage

The initial preview of /permissive- allows you to try out the switch and make most of the changes that will be required in your code. At this stage there will be some inconsistencies in the experience: the feature set under /permissive- will grow slightly, and editor features like IntelliSense may not match exactly. But all the changes you make to your code in this stage will work with and without /permissive-.

Adoption stage

The Visual C++ team will continue to add new conformance behavior under the /permissive- option throughout the Visual Studio 2017 release cycle. But we know that you rely on libraries and that those libraries also need to work with /permissive- in order for you to opt-in. Our first priority is to ensure that all public headers from our team, and those from Microsoft as a whole, compile cleanly with the /permissive- option. For example, the standard library headers compile correctly both with /permissive- and without. We are also working with the community to make changes to existing open source C++ projects (this typically involves removal of Visual C++-specific workarounds.)

On by default

When developers have had time to migrate their code to conform more closely to the C++ standards we will flip the default so that the compiler applies full ISO C++ standard rules when interpreting your source code. This won’t happen immediately–it’s a long-term goal for our conformance work. Until we make this switch—and at least for the entire Visual Studio 2017 cycle–you will have to opt-in to /permissive-. When we do switch it on by default your existing projects won’t change, but VS will make new projects opt-in to conformance.

Call to action

We encourage you to try the new option /permissive- in your projects. Please add this option to your build settings–either in the IDE or in your build scripts. If your code is already used cross-platform (i.e., it compiles with multiple compilers on many platforms) and does not contain Visual C++ specific workarounds, the chances are that the code will compile just fine with /permissive-!

If you do use Visual C++ specific, non-conforming constructs in your code, chances are that your code won’t compile the first time with /permissive-. There’s a list of patterns below of non-conforming C++ code, the typical error message/number you’ll see, and how to fix the code.  The good news is that the fixes work both in the conforming mode and in the permissive mode. Therefore, if you try to compile with /permissive- and you correct issues, the corrected code will be good for the permissive mode too. This enables you to migrate your code to /permissive- incrementally, rather than making all of the changes at once.

How to fix your code

Here’s a list of behaviors that are affected by the permissive option today. Note that this list is incomplete because the /permissive- switch is incomplete. As we add new conforming behaviors the potential breaking changes protected by /permissive- will expand. This means even if you fix your code to work with /permissive- today you may still have to make fixes in the future as we make Visual C++ conform better to the standard. Again, these changes will only affect your code if you opt-in by using the /permissive- switch.

Lookup members in dependent base

template  struct B {
  void f();
};
template  struct D
    : public B // B is a dependent base because its type depends on the type of T.
{
    // One possible fix is to uncomment the following line.  If this were a type don't forget the 'typename' keyword
    // using B::f;
void g() {
      f(); // error C3861: 'f': identifier not found
           // change it to 'this->f();'
    }
};
template  struct C
    : public B
{
    C()
      : B() // error C2955: 'B': use of class template requires template argument list
               // Change to B() to fix
    {}
};
void h()
{
   D d;
   d.g();
   C c;
}

Use of qualified names in member declarations

struct A {
    void A::f() { } // error C4596: illegal qualified name in member declaration
                    // remove redundant 'A::' to fix
};

Initializing multiple union members in a member initializer

union U
{
  U() 
    : i(1), j(1) // error C3442: Initializing multiple members of union: 'U::i' and 'U::j'
                 // Remove all but one of the initializations
  {}
  int i;
  int j;
};

Hidden friend name lookup rules

// Example 1
struct S {
friend void f(S *);
};
// Uncomment this declaration to make the hidden friend visible
//void f(S *); // this declaration makes the hidden friend visible
using type = void (*)(S *);
type p = &f; //error C2065: 'f': undeclared identifier

/*--------------------------------------------------------*/

// Example 2
struct S {
friend void f(S *);
};
 
void g() { 
    // using nullptr instead of S prevents argument dependent lookup in S
    f(nullptr); // error C3861: 'f': identifier not found
    
S *p = nullptr;
f( S ); // hidden friend can be found via argument-dependent lookup.
} 

Using scoped enums in array bounds

enum class Color 
{ 
    Red, Green, Blue 
};
int data[Color::Blue];// error C3411: 'Color' is not valid as the size of an array as it is not an integer type
                      // Cast to type size_t or int

Using ‘default’ as an identifier in native code

void func(int default); // Error C2321: 'default' is a keyword, and cannot be used in this context

‘for each’ in native code

void func()
{
   int array[] = {1, 2, 30, 40};
   for each( int i in array) // error C4496: nonstandard extension 'for each' used: replace with ranged-for statement
                             // for( int i: array)
   {
       // ... 
   }
}

Defaulting conformance switches

The compiler switches /Zc:strictStrings and /Zc:rvalueCast are currently off by default, allowing non-conforming behavior. The switch /permissive- turns them on by default. You can pass in the /Zc flags after /permissive- to override this behavior if needed.

See these MSDN pages for more information:

  • /Zc:strictStrings https://msdn.microsoft.com/en-us/library/dn449508.aspx
  • /Zc:rvalueCast https://msdn.microsoft.com/en-us/library/dn449507.aspx

ATL attributes

We started to deprecate attributed ATL support in VS 2008. The /permissive- switch removes support for attributed ATL.

// Example 1
[uuid("594382D9-44B0-461A-8DE3-E06A3E73C5EB")]
class A {};
// Fix for example 1
class __declspec(uuid("594382D9-44B0-461A-8DE3-E06A3E73C5EB")) B {};
// Example 2
[emitidl];
[module(name="Foo")];
[object, local, uuid("9e66a290-4365-11d2-a997-00c04fa37ddb")]
__interface ICustom {
HRESULT Custom([in] long l, [out, retval] long *pLong);
[local] HRESULT CustomLocal([in] long l, [out, retval] long *pLong);
};
[coclass, appobject, uuid("9e66a294-4365-11d2-a997-00c04fa37ddb")]
class CFoo : public ICustom
{};
// Fix for example 2
// First, create the *.idl file. The vc140.idl generated file can be used to automatically obtain *.idl file for the interfaces with annotation. 
// Second, add a midl step to your build system to make sure that the C++ interface definitions are outputted. 
// Lastly, adjust your existing code to use ATL directly as shown in atl implementation section

-- IDL  FILE -- 
import "docobj.idl";
 
[object, local, uuid(9e66a290-4365-11d2-a997-00c04fa37ddb)] 
interface ICustom : IUnknown {
HRESULT  Custom([in] long l, [out,retval] long *pLong);
[local] HRESULT  CustomLocal([in] long l, [out,retval] long *pLong);
};
 
[ version(1.0), uuid(29079a2c-5f3f-3325-99a1-3ec9c40988bb) ]
library Foo {
importlib("stdole2.tlb");
importlib("olepro32.dll");
 
[version(1.0), appobject, uuid(9e66a294-4365-11d2-a997-00c04fa37ddb)] 
 
coclass CFoo { interface ICustom; };
}
 
-- ATL IMPLEMENTATION--
#include 
#include 
class ATL_NO_VTABLE CFooImpl : public ICustom, public ATL::CComObjectRootEx {
public:
BEGIN_COM_MAP(CFooImpl)
COM_INTERFACE_ENTRY(ICustom)
END_COM_MAP()
}; 

Changes not present in VS 2017 RC

Here are some changes to /permissive- that we expect to enable in future releases and updates of VS 2017. This list may not be complete.

  • Two-phase name lookup
  • Error when binding a non-const reference to a temporary
  • Not treating copy init as direct init
  • Not allowing multiple user-defined conversions in initialization.
  • Alternative tokens for logical operators (‘and’, ‘or’, etc…)

In closing

As always, we welcome your feedback. Please give us feedback about the /permissive- feature in the comments below or through e-mail at visualcpp@microsoft.com.

If you encounter other problems with Visual C++ in VS 2017 RC please let us know via the Report a Problem option, either from the installer or the Visual Studio IDE itself. For suggestions, let us know through UserVoice. Thank you!


Viewing all articles
Browse latest Browse all 5971

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>