Browse Source

Merge topic 'msvc-runtime-checks'

2b2344b412 MSVC: Add abstraction for runtime checks
49dcd1ce5d Help: Fix MSVC_DEBUG_INFORMATION_FORMAT description of example
2f8e643d9d CMP0141: Fix documentation copied from CMP0091

Acked-by: Kitware Robot <[email protected]>
Acked-by: Alex <[email protected]>
Merge-request: !10214
Brad King 8 months ago
parent
commit
3e64c6a56e
51 changed files with 603 additions and 26 deletions
  1. 2 0
      Auxiliary/vim/syntax/cmake.vim
  2. 6 0
      Help/command/try_compile.rst
  3. 1 0
      Help/manual/cmake-policies.7.rst
  4. 1 0
      Help/manual/cmake-properties.7.rst
  5. 1 0
      Help/manual/cmake-variables.7.rst
  6. 2 2
      Help/policy/CMP0141.rst
  7. 54 0
      Help/policy/CMP0184.rst
  8. 1 1
      Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT.rst
  9. 16 0
      Help/prop_tgt/MSVC_RUNTIME_CHECKS-VALUES.txt
  10. 34 0
      Help/prop_tgt/MSVC_RUNTIME_CHECKS.rst
  11. 7 0
      Help/release/dev/msvc-runtime-checks.rst
  12. 1 1
      Help/variable/CMAKE_MSVC_DEBUG_INFORMATION_FORMAT.rst
  13. 36 0
      Help/variable/CMAKE_MSVC_RUNTIME_CHECKS.rst
  14. 5 2
      Modules/CMakeASM_MARMASMInformation.cmake
  15. 5 2
      Modules/CMakeASM_MASMInformation.cmake
  16. 4 0
      Modules/Platform/Windows-Clang-ASM.cmake
  17. 15 0
      Modules/Platform/Windows-Clang.cmake
  18. 6 0
      Modules/Platform/Windows-Intel-Fortran.cmake
  19. 6 0
      Modules/Platform/Windows-IntelLLVM-Fortran.cmake
  20. 32 4
      Modules/Platform/Windows-MSVC.cmake
  21. 12 1
      Modules/Platform/Windows-NVIDIA-CUDA.cmake
  22. 10 0
      Source/cmCoreTryCompile.cxx
  23. 47 12
      Source/cmLocalGenerator.cxx
  24. 4 1
      Source/cmPolicies.h
  25. 2 0
      Source/cmTarget.cxx
  26. 8 0
      Tests/CMakeLists.txt
  27. 1 0
      Tests/FortranOnly/CMakeLists.txt
  28. 105 0
      Tests/MSVCRuntimeChecks/CMakeLists.txt
  29. 3 0
      Tests/MSVCRuntimeChecks/override-C.cmake
  30. 3 0
      Tests/MSVCRuntimeChecks/override-CUDA.cmake
  31. 3 0
      Tests/MSVCRuntimeChecks/override-CXX.cmake
  32. 3 0
      Tests/MSVCRuntimeChecks/override-Fortran.cmake
  33. 50 0
      Tests/MSVCRuntimeChecks/override-common.cmake
  34. 1 0
      Tests/MSVCRuntimeChecks/verify.F90
  35. 1 0
      Tests/MSVCRuntimeChecks/verify.c
  36. 1 0
      Tests/MSVCRuntimeChecks/verify.cu
  37. 1 0
      Tests/MSVCRuntimeChecks/verify.cxx
  38. 61 0
      Tests/MSVCRuntimeChecks/verify.h
  39. 1 0
      Tests/RunCMake/CMakeLists.txt
  40. 1 0
      Tests/RunCMake/MSVCRuntimeChecks/CMP0184-NEW-result.txt
  41. 4 0
      Tests/RunCMake/MSVCRuntimeChecks/CMP0184-NEW-stderr.txt
  42. 2 0
      Tests/RunCMake/MSVCRuntimeChecks/CMP0184-NEW.cmake
  43. 4 0
      Tests/RunCMake/MSVCRuntimeChecks/CMP0184-NoEffect.cmake
  44. 2 0
      Tests/RunCMake/MSVCRuntimeChecks/CMP0184-OLD.cmake
  45. 2 0
      Tests/RunCMake/MSVCRuntimeChecks/CMP0184-WARN.cmake
  46. 25 0
      Tests/RunCMake/MSVCRuntimeChecks/CMP0184-common.cmake
  47. 3 0
      Tests/RunCMake/MSVCRuntimeChecks/CMakeLists.txt
  48. 6 0
      Tests/RunCMake/MSVCRuntimeChecks/RunCMakeTest.cmake
  49. 0 0
      Tests/RunCMake/MSVCRuntimeChecks/empty.c
  50. 1 0
      Tests/VSMARMASM/CMakeLists.txt
  51. 1 0
      Tests/VSMASM/CMakeLists.txt

+ 2 - 0
Auxiliary/vim/syntax/cmake.vim

@@ -305,6 +305,7 @@ syn keyword cmakeProperty contained
             \ MEASUREMENT
             \ MODIFIED
             \ MSVC_DEBUG_INFORMATION_FORMAT
+            \ MSVC_RUNTIME_CHECKS
             \ MSVC_RUNTIME_LIBRARY
             \ NAME
             \ NO_SONAME
@@ -1533,6 +1534,7 @@ syn keyword cmakeVariable contained
             \ CMAKE_MODULE_PATH
             \ CMAKE_MSVCIDE_RUN_PATH
             \ CMAKE_MSVC_DEBUG_INFORMATION_FORMAT
+            \ CMAKE_MSVC_RUNTIME_CHECKS
             \ CMAKE_MSVC_RUNTIME_LIBRARY
             \ CMAKE_NETRC
             \ CMAKE_NETRC_FILE

+ 6 - 0
Help/command/try_compile.rst

@@ -349,6 +349,7 @@ as needed to honor the state of the calling project:
 * :policy:`CMP0155`
 * :policy:`CMP0157`
 * :policy:`CMP0181`
+* :policy:`CMP0184`
 
 .. versionadded:: 4.0
   The current setting of :policy:`CMP0181` policy is propagated through to the
@@ -417,6 +418,11 @@ configuration:
   propagated into the test project's build configuration when using the
   :ref:`whole-project signature <Try Compiling Whole Projects>`.
 
+.. versionadded:: 4.0
+  If :policy:`CMP0184` is set to ``NEW``, one can use
+  :variable:`CMAKE_MSVC_RUNTIME_CHECKS` to specify the enabled MSVC runtime
+  checks.
+
 See Also
 ^^^^^^^^
 

+ 1 - 0
Help/manual/cmake-policies.7.rst

@@ -98,6 +98,7 @@ Policies Introduced by CMake 4.0
 .. toctree::
    :maxdepth: 1
 
+   CMP0184: MSVC runtime checks flags are selected by an abstraction. </policy/CMP0184>
    CMP0183: add_feature_info() supports full Condition Syntax. </policy/CMP0183>
    CMP0182: Create shared library archives by default on AIX. </policy/CMP0182>
    CMP0181: Link command-line fragment variables are parsed and re-quoted. </policy/CMP0181>

+ 1 - 0
Help/manual/cmake-properties.7.rst

@@ -359,6 +359,7 @@ Properties on Targets
    /prop_tgt/MANUALLY_ADDED_DEPENDENCIES
    /prop_tgt/MAP_IMPORTED_CONFIG_CONFIG
    /prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT
+   /prop_tgt/MSVC_RUNTIME_CHECKS
    /prop_tgt/MSVC_RUNTIME_LIBRARY
    /prop_tgt/NAME
    /prop_tgt/NO_SONAME

