Skip to content

Commit

Permalink
Add a CORRADE_MSVC_COMPATIBILITY option.
Browse files Browse the repository at this point in the history
This will cover issues with MSVC2019+ that happen only when /permissive-
is not set -- most of the workarounds for MSVC 2019 were already
happening only when the standards conforming mode was not enabled, and I
expect this only get better with MSVC 2022.

The flag is impossible to detect via a preprocessor which means we can't
just disable / change certain parts of the API if the flag is missing,
thus it has to be done the old explicit way by setting it in CMake and
then remembering it inside configure.h. That way however avoids nasty
corner cases, especially when the API changes would change the ABI, so
that's nice.

Furthermore, to ensure /permissive- is always present when
CORRADE_MSVC_COMPATIBILITY is off, configure.h has a static_assert()
that fails when the flag is not present. This is the only (and not fully
robust) way to check the presence of the flag, but could be good enough.
  • Loading branch information
mosra committed Feb 7, 2022
1 parent d154541 commit 00cfbbb
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 16 deletions.
33 changes: 29 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ project(Corrade CXX)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)

include(CMakeDependentOption)

# We want to present the option to the user, but we also want to implicitly
# enable it during the first run, unless manually overriden. Thus we first
# remember if it was defined, then define the option() and then below check the
# remembered state.
if(NOT DEFINED MSVC_COMPATIBILITY)
set(_CORRADE_MSVC_COMPATIBILITY_NOT_DEFINED ON)
endif()
option(MSVC_COMPATIBILITY "Enable compatibility mode for MSVC 2019+ without /permissive- set (might disable some features)" OFF)

