From 290884a7b95d86a5383af4031d9aa8dc4875f15c Mon Sep 17 00:00:00 2001 From: statementreply Date: Sat, 24 Jun 2023 19:04:30 +0800 Subject: [PATCH 1/5] Add [[msvc::lifetimebound]] to `minmax` --- stl/inc/algorithm | 20 ++++++++++++++++---- stl/inc/yvals_core.h | 16 ++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 7a8f91dc1aa..6c81e7def30 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -21,6 +21,12 @@ _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new +// TRANSITION, non-_Ugly attribute tokens +#pragma push_macro("msvc") +#pragma push_macro("lifetimebound") +#undef msvc +#undef lifetimebound + #if _USE_STD_VECTOR_ALGORITHMS _EXTERN_C @@ -10003,8 +10009,9 @@ namespace ranges { #endif // _HAS_CXX17 _EXPORT_STD template -_NODISCARD constexpr pair minmax(const _Ty& _Left, const _Ty& _Right, _Pr _Pred) noexcept( - noexcept(_DEBUG_LT_PRED(_Pred, _Right, _Left))) /* strengthened */ { +_NODISCARD constexpr pair minmax(const _Ty& _Left _MSVC_LIFETIMEBOUND, + const _Ty& _Right _MSVC_LIFETIMEBOUND, + _Pr _Pred) noexcept(noexcept(_DEBUG_LT_PRED(_Pred, _Right, _Left))) /* strengthened */ { // return pair(leftmost/smaller, rightmost/larger) of _Left and _Right if (_DEBUG_LT_PRED(_Pred, _Right, _Left)) { return {_Right, _Left}; @@ -10021,8 +10028,8 @@ _NODISCARD constexpr pair<_Ty, _Ty> minmax(initializer_list<_Ty> _Ilist, _Pr _Pr } _EXPORT_STD template -_NODISCARD constexpr pair minmax(const _Ty& _Left, const _Ty& _Right) noexcept( - noexcept(_Right < _Left)) /* strengthened */ { +_NODISCARD constexpr pair minmax(const _Ty& _Left _MSVC_LIFETIMEBOUND, + const _Ty& _Right _MSVC_LIFETIMEBOUND) noexcept(noexcept(_Right < _Left)) /* strengthened */ { // return pair(leftmost/smaller, rightmost/larger) of _Left and _Right if (_Right < _Left) { _STL_ASSERT(!(_Left < _Right), "invalid comparator"); @@ -10673,6 +10680,11 @@ namespace ranges { #endif // _HAS_CXX17 _STD_END + +// TRANSITION, non-_Ugly attribute tokens +#pragma pop_macro("lifetimebound") +#pragma pop_macro("msvc") + #pragma pop_macro("new") _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index ceb2fce5ed8..3e7f53ffaa3 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -663,10 +663,12 @@ #pragma push_macro("known_semantics") #pragma push_macro("noop_dtor") #pragma push_macro("intrinsic") +#pragma push_macro("lifetimebound") #undef msvc #undef known_semantics #undef noop_dtor #undef intrinsic +#undef lifetimebound #ifndef __has_cpp_attribute #define _HAS_MSVC_ATTRIBUTE(x) 0 @@ -700,7 +702,21 @@ #define _MSVC_INTRINSIC #endif +// Should we enable [[msvc::lifetimebound]] or [[clang::lifetimebound]] warnings? +#ifndef _SILENCE_LIFETIMEBOUND_WARNING +#if _HAS_MSVC_ATTRIBUTE(lifetimebound) +#define _MSVC_LIFETIMEBOUND [[msvc::lifetimebound]] +#elif __has_cpp_attribute(_Clang::__lifetimebound__) +#define _MSVC_LIFETIMEBOUND [[_Clang::__lifetimebound__]] +#else +#define _MSVC_LIFETIMEBOUND +#endif +#else // ^^^ warning enabled / warning disabled vvv +#define _MSVC_LIFETIMEBOUND +#endif // ^^^ warning disabled ^^^ + #undef _HAS_MSVC_ATTRIBUTE +#pragma pop_macro("lifetimebound") #pragma pop_macro("intrinsic") #pragma pop_macro("noop_dtor") #pragma pop_macro("known_semantics") From c285d9031fb8e72f67f1a47ab03fc9fa10a35b1c Mon Sep 17 00:00:00 2001 From: statementreply Date: Sat, 24 Jun 2023 19:07:14 +0800 Subject: [PATCH 2/5] Add C++ Core Guidelines code analysis rule for [[msvc::lifetimebound]] --- tests/std/lit.site.cfg.in | 2 ++ tests/std/rulesets/stl.ruleset | 11 +++++++++++ tests/std/test.lst | 1 + .../tests/GH_002094_cpp_core_guidelines/env.lst | 16 ++++++++++++++++ .../test.compile.pass.cpp | 4 ++++ tests/utils/stl/test/format.py | 2 +- tests/utils/stl/test/tests.py | 14 ++++++++++++++ 7 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 tests/std/rulesets/stl.ruleset create mode 100644 tests/std/tests/GH_002094_cpp_core_guidelines/env.lst create mode 100644 tests/std/tests/GH_002094_cpp_core_guidelines/test.compile.pass.cpp diff --git a/tests/std/lit.site.cfg.in b/tests/std/lit.site.cfg.in index 78cdb1a7130..9941c44e79c 100644 --- a/tests/std/lit.site.cfg.in +++ b/tests/std/lit.site.cfg.in @@ -19,12 +19,14 @@ config.test_format = stl.test.format.STLTestFormat() lit_config.expected_results = getattr(lit_config, 'expected_results', dict()) lit_config.include_dirs = getattr(lit_config, 'include_dirs', dict()) lit_config.library_dirs = getattr(lit_config, 'library_dirs', dict()) +lit_config.ruleset_dirs = getattr(lit_config, 'ruleset_dirs', dict()) lit_config.test_subdirs = getattr(lit_config, 'test_subdirs', dict()) lit_config.expected_results[config.name] = stl.test.file_parsing.parse_result_file('@STD_EXPECTED_RESULTS@') lit_config.include_dirs[config.name] = \ ['@STL_TESTED_HEADERS_DIR@', '@LIBCXX_SOURCE_DIR@/test/support', '@STL_SOURCE_DIR@/tests/std/include'] lit_config.library_dirs[config.name] = ['@STL_LIBRARY_OUTPUT_DIRECTORY@', '@TOOLSET_LIB@'] +lit_config.ruleset_dirs[config.name] = ['@STL_SOURCE_DIR@/tests/std/rulesets'] lit_config.test_subdirs[config.name] = ['@CMAKE_CURRENT_SOURCE_DIR@/tests'] lit_config.cxx_headers = '@STL_TESTED_HEADERS_DIR@' diff --git a/tests/std/rulesets/stl.ruleset b/tests/std/rulesets/stl.ruleset new file mode 100644 index 00000000000..7859a44da86 --- /dev/null +++ b/tests/std/rulesets/stl.ruleset @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/tests/std/test.lst b/tests/std/test.lst index 3059eef1402..38cb39b191b 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -200,6 +200,7 @@ tests\GH_002030_asan_annotate_vector tests\GH_002039_byte_is_not_trivially_swappable tests\GH_002045_put_time_changes_errno tests\GH_002058_debug_iterator_race +tests\GH_002094_cpp_core_guidelines tests\GH_002120_streambuf_seekpos_and_seekoff tests\GH_002168_regex_overflow tests\GH_002206_unreserved_names diff --git a/tests/std/tests/GH_002094_cpp_core_guidelines/env.lst b/tests/std/tests/GH_002094_cpp_core_guidelines/env.lst new file mode 100644 index 00000000000..56a4f87e0b0 --- /dev/null +++ b/tests/std/tests/GH_002094_cpp_core_guidelines/env.lst @@ -0,0 +1,16 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\prefix.lst +RUNALL_CROSSLIST +PM_CL="/EHsc /w14640 /Zc:threadSafeInit-" +RUNALL_CROSSLIST +PM_CL="/MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++14 /analyze:only /analyze:autolog- /analyze:plugin EspXEngine.dll /analyze:ruleset stl.ruleset" +PM_CL="/MDd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++17 /permissive- /analyze:only /analyze:autolog- /analyze:plugin EspXEngine.dll /analyze:ruleset stl.ruleset" +PM_CL="/MDd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++20 /permissive- /analyze:only /analyze:autolog- /analyze:plugin EspXEngine.dll /analyze:ruleset stl.ruleset" +PM_CL="/MT /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /analyze:only /analyze:autolog- /analyze:plugin EspXEngine.dll /analyze:ruleset stl.ruleset" +PM_CL="/MTd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++latest /permissive- /analyze:only /analyze:autolog- /analyze:plugin EspXEngine.dll /analyze:ruleset stl.ruleset" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /MD /std:c++14" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /MDd /std:c++17" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /MT /std:c++20 /permissive-" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /MTd /std:c++latest /permissive-" diff --git a/tests/std/tests/GH_002094_cpp_core_guidelines/test.compile.pass.cpp b/tests/std/tests/GH_002094_cpp_core_guidelines/test.compile.pass.cpp new file mode 100644 index 00000000000..1132aa12357 --- /dev/null +++ b/tests/std/tests/GH_002094_cpp_core_guidelines/test.compile.pass.cpp @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include <__msvc_all_public_headers.hpp> diff --git a/tests/utils/stl/test/format.py b/tests/utils/stl/test/format.py index 7de5471eda7..096a0a7907c 100644 --- a/tests/utils/stl/test/format.py +++ b/tests/utils/stl/test/format.py @@ -145,7 +145,7 @@ class SharedState: env: Dict[str, str] = field(default_factory=dict) execDir, _ = test.getTempPaths() - shared = SharedState(None, execDir, copy.deepcopy(litConfig.test_env)) + shared = SharedState(None, execDir, _mergeEnvironments(litConfig.test_env, test.env)) shared.env['TMP'] = execDir shared.env['TEMP'] = execDir shared.env['TMPDIR'] = execDir diff --git a/tests/utils/stl/test/tests.py b/tests/utils/stl/test/tests.py index fc0b0e2fafe..21f0bb9921a 100644 --- a/tests/utils/stl/test/tests.py +++ b/tests/utils/stl/test/tests.py @@ -38,6 +38,7 @@ def __init__(self, suite, pathInSuite, litConfig, testConfig, envlstEntry, envNu def configureTest(self, litConfig): self.compileFlags = [] self.cxx = None + self.env = {} self.fileDependencies = [] self.flags = [] self.isenseRspPath = None @@ -234,7 +235,13 @@ def _addCustomFeature(self, name): def _parseFlags(self, litConfig): foundStd = False foundCRT = False + afterAnalyzePlugin = False for flag in chain(self.flags, self.compileFlags, self.linkFlags): + if afterAnalyzePlugin: + if 'EspXEngine.dll'.casefold() in flag.casefold(): + self._addCustomFeature('espxengine') + afterAnalyzePlugin = False + if flag[1:5] == 'std:': foundStd = True if flag[5:] == 'c++latest': @@ -279,6 +286,8 @@ def _parseFlags(self, litConfig): self._addCustomFeature('MT') self._addCustomFeature('static_CRT') foundCRT = True + elif flag[1:] == 'analyze:plugin': + afterAnalyzePlugin = True if not foundStd: self._addCustomFeature('c++14') @@ -294,6 +303,11 @@ def _parseFlags(self, litConfig): if 'asan' in self.config.available_features and 'clang' in self.config.available_features: self.linkFlags.append("/INFERASANLIBS") + # code analysis settings + if 'espxengine' in self.config.available_features: + self.compileFlags.extend(["/analyze:rulesetdirectory", ';'.join(litConfig.ruleset_dirs[self.config.name])]) + self.env['Esp.Extensions'] = 'CppCoreCheck.dll' + self.env['Esp.AnnotationBuildLevel'] = 'Ignore' class LibcxxTest(STLTest): def getTestName(self): From ea54fb430b98f7fc4a9672efd6dd4574f964c30a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 6 Jul 2023 16:59:02 -0700 Subject: [PATCH 3/5] Check whether `__has_cpp_attribute` is usable. --- stl/inc/yvals_core.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 3e7f53ffaa3..302292aacd2 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -703,17 +703,15 @@ #endif // Should we enable [[msvc::lifetimebound]] or [[clang::lifetimebound]] warnings? -#ifndef _SILENCE_LIFETIMEBOUND_WARNING -#if _HAS_MSVC_ATTRIBUTE(lifetimebound) +#if !defined(__has_cpp_attribute) || defined(_SILENCE_LIFETIMEBOUND_WARNING) +#define _MSVC_LIFETIMEBOUND +#elif _HAS_MSVC_ATTRIBUTE(lifetimebound) #define _MSVC_LIFETIMEBOUND [[msvc::lifetimebound]] #elif __has_cpp_attribute(_Clang::__lifetimebound__) #define _MSVC_LIFETIMEBOUND [[_Clang::__lifetimebound__]] #else #define _MSVC_LIFETIMEBOUND #endif -#else // ^^^ warning enabled / warning disabled vvv -#define _MSVC_LIFETIMEBOUND -#endif // ^^^ warning disabled ^^^ #undef _HAS_MSVC_ATTRIBUTE #pragma pop_macro("lifetimebound") From 84673f54606076235b9b2ea6bfa6902b2b9aad3c Mon Sep 17 00:00:00 2001 From: statementreply Date: Sat, 8 Jul 2023 14:09:33 +0800 Subject: [PATCH 4/5] Add [[msvc::lifetimebound]] to `ranges::minmax` --- stl/inc/algorithm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 6c81e7def30..466fbf25731 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -10056,8 +10056,8 @@ namespace ranges { template > _Pr = ranges::less> - _NODISCARD constexpr minmax_result operator()( - const _Ty& _Left, const _Ty& _Right, _Pr _Pred = {}, _Pj _Proj = {}) const { + _NODISCARD constexpr minmax_result operator()(const _Ty& _Left _MSVC_LIFETIMEBOUND, + const _Ty& _Right _MSVC_LIFETIMEBOUND, _Pr _Pred = {}, _Pj _Proj = {}) const { if (_STD invoke(_Pred, _STD invoke(_Proj, _Right), _STD invoke(_Proj, _Left))) { return {_Right, _Left}; } else { From f3f447f41752f71f52d6b19e8f626a30ae329a37 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Thu, 13 Jul 2023 12:16:00 -0700 Subject: [PATCH 5/5] Skip GH_002094_cpp_core_guidelines in the MSVC-internal test harness for now. --- tests/std/test.lst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/std/test.lst b/tests/std/test.lst index 38cb39b191b..154fa991f48 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -200,7 +200,8 @@ tests\GH_002030_asan_annotate_vector tests\GH_002039_byte_is_not_trivially_swappable tests\GH_002045_put_time_changes_errno tests\GH_002058_debug_iterator_race -tests\GH_002094_cpp_core_guidelines +# Needs special machinery to work in the MSVC-internal test harness, not yet implemented: +# tests\GH_002094_cpp_core_guidelines tests\GH_002120_streambuf_seekpos_and_seekoff tests\GH_002168_regex_overflow tests\GH_002206_unreserved_names