+ 1 - 0
Help/manual/cmake-variables.7.rst

@@ -519,6 +519,7 @@ Variables that Control the Build
    /variable/CMAKE_MODULE_LINKER_FLAGS_CONFIG_INIT
    /variable/CMAKE_MODULE_LINKER_FLAGS_INIT
    /variable/CMAKE_MSVC_DEBUG_INFORMATION_FORMAT
+   /variable/CMAKE_MSVC_RUNTIME_CHECKS
    /variable/CMAKE_MSVC_RUNTIME_LIBRARY
    /variable/CMAKE_MSVCIDE_RUN_PATH
    /variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX

+ 2 - 2
Help/policy/CMP0141.rst

@@ -13,8 +13,8 @@ In CMake 3.24 and below, debug information format flags are added to
 the default :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` cache entries by CMake
 automatically.  This allows users to edit their cache entries to adjust the
 flags.  However, the presence of such default flags is problematic for
-projects that want to choose a different runtime library programmatically.
-In particular, it requires string editing of the
+projects that want to choose a different debug information format
+programmatically.  In particular, it requires string editing of the
 :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` variables with knowledge of the
 CMake builtin defaults so they can be replaced.
 

+ 54 - 0
Help/policy/CMP0184.rst

@@ -0,0 +1,54 @@
+CMP0184
+-------
+
+.. versionadded:: 4.0
+
+MSVC runtime checks flags are selected by an abstraction.
+
+Compilers targeting the MSVC ABI have flags to select the runtime checks.
+Runtime checks selection typically varies with build
+configuration.
+
+In CMake 3.31 and below, runtime checks flags are added to
+the default :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` cache entries by CMake
+automatically.  This allows users to edit their cache entries to adjust the
+flags.  However, the presence of such default flags is problematic for
+projects that want to choose different runtime checks programmatically.
+In particular, it requires string editing of the
+:variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` variables with knowledge of the
+CMake builtin defaults so they can be replaced.
+
+CMake 4.0 and above prefer to leave the runtime checks flags
+out of the default :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` values and instead
+offer a first-class abstraction.  The
+:variable:`CMAKE_MSVC_RUNTIME_CHECKS` variable and
+:prop_tgt:`MSVC_RUNTIME_CHECKS` target property may be set to
+select the MSVC runtime checks.  If they are not set, CMake
+enables runtime checks in ``Debug`` configuration using the default value
+``$<$<CONFIG:Debug>:StackFrameErrorCheck;UninitializedVariable>``, if
+supported by the compiler, or empty value otherwise.
+
+This policy provides compatibility with projects that have not been updated
+to be aware of the abstraction.  The policy setting takes effect as of the
+first :command:`project` or :command:`enable_language` command that enables
+a language whose compiler targets the MSVC ABI.
+
+.. note::
+
+  Once the policy has taken effect at the top of a project, that choice
+  will be used throughout the tree.  In projects that have nested projects
+  in subdirectories, be sure to confirm if everything is working with the
+  selected policy behavior.
+
+The ``OLD`` behavior for this policy is to place MSVC runtimes checks
+flags in the default :variable:`CMAKE_<LANG>_FLAGS_<CONFIG>` cache
+entries and ignore the :variable:`CMAKE_MSVC_RUNTIME_CHECKS`
+abstraction.  The ``NEW`` behavior for this policy is to *not* place MSVC
+runtime checks flags in the default cache entries and use
+the abstraction instead.
+
+.. |INTRODUCED_IN_CMAKE_VERSION| replace:: 4.0
+.. |WARNS_OR_DOES_NOT_WARN| replace:: does *not* warn
+.. include:: STANDARD_ADVICE.txt
+
+.. include:: DEPRECATED.txt

+ 1 - 1
Help/prop_tgt/MSVC_DEBUG_INFORMATION_FORMAT.rst

@@ -19,7 +19,7 @@ support per-configuration specification.  For example, the code:
     MSVC_DEBUG_INFORMATION_FORMAT "$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>")
 
 selects for the target ``foo`` the program database debug information format
-for the Debug configuration.
+for the ``Debug`` and ``RelWithDebInfo`` configurations.
 
 This property is initialized from the value of the
 :variable:`CMAKE_MSVC_DEBUG_INFORMATION_FORMAT` variable, if it is set.

+ 16 - 0
Help/prop_tgt/MSVC_RUNTIME_CHECKS-VALUES.txt

@@ -0,0 +1,16 @@
+``PossibleDataLoss``
+  Compile with ``-RTCc`` or equivalent flag(s) to enable possible
+  data loss checks.
+``StackFrameErrorCheck``
+  Compile with ``-RTCs`` or equivalent flag(s) to enable stack frame
+  error checks.
+``UninitializedVariable``
+  Compile with ``-RTCu`` or equivalent flag(s) to enable uninitialized
+  variables checks.
+
+The value is ignored on compilers not targeting the MSVC ABI, but an
+unsupported value will be rejected as an error when using a compiler
+targeting the MSVC ABI.
+
+The value may also be the empty string (``""``), in which case no runtime
+error check flags will be added explicitly by CMake.

+ 34 - 0
Help/prop_tgt/MSVC_RUNTIME_CHECKS.rst

@@ -0,0 +1,34 @@
+MSVC_RUNTIME_CHECKS
+-------------------
+
+.. versionadded:: 4.0
+
+Select the list of enabled runtime checks when targeting the MSVC ABI.
+
+The allowed values are:
+
+.. include:: MSVC_RUNTIME_CHECKS-VALUES.txt
+
+Use :manual:`generator expressions <cmake-generator-expressions(7)>` to
+support per-configuration specification.  For example, the code:
+
+.. code-block:: cmake
+
+  add_executable(foo foo.c)
+  set_property(TARGET foo PROPERTY
+    MSVC_RUNTIME_CHECKS "$<$<CONFIG:Debug,RelWithDebInfo>:PossibleDataLoss;UninitializedVariable>")
+
+enables for the target ``foo`` the possible data loss and uninitialized variables checks
+for the ``Debug`` and ``RelWithDebInfo`` configurations.
+
+This property is initialized from the value of the
+:variable:`CMAKE_MSVC_RUNTIME_CHECKS` variable, if it is set.
+If this property is not set, CMake selects a runtime checks using
+the default value ``$<$<CONFIG:Debug>:StackFrameErrorCheck;UninitializedVariable>``, if
+supported by the compiler, or empty value otherwise.
+
+.. note::
+
+  This property has effect only when policy :policy:`CMP0184` is set to ``NEW``
+  prior to the first :command:`project` or :command:`enable_language` command
+  that enables a language using a compiler targeting the MSVC ABI.

+ 7 - 0
Help/release/dev/msvc-runtime-checks.rst

@@ -0,0 +1,7 @@
+msvc-runtime-checks
+-------------------
+
+* The :variable:`CMAKE_MSVC_RUNTIME_CHECKS` variable and
+  :prop_tgt:`MSVC_RUNTIME_CHECKS` target property were introduced
+  to select runtime checks for compilers targeting the MSVC ABI.
+  See policy :policy:`CMP0184`.

+ 1 - 1
Help/variable/CMAKE_MSVC_DEBUG_INFORMATION_FORMAT.rst

@@ -21,7 +21,7 @@ support per-configuration specification.  For example, the code:
   set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$<CONFIG:Debug,RelWithDebInfo>:ProgramDatabase>")
 
 selects for all following targets the program database debug information format
-for the Debug configuration.
+for the ``Debug`` and ``RelWithDebInfo`` configurations.
 
 If this variable is not set, the :prop_tgt:`MSVC_DEBUG_INFORMATION_FORMAT`
 target property will not be set automatically.  If that property is not set,

+ 36 - 0
Help/variable/CMAKE_MSVC_RUNTIME_CHECKS.rst

@@ -0,0 +1,36 @@
+CMAKE_MSVC_RUNTIME_CHECKS
+-------------------------
+
+.. versionadded:: 4.0
+
+Select the list of enabled runtime checks when targeting the MSVC ABI.
+This variable is used to initialize the
+:prop_tgt:`MSVC_RUNTIME_CHECKS` property on all targets as they are
+created. It is also propagated by calls to the :command:`try_compile` command
+into the test project.
+
+The allowed values are:
+
+.. include:: ../prop_tgt/MSVC_RUNTIME_CHECKS-VALUES.txt
+
+Use :manual:`generator expressions <cmake-generator-expressions(7)>` to
+support per-configuration specification. For example, the code:
+
+.. code-block:: cmake
+
+  set(CMAKE_MSVC_RUNTIME_CHECKS "$<$<CONFIG:Debug,RelWithDebInfo>:PossibleDataLoss;UninitializedVariable>")
+
+enables for the target ``foo`` the possible data loss and uninitialized variables checks
+for the ``Debug`` and ``RelWithDebInfo`` configurations.
+
+If this variable is not set, the :prop_tgt:`MSVC_RUNTIME_CHECKS`
+target property will not be set automatically.  If that property is not set,
+CMake selects runtime checks using the default value
+``$<$<CONFIG:Debug>:StackFrameErrorCheck;UninitializedVariable>``,
+if supported by the compiler, or empty value otherwise.
+
+.. note::
+
+  This variable has effect only when policy :policy:`CMP0184` is set to ``NEW``
+  prior to the first :command:`project` or :command:`enable_language` command
+  that enables a language using a compiler targeting the MSVC ABI.

+ 5 - 2
Modules/CMakeASM_MARMASMInformation.cmake

@@ -11,12 +11,15 @@ set(CMAKE_ASM${ASM_DIALECT}_SOURCE_FILE_EXTENSIONS asm)
 set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OBJECT "<CMAKE_ASM${ASM_DIALECT}_COMPILER> <INCLUDES> <FLAGS> -o <OBJECT> <SOURCE>")
 set(CMAKE_ASM${ASM_DIALECT}_CREATE_STATIC_LIBRARY "<CMAKE_AR> <LINK_FLAGS> /out:<TARGET> <OBJECTS> ")
 
-# The ASM_MARMASM compiler id for this compiler is "MSVC", so fill out the runtime library table.
+# The ASM_MARMASM compiler id for this compiler is "MSVC", so fill out the abstraction tables.
+set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_PossibleDataLoss      "")
+set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_StackFrameErrorCheck  "")
+set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_UninitializedVariable "")
+set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_RTCsu                 "")
 set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         "")
 set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      "")
 set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    "")
 set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL "")
-
 set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_Embedded        "-g")
 set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase "")
 set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_EditAndContinue "")

+ 5 - 2
Modules/CMakeASM_MASMInformation.cmake

@@ -11,12 +11,15 @@ set(CMAKE_ASM${ASM_DIALECT}_SOURCE_FILE_EXTENSIONS asm)
 set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OBJECT "<CMAKE_ASM${ASM_DIALECT}_COMPILER> <DEFINES> <INCLUDES> <FLAGS> -c -Fo <OBJECT> <SOURCE>")
 set(CMAKE_ASM${ASM_DIALECT}_CREATE_STATIC_LIBRARY "<CMAKE_AR> <LINK_FLAGS> /out:<TARGET> <OBJECTS> ")
 
-# The ASM_MASM compiler id for this compiler is "MSVC", so fill out the runtime library table.
+# The ASM_MASM compiler id for this compiler is "MSVC", so fill out the abstraction tables.
+set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_PossibleDataLoss      "")
+set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_StackFrameErrorCheck  "")
+set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_UninitializedVariable "")
+set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_RTCsu                 "")
 set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         "")
 set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      "")
 set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    "")
 set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebugDLL "")
-
 set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_Embedded        "-Zi")
 set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase "")
 set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_EditAndContinue "")

+ 4 - 0
Modules/Platform/Windows-Clang-ASM.cmake

@@ -1,6 +1,10 @@
 include(Platform/Windows-Clang)
 __windows_compiler_clang(ASM)
 
+set(CMAKE_ASM_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_PossibleDataLoss      "")
+set(CMAKE_ASM_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_StackFrameErrorCheck  "")
+set(CMAKE_ASM_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_UninitializedVariable "")
+set(CMAKE_ASM_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_RTCsu                 "")
 set(CMAKE_ASM_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         "")
 set(CMAKE_ASM_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      "")
 set(CMAKE_ASM_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    "")

+ 15 - 0
Modules/Platform/Windows-Clang.cmake

@@ -117,6 +117,13 @@ macro(__windows_compiler_clang_gnu lang)
     string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT " -O3 -DNDEBUG${_RTL_FLAGS}")
     string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT " -O2 -DNDEBUG${_DBG_FLAGS}${_RTL_FLAGS}")
 
+    # clang-cl accepts -RTC* flags but ignores them.  Simulate this
+    # with the GNU-like drivers by simply passing no flags at all.
+    set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_PossibleDataLoss      "")
+    set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_StackFrameErrorCheck  "")
+    set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_UninitializedVariable "")
+    set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_RTCsu                 "")
+
     set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_Embedded        -g -Xclang -gcodeview)
     #set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_ProgramDatabase) # not supported by Clang
     #set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_EditAndContinue) # not supported by Clang
@@ -239,6 +246,14 @@ endmacro()
     endif()
     unset(__WINDOWS_MSVC_CMP0141)
 
+    cmake_policy(GET CMP0184 __WINDOWS_MSVC_CMP0184)
+    if(__WINDOWS_MSVC_CMP0184 STREQUAL "NEW")
+      set(CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT "$<$<CONFIG:Debug>:StackFrameErrorCheck;UninitializedVariable>")
+    else()
+      set(CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT "")
+    endif()
+    unset(__WINDOWS_MSVC_CMP0184)
+
     set(CMAKE_BUILD_TYPE_INIT Debug)
 
     __enable_llvm_rc_preprocessing("" "-x c")

+ 6 - 0
Modules/Platform/Windows-Intel-Fortran.cmake

@@ -29,6 +29,12 @@ unset(_LIBSDLL)
 unset(_DBGLIBS)
 unset(_THREADS)
 
+# icl accepts -RTC* flags but ignores them.  ifort accepts -RTCu only.
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_PossibleDataLoss      "")
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_StackFrameErrorCheck  "")
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_UninitializedVariable -RTCu)
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_RTCsu                 "")
+
 set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         -threads -libs:static)
 set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      -threads -libs:dll)
 set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    -threads -libs:static -dbglibs)

+ 6 - 0
Modules/Platform/Windows-IntelLLVM-Fortran.cmake

@@ -29,6 +29,12 @@ unset(_LIBSDLL)
 unset(_DBGLIBS)
 unset(_THREADS)
 
+# icx accepts -RTC* flags but ignores them.  ifx accepts -RTCu only.
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_PossibleDataLoss      "")
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_StackFrameErrorCheck  "")
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_UninitializedVariable -RTCu)
+set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_RTCsu                 "")
+
 set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         -threads -libs:static)
 set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      -threads -libs:dll)
 set(CMAKE_Fortran_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    -threads -libs:static -dbglibs)

+ 32 - 4
Modules/Platform/Windows-MSVC.cmake

@@ -192,6 +192,9 @@ if(WINCE)
   set(_PLATFORM_DEFINES_CXX " /D${_MSVC_CXX_ARCHITECTURE_FAMILY} /D_${_MSVC_CXX_ARCHITECTURE_FAMILY_UPPER}_")
 
   set(_RTC1 "")
+  set(_RTCc "")
+  set(_RTCs "")
+  set(_RTCu "")
   set(_FLAGS_C "")
   set(_FLAGS_CXX "${_GR} /EHsc")
 
@@ -248,17 +251,24 @@ else()
     if(CMAKE_VS_PLATFORM_TOOLSET MATCHES "v[0-9]+_clang_.*")
       # Clang/C2 in MSVC14 Update 1 seems to not support -fsantinize (yet?)
       # set(_RTC1 "-fsantinize=memory,safe-stack")
+      # set(_RTCs "-fsantinize=safe-stack")
+      # set(_RTCu "-fsantinize=memory")
       set(_FLAGS_CXX " -frtti -fexceptions")
     else()
-      set(_RTC1 "/RTC1")
+      set(_RTC1 "-RTC1")
+      set(_RTCc "-RTCc")
+      set(_RTCs "-RTCs")
+      set(_RTCu "-RTCu")
       set(_FLAGS_CXX "${_GR} /EHsc")
     endif()
     set(CMAKE_C_STANDARD_LIBRARIES_INIT "kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib")
   else()
-    set(_RTC1 "/GZ")
+    set(_RTC1 "-GZ")
+    set(_RTCs "-GZ")
     set(_FLAGS_CXX "${_GR} /GX")
     set(CMAKE_C_STANDARD_LIBRARIES_INIT "kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib")
   endif()
+  set(_RTCsu "${_RTC1}")
 
   if((_MSVC_C_ARCHITECTURE_FAMILY STREQUAL "ARM64EC") OR (_MSVC_CXX_ARCHITECTURE_FAMILY STREQUAL "ARM64EC"))
     string(APPEND CMAKE_C_STANDARD_LIBRARIES_INIT " softintrin.lib")
@@ -353,6 +363,13 @@ else()
 endif()
 unset(__WINDOWS_MSVC_CMP0141)
 
+cmake_policy(GET CMP0184 __WINDOWS_MSVC_CMP0184)
+if(__WINDOWS_MSVC_CMP0184 STREQUAL "NEW")
+  set(CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT "$<$<CONFIG:Debug>:StackFrameErrorCheck;UninitializedVariable>")
+else()
+  set(CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT "")
+endif()
+unset(__WINDOWS_MSVC_CMP0184)
 
 macro(__windows_compiler_msvc lang)
   if(NOT MSVC_VERSION LESS 1400)
@@ -467,17 +484,23 @@ macro(__windows_compiler_msvc lang)
       set(_Zi " /Zi")
     endif()
 
+    if(CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT)
+      set(_RTC1_local "")
+    else()
+      string(REPLACE " -" " /" _RTC1_local " ${_RTC1}")
+    endif()
+
     if(CMAKE_VS_PLATFORM_TOOLSET MATCHES "v[0-9]+_clang_.*")
       # note: MSVC 14 2015 Update 1 sets -fno-ms-compatibility by default, but this does not allow one to compile many projects
       # that include MS's own headers. CMake itself is affected project too.
       string(APPEND CMAKE_${lang}_FLAGS_INIT " ${_PLATFORM_DEFINES}${_PLATFORM_DEFINES_${lang}} -fms-extensions -fms-compatibility -D_WINDOWS${_Wall}${_FLAGS_${lang}}")
-      string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT "${_MDd} -gline-tables-only -fno-inline -O0 ${_RTC1}")
+      string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT "${_MDd} -gline-tables-only -fno-inline -O0${_RTC1_local}")
       string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT "${_MD} -O2 -DNDEBUG")
       string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT "${_MD} -gline-tables-only -O2 -fno-inline -DNDEBUG")
       string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT "${_MD} -DNDEBUG") # TODO: Add '-Os' once VS generator maps it properly for Clang
     else()
       string(APPEND CMAKE_${lang}_FLAGS_INIT " ${_PLATFORM_DEFINES}${_PLATFORM_DEFINES_${lang}} /D_WINDOWS${_W3}${_FLAGS_${lang}}")
-      string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT "${_MDd}${_Zi} /Ob0 /Od ${_RTC1}")
+      string(APPEND CMAKE_${lang}_FLAGS_DEBUG_INIT "${_MDd}${_Zi} /Ob0 /Od${_RTC1_local}")
       string(APPEND CMAKE_${lang}_FLAGS_RELEASE_INIT "${_MD} /O2 /Ob2 /DNDEBUG")
       string(APPEND CMAKE_${lang}_FLAGS_RELWITHDEBINFO_INIT "${_MD}${_Zi} /O2 /Ob1 /DNDEBUG")
       string(APPEND CMAKE_${lang}_FLAGS_MINSIZEREL_INIT "${_MD} /O1 /Ob1 /DNDEBUG")
@@ -487,7 +510,12 @@ macro(__windows_compiler_msvc lang)
     unset(_MDd)
     unset(_MD)
     unset(_Zi)
+    unset(_RTC1_local)
 
+    set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_PossibleDataLoss      "${_RTCc}")
+    set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_StackFrameErrorCheck  "${_RTCs}")
+    set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_UninitializedVariable "${_RTCu}")
+    set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_RTCsu                 "${_RTCsu}")
     set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         -MT)
     set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      -MD)
     set(CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    -MTd)

+ 12 - 1
Modules/Platform/Windows-NVIDIA-CUDA.cmake

@@ -68,6 +68,12 @@ else()
   set(_Zi " -Zi")
 endif()
 
+if(CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT)
+  set(_RTC1_local "")
+else()
+  string(REPLACE " -" " /" _RTC1_local " ${_RTC1}")
+endif()
+
 cmake_policy(GET CMP0092 _cmp0092)
 if(_cmp0092 STREQUAL "NEW")
   set(_W3 "")
@@ -77,7 +83,7 @@ endif()
 unset(_cmp0092)
 
 string(APPEND CMAKE_CUDA_FLAGS_INIT " ${PLATFORM_DEFINES_CUDA} -D_WINDOWS -Xcompiler=\"${_W3}${_FLAGS_CXX}\"")
-string(APPEND CMAKE_CUDA_FLAGS_DEBUG_INIT " -Xcompiler=\"${_MDd}${_Zi} -Ob0 -Od ${_RTC1}\"")
+string(APPEND CMAKE_CUDA_FLAGS_DEBUG_INIT " -Xcompiler=\"${_MDd}${_Zi} -Ob0 -Od${_RTC1_local}\"")
 string(APPEND CMAKE_CUDA_FLAGS_RELEASE_INIT " -Xcompiler=\"${_MD}-O2 -Ob2\" -DNDEBUG")
 string(APPEND CMAKE_CUDA_FLAGS_RELWITHDEBINFO_INIT " -Xcompiler=\"${_MD}${_Zi} -O2 -Ob1\" -DNDEBUG")
 string(APPEND CMAKE_CUDA_FLAGS_MINSIZEREL_INIT " -Xcompiler=\"${_MD}-O1 -Ob1\" -DNDEBUG")
@@ -85,7 +91,12 @@ unset(_W3)
 unset(_Zi)
 unset(_MDd)
 unset(_MD)
+unset(_RTC1_local)
 
+set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_PossibleDataLoss      "-Xcompiler=${_RTCc} -D_ALLOW_RTCc_IN_STL")
+set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_StackFrameErrorCheck  "-Xcompiler=${_RTCs}")
+set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_UninitializedVariable "-Xcompiler=${_RTCu}")
+set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_RTCsu                 "-Xcompiler=${_RTCsu}")
 set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreaded         -Xcompiler=-MT)
 set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDLL      -Xcompiler=-MD)
 set(CMAKE_CUDA_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_MultiThreadedDebug    -Xcompiler=-MTd)

+ 10 - 0
Source/cmCoreTryCompile.cxx

@@ -110,6 +110,8 @@ std::string const kCMAKE_WATCOM_RUNTIME_LIBRARY_DEFAULT =
   "CMAKE_WATCOM_RUNTIME_LIBRARY_DEFAULT";
 std::string const kCMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT =
   "CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT";
+std::string const kCMAKE_MSVC_RUNTIME_CHECKS_DEFAULT =
+  "CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT";
 
 /* GHS Multi platform variables */
 std::set<std::string> const ghs_platform_vars{
@@ -683,6 +685,13 @@ cm::optional<cmTryCompileResult> cmCoreTryCompile::TryCompileCode(
               !msvcDebugInformationFormatDefault->empty() ? "NEW" : "OLD");
     }
 
+    /* Set MSVC runtime checks policy to match our selection.  */
+    if (cmValue msvcRuntimeChecksDefault =
+          this->Makefile->GetDefinition(kCMAKE_MSVC_RUNTIME_CHECKS_DEFAULT)) {
+      fprintf(fout, "cmake_policy(SET CMP0184 %s)\n",
+              !msvcRuntimeChecksDefault->empty() ? "NEW" : "OLD");
+    }
+
     /* Set cache/normal variable policy to match outer project.
        It may affect toolchain files.  */
     if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) !=
@@ -1111,6 +1120,7 @@ cm::optional<cmTryCompileResult> cmCoreTryCompile::TryCompileCode(
     vars.emplace("CMAKE_MSVC_RUNTIME_LIBRARY"_s);
     vars.emplace("CMAKE_WATCOM_RUNTIME_LIBRARY"_s);
     vars.emplace("CMAKE_MSVC_DEBUG_INFORMATION_FORMAT"_s);
+    vars.emplace("CMAKE_MSVC_RUNTIME_CHECKS"_s);
     vars.emplace("CMAKE_CXX_COMPILER_CLANG_SCAN_DEPS"_s);
     vars.emplace("CMAKE_VS_USE_DEBUG_LIBRARIES"_s);
 

+ 47 - 12
Source/cmLocalGenerator.cxx

@@ -2193,10 +2193,7 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags,
             "CMAKE_" + lang + "_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_" +
             msvcRuntimeLibrary)) {
         this->AppendCompileOptions(flags, *msvcRuntimeLibraryOptions);
-      } else if ((this->Makefile->GetSafeDefinition(
-                    "CMAKE_" + lang + "_COMPILER_ID") == "MSVC" ||
-                  this->Makefile->GetSafeDefinition(
-                    "CMAKE_" + lang + "_SIMULATE_ID") == "MSVC") &&
+      } else if ((compiler == "MSVC" || compilerSimulateId == "MSVC") &&
                  !cmSystemTools::GetErrorOccurredFlag()) {
         // The compiler uses the MSVC ABI so it needs a known runtime library.
         this->IssueMessage(MessageType::FATAL_ERROR,
@@ -2224,10 +2221,8 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags,
             "CMAKE_" + lang + "_COMPILE_OPTIONS_WATCOM_RUNTIME_LIBRARY_" +
             watcomRuntimeLibrary)) {
         this->AppendCompileOptions(flags, *watcomRuntimeLibraryOptions);
-      } else if ((this->Makefile->GetSafeDefinition(
-                    "CMAKE_" + lang + "_COMPILER_ID") == "OpenWatcom" ||
-                  this->Makefile->GetSafeDefinition(
-                    "CMAKE_" + lang + "_SIMULATE_ID") == "OpenWatcom") &&
+      } else if ((compiler == "OpenWatcom" ||
+                  compilerSimulateId == "OpenWatcom") &&
                  !cmSystemTools::GetErrorOccurredFlag()) {
         // The compiler uses the Watcom ABI so it needs a known runtime
         // library.
@@ -2239,6 +2234,49 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags,
     }
   }
 
+  // Add MSVC runtime checks flags. This is activated by the presence
+  // of a default selection whether or not it is overridden by a property.
+  cmValue msvcRuntimeChecksDefault =
+    this->Makefile->GetDefinition("CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT");
+  if (cmNonempty(msvcRuntimeChecksDefault)) {
+    cmValue msvcRuntimeChecksValue =
+      target->GetProperty("MSVC_RUNTIME_CHECKS");
+    if (!msvcRuntimeChecksValue) {
+      msvcRuntimeChecksValue = msvcRuntimeChecksDefault;
+    }
+    cmList msvcRuntimeChecksList = cmGeneratorExpression::Evaluate(
+      *msvcRuntimeChecksValue, this, config, target);
+    msvcRuntimeChecksList.remove_duplicates();
+
+    // RTC1/RTCsu VS GUI workaround
+    std::string const stackFrameErrorCheck = "StackFrameErrorCheck";
+    std::string const unitinitializedVariable = "UninitializedVariable";
+    std::string const rtcSU = "RTCsu";
+    if ((cm::contains(msvcRuntimeChecksList, stackFrameErrorCheck) &&
+         cm::contains(msvcRuntimeChecksList, unitinitializedVariable)) ||
+        cm::contains(msvcRuntimeChecksList, rtcSU)) {
+      msvcRuntimeChecksList.remove_items(
+        { stackFrameErrorCheck, unitinitializedVariable, rtcSU });
+      msvcRuntimeChecksList.append(rtcSU);
+    }
+
+    for (std::string const& msvcRuntimeChecks : msvcRuntimeChecksList) {
+      if (cmValue msvcRuntimeChecksOptions =
+            this->Makefile->GetDefinition(cmStrCat(
+              "CMAKE_", lang,
+              "_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_" + msvcRuntimeChecks))) {
+        this->AppendCompileOptions(flags, *msvcRuntimeChecksOptions);
+      } else if ((compiler == "MSVC" || compilerSimulateId == "MSVC") &&
+                 !cmSystemTools::GetErrorOccurredFlag()) {
+        // The compiler uses the MSVC ABI so it needs a known runtime checks.
+        this->IssueMessage(MessageType::FATAL_ERROR,
+                           cmStrCat("MSVC_RUNTIME_CHECKS value '",
+                                    msvcRuntimeChecks, "' not known for this ",
+                                    lang, " compiler."));
+      }
+    }
+  }
+
   // Add MSVC debug information format flags if CMP0141 is NEW.
   if (cm::optional<std::string> msvcDebugInformationFormat =
         this->GetMSVCDebugFormatName(config, target)) {
@@ -2249,10 +2287,7 @@ void cmLocalGenerator::AddLanguageFlags(std::string& flags,
                        "_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_",
                        *msvcDebugInformationFormat))) {
         this->AppendCompileOptions(flags, *msvcDebugInformationFormatOptions);
-      } else if ((this->Makefile->GetSafeDefinition(
-                    cmStrCat("CMAKE_", lang, "_COMPILER_ID")) == "MSVC"_s ||
-                  this->Makefile->GetSafeDefinition(
-                    cmStrCat("CMAKE_", lang, "_SIMULATE_ID")) == "MSVC"_s) &&
+      } else if ((compiler == "MSVC" || compilerSimulateId == "MSVC") &&
                  !cmSystemTools::GetErrorOccurredFlag()) {
         // The compiler uses the MSVC ABI so it needs a known runtime library.
         this->IssueMessage(MessageType::FATAL_ERROR,

+ 4 - 1
Source/cmPolicies.h

@@ -549,7 +549,10 @@ class cmMakefile;
   SELECT(POLICY, CMP0182,                                                     \
          "Create shared library archives by default on AIX.", 4, 0, 0, WARN)  \
   SELECT(POLICY, CMP0183,                                                     \
-         "add_feature_info() supports full Condition Syntax.", 4, 0, 0, WARN)
+         "add_feature_info() supports full Condition Syntax.", 4, 0, 0, WARN) \
+  SELECT(POLICY, CMP0184,                                                     \
+         "MSVC runtime check flags are selected by an abstraction.", 4, 0, 0, \
+         WARN)
 
 #define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
 #define CM_FOR_EACH_POLICY_ID(POLICY)                                         \

+ 2 - 0
Source/cmTarget.cxx

@@ -330,6 +330,7 @@ TargetProperty const StaticTargetProperties[] = {
   { "OSX_ARCHITECTURES"_s, IC::CanCompileSources },
   // ---- Windows
   { "MSVC_DEBUG_INFORMATION_FORMAT"_s, IC::CanCompileSources },
+  { "MSVC_RUNTIME_CHECKS"_s, IC::CanCompileSources },
   { "MSVC_RUNTIME_LIBRARY"_s, IC::CanCompileSources },
   { "VS_JUST_MY_CODE_DEBUGGING"_s, IC::CanCompileSources },
   { "VS_DEBUGGER_COMMAND"_s, IC::ExecutableTarget },
@@ -1763,6 +1764,7 @@ void cmTarget::CopyImportedCxxModulesProperties(cmTarget const* tgt)
     "OSX_ARCHITECTURES",
     // ---- Windows
     "MSVC_DEBUG_INFORMATION_FORMAT",
+    "MSVC_RUNTIME_CHECKS",
     "MSVC_RUNTIME_LIBRARY",
     "VS_PLATFORM_TOOLSET",
     // ---- OpenWatcom

+ 8 - 0
Tests/CMakeLists.txt

@@ -2128,6 +2128,14 @@ if(BUILD_TESTING)
     set_property(TEST MSVCDebugInformationFormat APPEND
       PROPERTY LABELS "CUDA" "Fortran")
 
+    set(MSVCRuntimeChecks_BUILD_OPTIONS -DCMake_TEST_CUDA=${CMake_TEST_CUDA})
+    if(CMAKE_Fortran_COMPILER)
+      list(APPEND MSVCRuntimeChecks_BUILD_OPTIONS -DCMake_TEST_Fortran=1)
+    endif()
+    ADD_TEST_MACRO(MSVCRuntimeChecks)
+    set_property(TEST MSVCRuntimeChecks APPEND
+      PROPERTY LABELS "CUDA" "Fortran")
+
     set(MSVCRuntimeLibrary_BUILD_OPTIONS -DCMake_TEST_CUDA=${CMake_TEST_CUDA})
     ADD_TEST_MACRO(MSVCRuntimeLibrary)
     set_property(TEST MSVCRuntimeLibrary APPEND

+ 1 - 0
Tests/FortranOnly/CMakeLists.txt

@@ -1,4 +1,5 @@
 cmake_minimum_required(VERSION 3.10...3.25) # Enable CMP0091 and CMP0141.
+cmake_policy(SET CMP0184 NEW)
 project(FortranOnly Fortran)
 message("CTEST_FULL_OUTPUT ")
 

+ 105 - 0
Tests/MSVCRuntimeChecks/CMakeLists.txt

@@ -0,0 +1,105 @@
+cmake_minimum_required(VERSION 3.31)
+cmake_policy(SET CMP0184 NEW)
+
+# All runtime checks flags enables single preprocessor definition,
+# so override our table of flags to artificially add a definition we can check.
+set(CMAKE_USER_MAKE_RULES_OVERRIDE_C ${CMAKE_CURRENT_SOURCE_DIR}/override-C.cmake)
+set(CMAKE_USER_MAKE_RULES_OVERRIDE_CXX ${CMAKE_CURRENT_SOURCE_DIR}/override-CXX.cmake)
+set(CMAKE_USER_MAKE_RULES_OVERRIDE_CUDA ${CMAKE_CURRENT_SOURCE_DIR}/override-CUDA.cmake)
+set(CMAKE_USER_MAKE_RULES_OVERRIDE_Fortran ${CMAKE_CURRENT_SOURCE_DIR}/override-Fortran.cmake)
+
+project(MSVCRuntimeChecks)
+if(CMake_TEST_CUDA STREQUAL "NVIDIA")
+  enable_language(CUDA)
+endif()
+if(CMake_TEST_Fortran)
+  enable_language(Fortran)
+endif()
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+
+set(verify_default VERIFY_RTCsu)
+
+set(verify_def_PossibleDataLoss      -DVERIFY_RTCc)
+set(verify_def_StackFrameErrorCheck  -DVERIFY_RTCs)
+set(verify_def_UninitializedVariable -DVERIFY_RTCu)
+set(verify_def_RTCsu                 -DVERIFY_RTCsu)
+
+function(verify_combination format verify_format_defs lang src)
+  # Test that try_compile builds with this runtime check.
+  set(CMAKE_MSVC_RUNTIME_CHECKS "${format}")
+  set(CMAKE_TRY_COMPILE_CONFIGURATION "Debug")
+  set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY")
+
+  string(REPLACE ";" "_" format_var_name "${format}")
+  if (NOT format_var_name)
+    set(format_var_name "Empty")
+  endif()
+
+  if (NOT verify_format_defs)
+    foreach(format_for_def IN LISTS format)
+      list(APPEND verify_format_defs ${verify_def_${format_for_def}})
+    endforeach()
+  endif()
+
+  try_compile(${format_var_name}_COMPILES
+    ${CMAKE_CURRENT_BINARY_DIR}/try_compile/${format_var_name}
+    ${CMAKE_CURRENT_SOURCE_DIR}/${src}
+    COMPILE_DEFINITIONS ${verify_format_defs}
+    CMAKE_FLAGS -DINCLUDE_DIRECTORIES=${CMAKE_CURRENT_SOURCE_DIR}
+    OUTPUT_VARIABLE ${format_var_name}_OUTPUT
+    )
+  if(${format_var_name}_COMPILES)
+    message(STATUS "try_compile ${lang} with \"${format}\" worked")
+  else()
+    string(REPLACE "\n" "\n  " ${format_var_name}_OUTPUT "  ${${format_var_name}_OUTPUT}")
+    message(SEND_ERROR "try_compile ${lang} with \"${format}\" failed:\n${${format_var_name}_OUTPUT}")
+  endif()
+
+  # Test that targets build with this runtime check.
+  set(CMAKE_MSVC_RUNTIME_CHECKS "$<$<BOOL:$<TARGET_PROPERTY:BOOL_TRUE>>:${format}>$<$<BOOL:$<TARGET_PROPERTY:BOOL_FALSE>>:BadContent>")
+  add_library(${format_var_name}-${lang} ${src})
+  set_property(TARGET ${format_var_name}-${lang} PROPERTY BOOL_TRUE TRUE)
+  target_compile_definitions(${format_var_name}-${lang} PRIVATE ${verify_format_defs})
+endfunction()
+
+function(verify lang src)
+  add_library(default-${lang} ${src})
+  target_compile_definitions(default-${lang} PRIVATE "$<$<CONFIG:Debug>:${verify_default}>")
+
+  # zero checkers
+  verify_combination("" "" ${lang} ${src})
+
+  # single checker
+  verify_combination(PossibleDataLoss "" ${lang} ${src})
+  verify_combination(StackFrameErrorCheck "" ${lang} ${src})
+  verify_combination(UninitializedVariable "" ${lang} ${src})
+  verify_combination(RTCsu "" ${lang} ${src})
+
+  # multiple checkers (without RTCsu merging)
+  verify_combination("PossibleDataLoss;StackFrameErrorCheck" "" ${lang} ${src})
+  verify_combination("PossibleDataLoss;UninitializedVariable" "" ${lang} ${src})
+
+  # multiple checkers (only RTCsu merging)
+  set(defs "${verify_def_RTCsu}")
+  verify_combination("StackFrameErrorCheck;UninitializedVariable" "${defs}" ${lang} ${src})
+  verify_combination("StackFrameErrorCheck;RTCsu" "${defs}" ${lang} ${src})
+  verify_combination("UninitializedVariable;RTCsu" "${defs}" ${lang} ${src})
+  verify_combination("StackFrameErrorCheck;UninitializedVariable;RTCsu" "${defs}" ${lang} ${src})
+
+  # multiple checkers (with RTCsu merging)
+  list(APPEND defs "${verify_def_PossibleDataLoss}")
+  verify_combination("PossibleDataLoss;StackFrameErrorCheck;UninitializedVariable" "${defs}" ${lang} ${src})
+  verify_combination("PossibleDataLoss;StackFrameErrorCheck;RTCsu" "${defs}" ${lang} ${src})
+  verify_combination("PossibleDataLoss;UninitializedVariable;RTCsu" "${defs}" ${lang} ${src})
+  verify_combination("PossibleDataLoss;StackFrameErrorCheck;UninitializedVariable;RTCsu" "${defs}" ${lang} ${src})
+endfunction()
+
+verify(C verify.c)
+verify(CXX verify.cxx)
+if(CMake_TEST_CUDA STREQUAL "NVIDIA")
+  verify(CUDA verify.cu)
+endif()
+if(CMake_TEST_Fortran)
+  verify(Fortran verify.F90)
+endif()

+ 3 - 0
Tests/MSVCRuntimeChecks/override-C.cmake

@@ -0,0 +1,3 @@
+set(delimiter ";")
+set(lang "C")
+include(${CMAKE_CURRENT_LIST_DIR}/override-common.cmake)

+ 3 - 0
Tests/MSVCRuntimeChecks/override-CUDA.cmake

@@ -0,0 +1,3 @@
+set(delimiter " ")
+set(lang "CUDA")
+include(${CMAKE_CURRENT_LIST_DIR}/override-common.cmake)

+ 3 - 0
Tests/MSVCRuntimeChecks/override-CXX.cmake

@@ -0,0 +1,3 @@
+set(delimiter ";")
+set(lang "CXX")
+include(${CMAKE_CURRENT_LIST_DIR}/override-common.cmake)

+ 3 - 0
Tests/MSVCRuntimeChecks/override-Fortran.cmake

@@ -0,0 +1,3 @@
+set(delimiter ";")
+set(lang "Fortran")
+include(${CMAKE_CURRENT_LIST_DIR}/override-common.cmake)

+ 50 - 0
Tests/MSVCRuntimeChecks/override-common.cmake

@@ -0,0 +1,50 @@
+if("${CMAKE_${lang}_COMPILER_ID};${CMAKE_${lang}_SIMULATE_ID};${CMAKE_${lang}_COMPILER_FRONTEND_VARIANT}" STREQUAL "Clang;MSVC;GNU")
+  # Clang does not actually support these, so Windows-Clang passes no flags.
+  set(empty_PossibleDataLoss 1)
+  set(empty_StackFrameErrorCheck 1)
+  set(empty_UninitializedVariable 1)
+  set(empty_RTCsu 1)
+elseif("${CMAKE_${lang}_COMPILER_ID};${CMAKE_${lang}_SIMULATE_ID};${lang}" MATCHES "^Intel(LLVM)?;MSVC;Fortran$")
+  # IntelLLVM Fortran does not actually support these, so Windows-IntelLLVM-Fortran passes no flags.
+  set(empty_PossibleDataLoss 1)
+  set(empty_StackFrameErrorCheck 1)
+  set(empty_UninitializedVariable 0) # this one is supported
+  set(empty_RTCsu 1)
+elseif(CMAKE_${lang}_COMPILER_ID STREQUAL "MSVC" AND CMAKE_${lang}_COMPILER_VERSION VERSION_LESS 14)
+  set(empty_PossibleDataLoss 1)
+  set(empty_StackFrameErrorCheck 0)
+  set(empty_UninitializedVariable 1)
+  set(empty_RTCsu 0)
+else()
+  set(empty_PossibleDataLoss 0)
+  set(empty_StackFrameErrorCheck 0)
+  set(empty_UninitializedVariable 0)
+  set(empty_RTCsu 0)
+endif()
+
+set(var "CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_PossibleDataLoss")
+if(empty_PossibleDataLoss AND ${var} STREQUAL "")
+  set("${var}" "-DTEST_RTCc")
+else()
+  string(REPLACE "-RTCc" "-RTCc${delimiter}-DTEST_RTCc" "${var}" "${${var}}")
+endif()
+set(var "CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_StackFrameErrorCheck")
+if(empty_StackFrameErrorCheck AND ${var} STREQUAL "")
+  set("${var}" "-DTEST_RTCs")
+else()
+  string(REPLACE "-RTCs" "-RTCs${delimiter}-DTEST_RTCs" "${var}" "${${var}}")
+  string(REPLACE "-GZ" "-GZ${delimiter}-DTEST_RTCs" "${var}" "${${var}}")
+endif()
+set(var "CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_UninitializedVariable")
+if(empty_UninitializedVariable AND ${var} STREQUAL "")
+  set("${var}" "-DTEST_RTCu")
+else()
+  string(REPLACE "-RTCu" "-RTCu${delimiter}-DTEST_RTCu" "${var}" "${${var}}")
+endif()
+set(var "CMAKE_${lang}_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_RTCsu")
+if(empty_RTCsu AND ${var} STREQUAL "")
+  set("${var}" "-DTEST_RTCsu")
+else()
+  string(REPLACE "-RTC1" "-RTC1${delimiter}-DTEST_RTCsu" "${var}" "${${var}}")
+  string(REPLACE "-GZ" "-GZ${delimiter}-DTEST_RTCsu" "${var}" "${${var}}")
+endif()

+ 1 - 0
Tests/MSVCRuntimeChecks/verify.F90

@@ -0,0 +1 @@
+#include "verify.h"

+ 1 - 0
Tests/MSVCRuntimeChecks/verify.c

@@ -0,0 +1 @@
+#include "verify.h"

+ 1 - 0
Tests/MSVCRuntimeChecks/verify.cu

@@ -0,0 +1 @@
+#include "verify.h"

+ 1 - 0
Tests/MSVCRuntimeChecks/verify.cxx

@@ -0,0 +1 @@
+#include "verify.h"

+ 61 - 0
Tests/MSVCRuntimeChecks/verify.h

@@ -0,0 +1,61 @@
+/* Some compilers ignore the -RTC flags even if specified.  */
+#if (defined(_MSC_VER) && _MSC_VER <= 1310) || defined(__clang__) ||          \
+  defined(__INTEL_LLVM_COMPILER) || defined(__INTEL_COMPILER)
+#  define NO__MSVC_RUNTIME_CHECKS
+#endif
+
+#ifdef VERIFY_RTCc
+#  ifndef TEST_RTCc
+#    error "TEST_RTCc incorrectly not defined by enabled runtime check"
+#  endif
+#  if !defined(__MSVC_RUNTIME_CHECKS) && !defined(NO__MSVC_RUNTIME_CHECKS)
+#    error                                                                    \
+      "__MSVC_RUNTIME_CHECKS incorrectly not defined by enabled RTCc runtime check"
+#  endif
+#else
+#  ifdef TEST_RTCc
+#    error "TEST_RTCc incorrectly defined by disabled runtime check"
+#  endif
+#endif
+
+#ifdef VERIFY_RTCs
+#  ifndef TEST_RTCs
+#    error "TEST_RTCs incorrectly not defined by enabled runtime check"
+#  endif
+#  if !defined(__MSVC_RUNTIME_CHECKS) && !defined(NO__MSVC_RUNTIME_CHECKS)
+#    error                                                                    \
+      "__MSVC_RUNTIME_CHECKS incorrectly not defined by enabled RTCs runtime check"
+#  endif
+#else
+#  ifdef TEST_RTCs
+#    error "TEST_RTCs incorrectly defined by disabled runtime check"
+#  endif
+#endif
+
+#ifdef VERIFY_RTCu
+#  ifndef TEST_RTCu
+#    error "TEST_RTCu incorrectly not defined by enabled runtime check"
+#  endif
+#  if !defined(__MSVC_RUNTIME_CHECKS) && !defined(NO__MSVC_RUNTIME_CHECKS)
+#    error                                                                    \
+      "__MSVC_RUNTIME_CHECKS incorrectly not defined by enabled RTCu runtime check"
+#  endif
+#else
+#  ifdef TEST_RTCu
+#    error "TEST_RTCu incorrectly defined by disabled runtime check"
+#  endif
+#endif
+
+#ifdef VERIFY_RTCsu
+#  ifndef TEST_RTCsu
+#    error "TEST_RTCsu incorrectly not defined by enabled runtime check"
+#  endif
+#  if !defined(__MSVC_RUNTIME_CHECKS) && !defined(NO__MSVC_RUNTIME_CHECKS)
+#    error                                                                    \
+      "__MSVC_RUNTIME_CHECKS incorrectly not defined by enabled RTCsu runtime check"
+#  endif
+#else
+#  ifdef TEST_RTCsu
+#    error "TEST_RTCsu incorrectly defined by disabled runtime check"
+#  endif
+#endif

+ 1 - 0
Tests/RunCMake/CMakeLists.txt

@@ -483,6 +483,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "^(Cray|PGI|NVHPC|XL|XLClang|IBMClang|Fujitsu|F
   add_RunCMake_test(MetaCompileFeatures)
 endif()
 if(MSVC)
+  add_RunCMake_test(MSVCRuntimeChecks)
   add_RunCMake_test(MSVCRuntimeLibrary)
   add_RunCMake_test(MSVCRuntimeTypeInfo)
   add_RunCMake_test(MSVCWarningFlags)

+ 1 - 0
Tests/RunCMake/MSVCRuntimeChecks/CMP0184-NEW-result.txt

@@ -0,0 +1 @@
+1

+ 4 - 0
Tests/RunCMake/MSVCRuntimeChecks/CMP0184-NEW-stderr.txt

@@ -0,0 +1,4 @@
+^CMake Error in CMakeLists.txt:
+  MSVC_RUNTIME_CHECKS value 'BogusValue' not known for this C compiler.
++
+CMake Generate step failed\.  Build files cannot be regenerated correctly\.$

+ 2 - 0
Tests/RunCMake/MSVCRuntimeChecks/CMP0184-NEW.cmake

@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0184 NEW)
+include(CMP0184-common.cmake)

+ 4 - 0
Tests/RunCMake/MSVCRuntimeChecks/CMP0184-NoEffect.cmake

@@ -0,0 +1,4 @@
+include(CMP0184-common.cmake)
+
+# Setting this policy after enable_language command has no effect.
+cmake_policy(SET CMP0184 NEW)

+ 2 - 0
Tests/RunCMake/MSVCRuntimeChecks/CMP0184-OLD.cmake

@@ -0,0 +1,2 @@
+cmake_policy(SET CMP0184 OLD)
+include(CMP0184-common.cmake)

+ 2 - 0
Tests/RunCMake/MSVCRuntimeChecks/CMP0184-WARN.cmake

@@ -0,0 +1,2 @@
+
+include(CMP0184-common.cmake)

+ 25 - 0
Tests/RunCMake/MSVCRuntimeChecks/CMP0184-common.cmake

@@ -0,0 +1,25 @@
+enable_language(C)
+
+cmake_policy(GET CMP0184 cmp0184)
+if(cmp0184 STREQUAL "NEW")
+  if(NOT CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT)
+    message(SEND_ERROR "CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT not set under NEW behavior")
+  endif()
+else()
+  if(CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT)
+    message(SEND_ERROR "CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT is set under OLD behavior")
+  endif()
+endif()
+
+if(cmp0184 STREQUAL "NEW")
+  if(CMAKE_C_FLAGS_DEBUG MATCHES "[/-](RTC1|GZ)( |$)")
+    message(SEND_ERROR "CMAKE_C_FLAGS_DEBUG has -RTC1 flag under NEW behavior:\n ${CMAKE_C_FLAGS_DEBUG}")
+  endif()
+else()
+  if(NOT (CMAKE_C_FLAGS_DEBUG MATCHES "/(RTC1|GZ)( |$)"))
+    message(SEND_ERROR "CMAKE_C_FLAGS_DEBUG does not have /RTC1 flag under OLD behavior:\n ${CMAKE_C_FLAGS_DEBUG}")
+  endif()
+endif()
+
+set(CMAKE_MSVC_RUNTIME_CHECKS BogusValue)
+add_library(foo empty.c)

+ 3 - 0
Tests/RunCMake/MSVCRuntimeChecks/CMakeLists.txt

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.31)
+project(${RunCMake_TEST} NONE)
+include(${RunCMake_TEST}.cmake)

+ 6 - 0
Tests/RunCMake/MSVCRuntimeChecks/RunCMakeTest.cmake

@@ -0,0 +1,6 @@
+include(RunCMake)
+
+run_cmake(CMP0184-WARN)
+run_cmake(CMP0184-OLD)
+run_cmake(CMP0184-NEW)
+run_cmake(CMP0184-NoEffect)

+ 0 - 0
Tests/RunCMake/MSVCRuntimeChecks/empty.c


+ 1 - 0
Tests/VSMARMASM/CMakeLists.txt

@@ -1,4 +1,5 @@
 cmake_minimum_required(VERSION 3.25) # Enable CMP0141
+cmake_policy(SET CMP0184 NEW)
 project(VSMARMASM C ASM_MARMASM)
 add_executable(VSMARMASM main.c foo.asm)
 target_compile_options(VSMARMASM PRIVATE

+ 1 - 0
Tests/VSMASM/CMakeLists.txt

@@ -1,4 +1,5 @@
 cmake_minimum_required(VERSION 3.25) # Enable CMP0141
+cmake_policy(SET CMP0184 NEW)
 project(VSMASM C ASM_MASM)
 if(CMAKE_SIZEOF_VOID_P EQUAL 8)
   add_definitions(-DTESTx64)