option(MSVC2019_COMPATIBILITY "Enable compatibility mode for MSVC 2019 (might disable some features)" OFF)
option(MSVC2017_COMPATIBILITY "Enable compatibility mode for MSVC 2017 (might disable some features)" OFF)
option(MSVC2015_COMPATIBILITY "Enable compatibility mode for MSVC 2015 (might disable some features)" OFF)
Expand Down Expand Up @@ -212,27 +222,42 @@ elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
set(MSVC2017_COMPATIBILITY ON)
message(WARNING "MSVC 2017 detected, automatically enabling MSVC2017_COMPATIBILITY. Note that some features may not be available with this compiler.")
endif()
elseif(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.30")
if(NOT MSVC2019_COMPATIBILITY)
set(MSVC2019_COMPATIBILITY ON)
message(WARNING "MSVC 2019 detected, automatically enabling MSVC2019_COMPATIBILITY. Note that some features may not be available with this compiler.")
else()
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.30")
if(NOT MSVC2019_COMPATIBILITY)
set(MSVC2019_COMPATIBILITY ON)
message(WARNING "MSVC 2019 detected, automatically enabling MSVC2019_COMPATIBILITY. Note that some features may not be available with this compiler.")
endif()
endif()

# MSVC_COMPATIBILITY is enforced by MSVC2017_COMPATIBILITY and
# MSVC2015_COMPATIBILITY below, but can be disabled for 2019+.
if(_CORRADE_MSVC_COMPATIBILITY_NOT_DEFINED)
set(MSVC_COMPATIBILITY ON)
message(WARNING "MSVC 2019+ detected, automatically enabling MSVC_COMPATIBILITY which may cause some features to not be available. You can disable this option if you pass /permissive- to the compiler to enable a standards-conforming mode.")
endif()
endif()
endif()

if(MSVC_COMPATIBILITY)
set(CORRADE_MSVC_COMPATIBILITY 1)
endif()

if(MSVC2019_COMPATIBILITY)
set(CORRADE_MSVC2019_COMPATIBILITY 1)
endif()

if(MSVC2017_COMPATIBILITY)
set(CORRADE_MSVC2017_COMPATIBILITY 1)
set(CORRADE_MSVC2019_COMPATIBILITY 1)
set(CORRADE_MSVC_COMPATIBILITY 1)
endif()

if(MSVC2015_COMPATIBILITY)
set(CORRADE_MSVC2015_COMPATIBILITY 1)
set(CORRADE_MSVC2017_COMPATIBILITY 1)
set(CORRADE_MSVC2019_COMPATIBILITY 1)
set(CORRADE_MSVC_COMPATIBILITY 1)
endif()

if(TESTSUITE_TARGET_XCTEST)
Expand Down
28 changes: 17 additions & 11 deletions doc/building-corrade.dox
Original file line number Diff line number Diff line change
Expand Up @@ -572,17 +572,23 @@ for both debug and release configurations. This distinction is handled
automatically when using the library in depending projects, see @ref corrade-cmake
for more information.

By default the library expects standards-conforming compiler. However, for
portability, compatibility mode for some compilers is provided. The build
system tries to autodect it, but in some rare cases you may need to enable
the compatibility mode explicitly:

- `MSVC2019_COMPATIBILITY` --- Enable for compiling with compatibility mode
for Microsoft Visual C++ 2019.
- `MSVC2017_COMPATIBILITY` --- Enable for compiling with compatibility mode
for Microsoft Visual C++ 2017.
- `MSVC2015_COMPATIBILITY` --- Enable for compiling with compatibility mode
for Microsoft Visual C++ 2015.
By default the library expects a standards-conforming compiler. However, for
portability, compatibility mode for some compilers is provided:

- `MSVC_COMPATIBILITY` --- Compiling with compatibility mode for Microsoft
Visual C++ 2019+ *without* the `/permissive-` flag set. Enabled implicitly
if MSVC (but not clang-cl) is detected. You can set it to `OFF` but then
you're required to specify `/permissive-` for all code using Corrade.
- `MSVC2019_COMPATIBILITY` --- Compiling with compatibility mode for
Microsoft Visual C++ 2019. Enabled implicitly if MSVC 2019 is detected,
implies `MSVC2022_COMPATIBILITY`.
- `MSVC2017_COMPATIBILITY` --- Compiling with compatibility mode for
Microsoft Visual C++ 2017. Enabled implicitly if MSVC 2017 is detected,
implies `MSVC2019_COMPATIBILITY` and `MSVC_COMPATIBILITY`.
- `MSVC2015_COMPATIBILITY` --- Compiling with compatibility mode for
Microsoft Visual C++ 2015. Enabled implicitly if MSVC 2015 is detected,
implies `MSVC2017_COMPATIBILITY`, `MSVC2019_COMPATIBILITY` and
`MSVC_COMPATIBILITY`.

Corrade will detect the compiler used and refuses to build (or be used) if
some required compatibility mode is not enabled. On the other hand, if any of
Expand Down
2 changes: 2 additions & 0 deletions doc/corrade-changelog.dox
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ namespace Corrade {
x86, ARM and WebAssembly
- New @ref CORRADE_TARGET_32BIT preprocessor variable for cross-platform
detection of 32-bit builds
- New @ref CORRADE_MSVC_COMPATIBILITY flag that controls workarounds for MSVC
2019+ with the `/permissive-` flag not enabled.

@subsubsection corrade-changelog-latest-new-containers Containers library

Expand Down
2 changes: 2 additions & 0 deletions doc/corrade-cmake.dox
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ Features of found Corrade library are exposed in these variables, they are
also available as preprocessor variables if you include
@ref Corrade/Corrade.h "Corrade/Corrade.h":

- `CORRADE_MSVC_COMPATIBILITY` --- Defined if compiled with compatibility
mode for MSVC 2019+ without the `/permissive-` flag set
- `CORRADE_MSVC2019_COMPATIBILITY` --- Defined if compiled with compatibility
mode for MSVC 2019
- `CORRADE_MSVC2017_COMPATIBILITY` --- Defined if compiled with compatibility
Expand Down
3 changes: 3 additions & 0 deletions modules/FindCorrade.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@
#
# Features of found Corrade library are exposed in these variables:
#
# CORRADE_MSVC_COMPATIBILITY - Defined if compiled with compatibility
# mode for MSVC 2019+ without the /permissive- flag set
# CORRADE_MSVC2019_COMPATIBILITY - Defined if compiled with compatibility
# mode for MSVC 2019
# CORRADE_MSVC2017_COMPATIBILITY - Defined if compiled with compatibility
Expand Down Expand Up @@ -313,6 +315,7 @@ set(_corradeFlags
MSVC2015_COMPATIBILITY
MSVC2017_COMPATIBILITY
MSVC2019_COMPATIBILITY
MSVC_COMPATIBILITY
BUILD_DEPRECATED
BUILD_STATIC
BUILD_STATIC_UNIQUE_GLOBALS
Expand Down
2 changes: 1 addition & 1 deletion package/ci/appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ environment:
# probably due to a lot of "warning C4530: C++ exception handler used, but
# unwind semantics are not enabled. Specify /EHsc" messages printed before.
# Not sure why is that NOT a problem without /permissive-.
COMPILER_EXTRA: -DCMAKE_CXX_FLAGS="/permissive- /EHsc"
COMPILER_EXTRA: -DCMAKE_CXX_FLAGS="/permissive- /EHsc" -DMSVC_COMPATIBILITY=OFF
PLATFORM: x64
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
APPVEYOR_JOB_NAME: windows-conforming-msvc2019
Expand Down
11 changes: 11 additions & 0 deletions src/Corrade/Corrade.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,17 @@
namespace Corrade {

#ifdef DOXYGEN_GENERATING_OUTPUT
/**
@brief MSVC compatibility
@m_since_latest
Defined if compatibility mode for Microsoft Visual C++ 2019+ *without* the
`/permissive-` flag set is enabled.
@see @ref building-corrade, @ref corrade-cmake
*/
#define CORRADE_MSVC_COMPATIBILITY
#undef CORRADE_MSVC_COMPATIBILITY

/**
@brief MSVC 2019 compatibility
Expand Down
8 changes: 8 additions & 0 deletions src/Corrade/Test/TargetTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,15 @@ void TargetTest::msvcPermissiveFlag() {
CORRADE_INFO((sizeof(1 ? "" : "") == 1 ?
"Standards-conforming C++ parser, compiled with the /permissive- flag" :
"Non-conforming C++ parser, compiled without /permissive-"));

#ifdef CORRADE_MSVC_COMPATIBILITY
if(sizeof(1 ? "" : "") == 1)
CORRADE_WARN("CORRADE_MSVC_COMPATIBILITY set, but the parser is standards-conforming.");
CORRADE_VERIFY(true);
#else
CORRADE_FAIL_IF(sizeof(1 ? "" : "") != 1,
"CORRADE_MSVC_COMPATIBILITY not set and the parser is not standards-conforming.");
#endif
}
#endif

Expand Down
31 changes: 31 additions & 0 deletions src/Corrade/configure.h.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
DEALINGS IN THE SOFTWARE.
*/

#cmakedefine CORRADE_MSVC_COMPATIBILITY
#cmakedefine CORRADE_MSVC2019_COMPATIBILITY
#cmakedefine CORRADE_MSVC2017_COMPATIBILITY
#cmakedefine CORRADE_MSVC2015_COMPATIBILITY
Expand Down Expand Up @@ -283,4 +284,34 @@
#define CORRADE_LONG_DOUBLE_SAME_AS_DOUBLE
#endif

/* Kill switch for when absence of the /permissive- flag is detected and
CORRADE_MSVC_COMPATIBILITY is not defined. According to
https://developercommunity.visualstudio.com/t/pre-define-a-macro-when-compiling-under-permissive/1253982
there's STILL no macro that advertising presence of the flag (independently
verified with `cl /EP /Zc:preprocessor /PD empty.cpp 2>nul` on MSVC 2022).
I suppose it's because it would break PCHs?

However, because we need to remove various class members and do other nasty
stuff to work without /permissive-, we need a way to control it from the
preprocessor, which the CORRADE_MSVC_COMPATIBILITY macro is for. It's
enabled implicitly but the user can explicitly disable it during the build
of Corrade, which is then recorded into the generated configure.h file. The
/permissive- flag is then expected to be passed.

To avoid nasty issues with ABI mismatch, the following assert checks that
the flag is passed if CORRADE_MSVC_COMPATIBILITY is not enabled. The hope is
that CORRADE_MSVC_COMPATIBILITY gets eventually dropped altogether, with
this assert being checked always. But as /permissive- caused ICEs in some
MSVC 2017 versions, it's not feasible to enforce it yet.
*/
#if defined(CORRADE_TARGET_MSVC) && !defined(CORRADE_TARGET_CLANG_CL) && !defined(CORRADE_MSVC_COMPATIBILITY)
/* Without /permissive- (or with /permissive- /Zc:ternary-, or with just
/Zc:ternary), it'd be sizeof(const char*) instead. Yes, it's not
bulletproof, but of all cases shown here it's the cheapest check:
https://docs.microsoft.com/en-us/cpp/build/reference/permissive-standards-conformance?view=msvc-170#ambiguous-conditional-operator-arguments */
static_assert(sizeof(1 ? "" : "") == 1,
"Corrade was built without CORRADE_MSVC_COMPATIBILITY, but /permissive- " "doesn't seem to be enabled. Either rebuild Corrade with the option "
"enabled or ensure /permissive- is set for all files that include Corrade " "headers.");
#endif

#endif // kate: hl c++

0 comments on commit 00cfbbb

Please sign in to comment.