From 082d80ae53925cad65e5f0b6a3ca00fd786fb96d Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 29 Jul 2025 15:42:56 -0700 Subject: [PATCH 01/24] Update WinSDK comments (#5659) --- CMakeLists.txt | 8 +++----- README.md | 4 ++-- tests/utils/stl/test/tests.py | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5064973ba73..2f6d1e4dda9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,7 +18,7 @@ int main() {} ]=] WINDOWS_SDK_VERSION_CHECK) if(NOT WINDOWS_SDK_VERSION_CHECK) - message(FATAL_ERROR "The STL must be built with the Windows 11 SDK (10.0.26100.3916) or later. Make sure it's available by selecting it in the Individual Components tab of the VS Installer.") + message(FATAL_ERROR "The STL must be built with the Windows 11 SDK (10.0.26100.4188) or later. Make sure it's available by selecting it in the Individual Components tab of the VS Installer.") endif() if(NOT DEFINED VCLIBS_TARGET_ARCHITECTURE) @@ -92,20 +92,18 @@ set(STL_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out/lib/${VCLIBS_I386_OR set(STL_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out/lib/${VCLIBS_I386_OR_AMD64}") set(STL_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/out/bin/${VCLIBS_I386_OR_AMD64}") -# TRANSITION, update internal crt-common.settings.targets, atlmfc.settings.targets, and fe-components.settings.targets -# from NTDDI_WIN10_NI to NTDDI_WIN11_GE when the internal WinSDK is updated. add_compile_definitions( _ALLOW_ITERATOR_DEBUG_LEVEL_MISMATCH WIN32_LEAN_AND_MEAN STRICT _CRT_STDIO_ARBITRARY_WIDE_SPECIFIERS _WIN32_WINNT=0x0A00 NTDDI_VERSION=NTDDI_WIN11_GE) if(STL_USE_ANALYZE) - # TRANSITION, Windows SDK 10.0.26100.3916 emits + # TRANSITION, Windows SDK 10.0.26100.4188 emits # "warning C6553: The annotation for function 'LCMapStringEx' on _Param_(9) does not apply to a value type." # Reported as OS-40109504 "Windows SDK: incorrect SAL annotations on functions the STL uses". add_compile_options("$<$:/analyze:autolog-;/wd6553>") if(VCLIBS_TARGET_ARCHITECTURE STREQUAL "arm64ec") - # TRANSITION, Windows SDK 10.0.26100.3916 emits + # TRANSITION, Windows SDK 10.0.26100.4188 emits # "warning C28301: No annotations for first declaration of 'meow'" # for various intrinsics when building for ARM64EC. add_compile_options("$<$:/wd28301>") diff --git a/README.md b/README.md index d7b661218fb..e1172f370c0 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,7 @@ Just try to follow these rules, so we can spend more time fixing bugs and implem # How To Build With The Visual Studio IDE 1. Install Visual Studio 2022 17.14.8 Preview 1 or later. - * Select "Windows 11 SDK (10.0.26100.3916)" in the VS Installer. + * Select "Windows 11 SDK (10.0.26100.4188)" in the VS Installer. * Select "MSVC v143 - VS 2022 C++ ARM64/ARM64EC build tools (Latest)" in the VS Installer if you would like to build the ARM64/ARM64EC target. * We recommend selecting "C++ CMake tools for Windows" in the VS Installer. @@ -159,7 +159,7 @@ Just try to follow these rules, so we can spend more time fixing bugs and implem # How To Build With A Native Tools Command Prompt 1. Install Visual Studio 2022 17.14.8 Preview 1 or later. - * Select "Windows 11 SDK (10.0.26100.3916)" in the VS Installer. + * Select "Windows 11 SDK (10.0.26100.4188)" in the VS Installer. * Select "MSVC v143 - VS 2022 C++ ARM64/ARM64EC build tools (Latest)" in the VS Installer if you would like to build the ARM64/ARM64EC target. * We recommend selecting "C++ CMake tools for Windows" in the VS Installer. diff --git a/tests/utils/stl/test/tests.py b/tests/utils/stl/test/tests.py index b049c185fa8..f8762f1cb2d 100644 --- a/tests/utils/stl/test/tests.py +++ b/tests/utils/stl/test/tests.py @@ -258,7 +258,7 @@ def _handleEnvlst(self, litConfig): self.compileFlags.append('/arm64EC') self.linkFlags.append('/machine:arm64ec') - # TRANSITION, Windows SDK 10.0.26100.3916 emits + # TRANSITION, Windows SDK 10.0.26100.4188 emits # "warning C28301: No annotations for first declaration of 'meow'" # for various intrinsics when building for ARM64EC. self.compileFlags.append('/wd28301') From 52e35aa6e01d112c3ff5c2c48c25fc060ee97cb4 Mon Sep 17 00:00:00 2001 From: Jonathan Emmett Date: Fri, 1 Aug 2025 15:16:48 -0300 Subject: [PATCH 02/24] Remove MSVC-specific workarounds for references-to-functions (#5661) --- .../P0896R4_ranges_iterator_machinery/test.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp index abc1699c201..9b09228e279 100644 --- a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp @@ -1073,15 +1073,9 @@ namespace iterator_cust_move_test { static_assert(noexcept(ranges::iter_move(static_cast(&some_ints[2])))); static_assert(same_as, int&&>); -#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1008447 +#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1008447, VSO-2066340 static_assert(same_as, int (&)(int)>); -#else // ^^^ no workaround / workaround vvv -#ifdef _MSVC_INTERNAL_TESTING // TRANSITION, assertion will fire once VSO-2066340 ships. - static_assert(same_as, int (&&)(int)>); -#else // ^^^ defined(_MSVC_INTERNAL_TESTING) / !defined(_MSVC_INTERNAL_TESTING) vvv - static_assert(same_as, int (*)(int)>); -#endif // ^^^ !defined(_MSVC_INTERNAL_TESTING) -#endif // ^^^ workaround ^^^ +#endif // ^^^ no workaround ^^^ static_assert(same_as, int&&>); static_assert(ranges::iter_move(some_ints) == 0); @@ -1092,9 +1086,7 @@ namespace iterator_cust_move_test { } #if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1008447 static_assert(same_as, int (&)(int)>); -#else // ^^^ no workaround / workaround vvv - static_assert(same_as, int (&&)(int)>); -#endif // ^^^ workaround ^^^ +#endif // ^^^ no workaround ^^^ static_assert(ranges::iter_move(&f)(42) == 43); static_assert(noexcept(ranges::iter_move(&f))); From 907f3e75f0252858d0c8247fdf423139ed313709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20M=C3=BCller?= Date: Fri, 8 Aug 2025 18:49:23 +0200 Subject: [PATCH 03/24] ``: Suppress code analysis warning C6510 for `basic_string` (#5563) Co-authored-by: Stephan T. Lavavej --- stl/inc/xstring | 7 ++ tests/std/tests/P0220R1_string_view/test.cpp | 75 ++++++++++++++++++-- 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/stl/inc/xstring b/stl/inc/xstring index 0ac07f32221..b49bb137779 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -580,6 +580,11 @@ concept _Contiguous_range_of = (_RANGES contiguous_range<_Rng>) && same_as>, _Ty>; #endif // _HAS_CXX23 +#pragma warning(push) +// Invalid annotation: 'NullTerminated' property may only be used on buffers whose elements are of integral or pointer +// type +#pragma warning(disable : 6510) + _EXPORT_STD template , class _Alloc = allocator<_Elem>> class basic_string { // null-terminated transparent array of elements private: @@ -3195,6 +3200,8 @@ private: _Compressed_pair<_Alty, _Scary_val> _Mypair; }; +#pragma warning(pop) + #if _HAS_CXX17 template >, enable_if_t, _Is_allocator<_Alloc>>, int> = 0> diff --git a/tests/std/tests/P0220R1_string_view/test.cpp b/tests/std/tests/P0220R1_string_view/test.cpp index dac4ec93ae0..edc4236ae50 100644 --- a/tests/std/tests/P0220R1_string_view/test.cpp +++ b/tests/std/tests/P0220R1_string_view/test.cpp @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#include #include #include #include @@ -1229,14 +1230,20 @@ static_assert(!is_constructible_v); static_assert(!is_assignable_v); #endif // _HAS_CXX23 -// Also test that no C6510 warning +// Also test that basic_string_view produces no C6510 warning +// when instantiated with custom character types. +// GH-5563: Extend coverage to basic_string struct char_wrapper { char c; }; template <> struct std::char_traits { - using char_type = char_wrapper; + using char_type = char_wrapper; + using int_type = int; + using pos_type = char_traits::pos_type; + using off_type = char_traits::off_type; + using state_type = char_traits::state_type; static bool eq(char_wrapper lhs, char_wrapper rhs) { return lhs.c == rhs.c; @@ -1247,18 +1254,76 @@ struct std::char_traits { return strlen(reinterpret_cast(a)); } + static char_wrapper* copy(char_wrapper* const first1, const char_wrapper* const first2, const size_t count) { + copy_n(first2, count, first1); + return first1; + } + + static char_wrapper* move(char_wrapper* const first1, const char_wrapper* const first2, const size_t count) { + memmove(first1, first2, count * sizeof(char_wrapper)); + return first1; + } + static int compare(const char_wrapper* lhs, const char_wrapper* rhs, size_t count) { return char_traits::compare( reinterpret_cast(lhs), reinterpret_cast(rhs), count); } -}; -using WrappedSV = basic_string_view>; + static const char_wrapper* find(const char_wrapper* first, size_t count, const char_wrapper& ch) { + for (; 0 < count; --count, ++first) { + if (eq(*first, ch)) { + return first; + } + } + + return nullptr; + } + + static char_wrapper* assign(char_wrapper* const first, size_t count, const char_wrapper ch) { + for (char_wrapper* next = first; count > 0; --count, ++next) { + *next = ch; + } + + return first; + } + + static void assign(char_wrapper& left, const char_wrapper& right) { + left = right; + } + + static bool lt(const char_wrapper left, const char_wrapper right) { + return char_traits::lt(left.c, right.c); + } + + static char_wrapper to_char_type(const int_type meta) { + return {char_traits::to_char_type(meta)}; + } + + static int_type to_int_type(const char_wrapper ch) { + return char_traits::to_int_type(ch.c); + } + + static bool eq_int_type(const int_type left, const int_type right) { + return char_traits::eq_int_type(left, right); + } + + static int_type not_eof(const int_type meta) { + return char_traits::not_eof(meta); + } + + static int_type eof() { + return char_traits::eof(); + } +}; void test_C6510_warning() { // compile-only char_wrapper a[] = {{'a'}, {'b'}, {'c'}, {'\0'}}; - WrappedSV sv(a); + basic_string_view sv(a); (void) sv; + + // GH-5563: Extend test coverage to basic_string + basic_string s(a); + (void) s; } #if _HAS_CXX20 From 72d8110942854ad16308779b6a53020cbab28658 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sat, 9 Aug 2025 00:55:27 +0800 Subject: [PATCH 04/24] Speculatively implement LWG-4270 Diagnose misuse of `std::projected::operator*` (#5572) Co-authored-by: Stephan T. Lavavej --- stl/inc/xutility | 35 ++++++++++++++++--- tests/libcxx/expected_results.txt | 9 ----- .../test.cpp | 4 --- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index c38cab33263..5805b9012a6 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -866,8 +866,36 @@ namespace ranges { template concept _Can_deref = requires(_Ty&& __t) { *static_cast<_Ty&&>(__t); }; + struct _Must_have_ADL_found_iter_move_or_be_dereferenceable {}; + class _Cpo { private: + template + _NODISCARD static auto _Choose_return_type() { + if constexpr (_Has_ADL<_Ty>) { + return type_identity()))>{}; // intentional ADL + } else if constexpr (_Can_deref<_Ty>) { + using _Ref = decltype(*_STD declval<_Ty>()); +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-1008447, VSO-2066340 + if constexpr (is_function_v>) { + return type_identity<_Ref&>{}; + } else +#endif // ^^^ workaround ^^^ + { + if constexpr (is_lvalue_reference_v<_Ref>) { + return type_identity&&>{}; + } else { + return type_identity<_Ref>{}; + } + } + } else { + return _Must_have_ADL_found_iter_move_or_be_dereferenceable{}; + } + } + + template + using _Return_type = decltype(_Choose_return_type<_Ty>())::type; + enum class _St { _None, _Custom, _Fallback }; template @@ -886,8 +914,7 @@ namespace ranges { public: template - requires (_Choice<_Ty>._Strategy != _St::_None) - _NODISCARD _STATIC_CALL_OPERATOR constexpr decltype(auto) operator()(_Ty&& _Val) _CONST_CALL_OPERATOR + _NODISCARD _STATIC_CALL_OPERATOR constexpr _Return_type<_Ty> operator()(_Ty&& _Val) _CONST_CALL_OPERATOR noexcept(_Choice<_Ty>._No_throw) { constexpr _St _Strat = _Choice<_Ty>._Strategy; @@ -1121,8 +1148,8 @@ struct _Projected_impl { using value_type = remove_cvref_t>; [[noreturn]] indirect_result_t<_Proj&, _It> operator*() const { - _STL_REPORT_ERROR("std::projected::operator*() can't be called (N5008 [projected]/1)"); - _STL_UNREACHABLE; // no return value available for "continue on error" + static_assert( + false, "std::projected::operator*() can't be instantiated (N5008 [projected] as modified by LWG-4270)"); } }; }; diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 984f683b573..0e91de44821 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -1164,12 +1164,6 @@ std/containers/sequences/vector.bool/vector_bool.pass.cpp FAIL # Not analyzed. Inspecting shift operators for quoted(). std/input.output/iostream.format/quoted.manip/quoted_traits.compile.pass.cpp FAIL -# Not analyzed. -# MSVC warning C5046: 'test_undefined_internal::A::operator *': Symbol involving type with internal linkage not defined -# Clang error: function 'test_undefined_internal()::A::operator*' has internal linkage but is not defined [-Werror,-Wundefined-internal] -std/iterators/iterator.requirements/iterator.cust/iterator.cust.move/iter_rvalue_reference_t.compile.pass.cpp FAIL -std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.comp/op_spaceship.pass.cpp FAIL - # Not analyzed. Failing assert(arr[0].moves() == 1 && arr[1].moves() == 3). std/iterators/iterator.requirements/iterator.cust/iterator.cust.swap/iter_swap.pass.cpp FAIL @@ -1518,9 +1512,6 @@ std/algorithms/alg.sorting/alg.set.operations/set.intersection/set_intersection_ # Clang assertion: std::hermite(n, +inf) == inf std/numerics/c.math/hermite.pass.cpp FAIL -# Not analyzed. Test coverage for LLVM-104496 uses span. -std/containers/views/views.span/span.cons/copy.pass.cpp FAIL - # Not analyzed. These tests disable or limit allocations, which interferes with our proxy objects. std/algorithms/alg.sorting/alg.sort/stable.sort/stable_sort.pass.cpp FAIL std/containers/sequences/vector/vector.capacity/shrink_to_fit_exceptions.pass.cpp FAIL diff --git a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp index 9b09228e279..aa4834cdeb3 100644 --- a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp @@ -1073,9 +1073,7 @@ namespace iterator_cust_move_test { static_assert(noexcept(ranges::iter_move(static_cast(&some_ints[2])))); static_assert(same_as, int&&>); -#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1008447, VSO-2066340 static_assert(same_as, int (&)(int)>); -#endif // ^^^ no workaround ^^^ static_assert(same_as, int&&>); static_assert(ranges::iter_move(some_ints) == 0); @@ -1084,9 +1082,7 @@ namespace iterator_cust_move_test { constexpr int f(int i) noexcept { return i + 1; } -#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-1008447 static_assert(same_as, int (&)(int)>); -#endif // ^^^ no workaround ^^^ static_assert(ranges::iter_move(&f)(42) == 43); static_assert(noexcept(ranges::iter_move(&f))); From 96f154a2f08e2b4794a785c9c95a51ccd9395d47 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sat, 9 Aug 2025 00:59:58 +0800 Subject: [PATCH 05/24] Speculatively implement LWG-4273 Standard execution policy types should be conventional tag class types (#5578) Co-authored-by: Stephan T. Lavavej --- stl/inc/execution | 16 +- tests/std/test.lst | 1 + tests/std/tests/LWG2510_tag_classes/env.lst | 4 + .../LWG2510_tag_classes/test.compile.pass.cpp | 207 ++++++++++++++++++ .../test.cpp | 30 +-- 5 files changed, 233 insertions(+), 25 deletions(-) create mode 100644 tests/std/tests/LWG2510_tag_classes/env.lst create mode 100644 tests/std/tests/LWG2510_tag_classes/test.compile.pass.cpp diff --git a/stl/inc/execution b/stl/inc/execution index 5f5134f768f..3e890f8aed6 100644 --- a/stl/inc/execution +++ b/stl/inc/execution @@ -73,23 +73,27 @@ namespace execution { _EXPORT_STD class sequenced_policy { // indicates support for only sequential execution, and requests termination on exceptions public: + explicit sequenced_policy() = default; // per LWG-4273 + using _Standard_execution_policy = int; static constexpr bool _Parallelize = false; static constexpr bool _Ivdep = false; }; - _EXPORT_STD inline constexpr sequenced_policy seq{/* unspecified */}; + _EXPORT_STD inline constexpr sequenced_policy seq{}; _EXPORT_STD class parallel_policy { // indicates support by element access functions for parallel execution with parallel forward progress // guarantees, and requests termination on exceptions public: + explicit parallel_policy() = default; // per LWG-4273 + using _Standard_execution_policy = int; static constexpr bool _Parallelize = true; static constexpr bool _Ivdep = true; }; - _EXPORT_STD inline constexpr parallel_policy par{/* unspecified */}; + _EXPORT_STD inline constexpr parallel_policy par{}; _EXPORT_STD class parallel_unsequenced_policy { // indicates support by element access functions for parallel execution with weakly parallel forward progress @@ -97,12 +101,14 @@ namespace execution { // // (at this time, equivalent to parallel_policy) public: + explicit parallel_unsequenced_policy() = default; // per LWG-4273 + using _Standard_execution_policy = int; static constexpr bool _Parallelize = true; static constexpr bool _Ivdep = true; }; - _EXPORT_STD inline constexpr parallel_unsequenced_policy par_unseq{/* unspecified */}; + _EXPORT_STD inline constexpr parallel_unsequenced_policy par_unseq{}; #if _HAS_CXX20 _EXPORT_STD class unsequenced_policy { @@ -112,12 +118,14 @@ namespace execution { // (at this time, equivalent to sequenced_policy except for for_each(_n), destroy(_n), // uninitialized_default_construct(_n), and uninitialized_value_construct(_n)) public: + explicit unsequenced_policy() = default; // per LWG-4273 + using _Standard_execution_policy = int; static constexpr bool _Parallelize = false; static constexpr bool _Ivdep = true; }; - _EXPORT_STD inline constexpr unsequenced_policy unseq{/* unspecified */}; + _EXPORT_STD inline constexpr unsequenced_policy unseq{}; #endif // _HAS_CXX20 } // namespace execution diff --git a/tests/std/test.lst b/tests/std/test.lst index 666f255aae7..2ef39ff142a 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -268,6 +268,7 @@ tests\GH_005421_vector_algorithms_integer_class_type_iterator tests\GH_005472_do_not_overlap tests\GH_005553_regex_character_translation tests\LWG2381_num_get_floating_point +tests\LWG2510_tag_classes tests\LWG2597_complex_branch_cut tests\LWG3018_shared_ptr_function tests\LWG3121_constrained_tuple_forwarding_ctor diff --git a/tests/std/tests/LWG2510_tag_classes/env.lst b/tests/std/tests/LWG2510_tag_classes/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/LWG2510_tag_classes/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/LWG2510_tag_classes/test.compile.pass.cpp b/tests/std/tests/LWG2510_tag_classes/test.compile.pass.cpp new file mode 100644 index 00000000000..5e7efbdf073 --- /dev/null +++ b/tests/std/tests/LWG2510_tag_classes/test.compile.pass.cpp @@ -0,0 +1,207 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _M_CEE_PURE +#include +#include +#endif // _M_CEE_PURE + +#if _HAS_CXX17 +#include +#endif // _HAS_CXX17 + +#if _HAS_CXX20 +#include +#include +#include +#include +#include +#endif // _HAS_CXX20 + +#if _HAS_CXX23 +#include +#include +#include +#include +#endif // _HAS_CXX23 + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +using namespace std; + +// LWG-2510 "Tag types should not be DefaultConstructible" + +template +void check_implicit_default_construction(const T&); // not defined + +template +constexpr bool implicitly_default_constructible = false; +template +constexpr bool implicitly_default_constructible({}))>> = true; + +STATIC_ASSERT(implicitly_default_constructible); +STATIC_ASSERT(implicitly_default_constructible); + +STATIC_ASSERT(!implicitly_default_constructible); +STATIC_ASSERT(!implicitly_default_constructible); +STATIC_ASSERT(!implicitly_default_constructible); + +template +void check_tag_class() { // COMPILE-ONLY + STATIC_ASSERT(is_default_constructible_v); + STATIC_ASSERT(!implicitly_default_constructible); + + STATIC_ASSERT(is_copy_constructible_v); + STATIC_ASSERT(is_move_constructible_v); + STATIC_ASSERT(is_constructible_v); + STATIC_ASSERT(is_constructible_v); + STATIC_ASSERT(is_convertible_v); + STATIC_ASSERT(is_convertible_v); + STATIC_ASSERT(is_convertible_v); + STATIC_ASSERT(is_convertible_v); + STATIC_ASSERT(is_copy_assignable_v); + STATIC_ASSERT(is_move_assignable_v); + STATIC_ASSERT(is_assignable_v); + STATIC_ASSERT(is_assignable_v); + + STATIC_ASSERT(is_nothrow_destructible_v); + + STATIC_ASSERT(!is_polymorphic_v); + + constexpr T constant_argument; + (void) constant_argument; + + // desired properties not yet guaranteed by the Standard + STATIC_ASSERT(is_nothrow_default_constructible_v); + STATIC_ASSERT(is_nothrow_copy_constructible_v); + STATIC_ASSERT(is_nothrow_move_constructible_v); + STATIC_ASSERT(is_nothrow_constructible_v); + STATIC_ASSERT(is_nothrow_constructible_v); +#if _HAS_CXX20 + STATIC_ASSERT(is_nothrow_convertible_v); + STATIC_ASSERT(is_nothrow_convertible_v); + STATIC_ASSERT(is_nothrow_convertible_v); + STATIC_ASSERT(is_nothrow_convertible_v); +#endif // _HAS_CXX20 + STATIC_ASSERT(is_nothrow_copy_assignable_v); + STATIC_ASSERT(is_nothrow_move_assignable_v); + STATIC_ASSERT(is_nothrow_assignable_v); + STATIC_ASSERT(is_nothrow_assignable_v); + + STATIC_ASSERT(is_trivially_default_constructible_v); + STATIC_ASSERT(is_trivially_copy_constructible_v); + STATIC_ASSERT(is_trivially_move_constructible_v); + STATIC_ASSERT(is_trivially_constructible_v); + STATIC_ASSERT(is_trivially_constructible_v); + STATIC_ASSERT(is_trivially_copy_assignable_v); + STATIC_ASSERT(is_trivially_move_assignable_v); + STATIC_ASSERT(is_trivially_assignable_v); + STATIC_ASSERT(is_trivially_assignable_v); + STATIC_ASSERT(is_trivially_destructible_v); + STATIC_ASSERT(is_trivially_copyable_v); + + STATIC_ASSERT(is_standard_layout_v); + STATIC_ASSERT(is_empty_v); // only guaranteed for allocator_arg_t and piecewise_construct_t + + // ABI-specific, seemingly agreed among mainstream implementations + STATIC_ASSERT(sizeof(T) == 1); +} + +void check_standard_tags() { // COMPILE-ONLY + check_tag_class(); + check_tag_class(); + check_tag_class(); +#ifndef _M_CEE_PURE + check_tag_class(); + check_tag_class(); + check_tag_class(); +#endif // _M_CEE_PURE + +#if _HAS_CXX17 + check_tag_class(); + check_tag_class>(); + check_tag_class>(); + check_tag_class(-1)>>(); + check_tag_class>(); + check_tag_class>>(); + check_tag_class>(); + check_tag_class>(); + + // LWG-4273 "Standard execution policy types should be conventional tag class types" + check_tag_class(); + check_tag_class(); + check_tag_class(); +#endif // _HAS_CXX17 + +#if _HAS_CXX20 + check_tag_class(); + check_tag_class(); + check_tag_class(); + + // LWG-4273 "Standard execution policy types should be conventional tag class types" + check_tag_class(); +#endif // _HAS_CXX20 + +#if _HAS_CXX23 + check_tag_class(); + check_tag_class(); + // TODO: Cover sorted_equivalent_t and sorted_unique_t once flat_meow are implemented. +#endif // _HAS_CXX23 + + // TODO: Update to cover newly introduced tag class types. +} + +// We intentionally implement internal disambiguation tag types like standard ones. +void check_implementation_details() { // COMPILE-ONLY + // TODO: Synchronize the check list with actual implementation details. + check_tag_class<_Alloc_exact_args_t>(); + check_tag_class<_Alloc_unpack_tuple_t>(); + check_tag_class<_Exact_args_t>(); + check_tag_class<_Leave_proxy_unbound>(); + check_tag_class<_Move_allocator_tag>(); + check_tag_class<_One_then_variadic_args_t>(); + check_tag_class<_String_constructor_concat_tag>(); + check_tag_class<_Secret_copyability_ignoring_tag>(); + check_tag_class<_Secret_scoped_allocator_construct_tag>(); + check_tag_class<_Unforced>(); + check_tag_class<_Unpack_tuple_t>(); + check_tag_class<_Value_init_tag>(); + check_tag_class<_Zero_then_variadic_args_t>(); +#ifndef _M_CEE_PURE + check_tag_class<_From_raw_state_tag>(); +#endif // _M_CEE_PURE + +#if _HAS_CXX17 + check_tag_class<_No_init_tag>(); + check_tag_class<_Not_fn_tag>(); +#endif // _HAS_CXX17 + +#if _HAS_CXX20 + check_tag_class<_Auto_id_tag>(); + check_tag_class<_For_overwrite_tag>(); + check_tag_class<_Variantish_empty_tag>(); + check_tag_class(); + check_tag_class(); + check_tag_class(); + check_tag_class(); +#endif // _HAS_CXX20 + +#if _HAS_CXX23 + check_tag_class<_Construct_expected_from_invoke_result_tag>(); + check_tag_class<_Construct_from_invoke_result_tag>(); + check_tag_class<_Extents_from_tuple>(); + check_tag_class<_Gen_detail::_Secret_tag>(); +#endif // _HAS_CXX23 +} diff --git a/tests/std/tests/VSO_0000000_more_pair_tuple_sfinae/test.cpp b/tests/std/tests/VSO_0000000_more_pair_tuple_sfinae/test.cpp index fb07af86b09..21a3bf66076 100644 --- a/tests/std/tests/VSO_0000000_more_pair_tuple_sfinae/test.cpp +++ b/tests/std/tests/VSO_0000000_more_pair_tuple_sfinae/test.cpp @@ -3,10 +3,6 @@ #include #include -#ifndef _M_CEE_PURE -#include -#endif // _M_CEE_PURE -#include #include #include #include @@ -42,39 +38,31 @@ STATIC_ASSERT(!is_constructible_v, allocator_arg_t, // LWG-2510 "Tag types should not be DefaultConstructible" -template -struct IsImplicitlyDefaultConstructible : false_type {}; +template +constexpr bool implicitly_default_constructible = false; -template -void ImplicitlyDefaultConstruct(const T&); +template +void check_implicit_default_construction(const T&); // not defined -template -struct IsImplicitlyDefaultConstructible({}))>> : true_type {}; +template +constexpr bool implicitly_default_constructible({}))>> = true; struct ExplicitDefault { explicit ExplicitDefault() = default; }; template -constexpr bool OrdinaryDC = is_default_constructible_v && IsImplicitlyDefaultConstructible::value; +constexpr bool OrdinaryDC = is_default_constructible_v && implicitly_default_constructible; template -constexpr bool ExplicitDC = is_default_constructible_v && !IsImplicitlyDefaultConstructible::value; +constexpr bool ExplicitDC = is_default_constructible_v && !implicitly_default_constructible; template -constexpr bool VerbotenDC = !is_default_constructible_v && !IsImplicitlyDefaultConstructible::value; +constexpr bool VerbotenDC = !is_default_constructible_v && !implicitly_default_constructible; STATIC_ASSERT(OrdinaryDC); STATIC_ASSERT(VerbotenDC); STATIC_ASSERT(ExplicitDC); -STATIC_ASSERT(ExplicitDC); -STATIC_ASSERT(ExplicitDC); -STATIC_ASSERT(ExplicitDC); -#ifndef _M_CEE_PURE -STATIC_ASSERT(ExplicitDC); -STATIC_ASSERT(ExplicitDC); -STATIC_ASSERT(ExplicitDC); -#endif // _M_CEE_PURE using Expl = ExplicitDefault; using NOPE = NoDefault; From c1ce930135901bed31374f8517603e2b49a7084f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20M=C3=BCller?= Date: Fri, 8 Aug 2025 19:02:46 +0200 Subject: [PATCH 06/24] ``: Use `std::search()` in skip heuristic (#5586) --- stl/inc/regex | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/stl/inc/regex b/stl/inc/regex index 1e912b60033..70a2fae1ab4 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -266,7 +266,7 @@ struct _Cmp_collate { // functor to compare for equality following collating tra template struct _Cmp_icase_translateleft { - // functor to compare for equality following collating translation of the left character + // functor to compare for equality following case-insensitive translation of the left character using _Elem = typename _RxTraits::char_type; explicit _Cmp_icase_translateleft(const _RxTraits& _Tr) noexcept : _Traits(_Tr) {} @@ -3675,6 +3675,20 @@ _BidIt1 _Compare_translate_left(_BidIt1 _Begin1, _BidIt1 _End1, _BidIt2 _Begin2, } } +template +_BidIt1 _Search_translate_left(_BidIt1 _Begin1, _BidIt1 _End1, _BidIt2 _Begin2, _BidIt2 _End2, const _RxTraits& _Traits, + regex_constants::syntax_option_type _Sflags) { + // searching the right character sequence in the left sequence, + // after translating characters in the left sequence according to syntax options + if (_Sflags & regex_constants::icase) { + return _STD search(_Begin1, _End1, _Begin2, _End2, _Cmp_icase_translateleft<_RxTraits>{_Traits}); + } else if (_Sflags & regex_constants::collate) { + return _STD search(_Begin1, _End1, _Begin2, _End2, _Cmp_collate_translateleft<_RxTraits>{_Traits}); + } else { + return _STD search(_Begin1, _End1, _Begin2, _End2, equal_to{}); + } +} + template bool _Lookup_range(unsigned int _Ch, const _Buf<_Elem>* _Bufptr) { // check whether _Ch is in _Buf using _Uelem = make_unsigned_t<_Elem>; @@ -4165,15 +4179,9 @@ _BidIt _Matcher2<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Skip( case _N_str: { // check for string match _Node_str<_Elem>* _Node = static_cast<_Node_str<_Elem>*>(_Nx); - for (; _First_arg != _Last; ++_First_arg) { // look for starting match - _BidIt _Next = _First_arg; - if (_STD _Compare_translate_left( - _First_arg, ++_Next, _Node->_Data._Str(), _Node->_Data._Str() + 1, _Traits, _Sflags) - != _First_arg) { - break; - } - } - return _First_arg; + auto _Str = _Node->_Data._Str(); + return _STD _Search_translate_left( + _First_arg, _Last, _Str, _Str + _Node->_Data._Size(), _Traits, _Sflags); } case _N_class: From 452543642b4556847baf1cd8d64479abf21b999b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20M=C3=BCller?= Date: Fri, 8 Aug 2025 19:05:20 +0200 Subject: [PATCH 07/24] ``: Remove usage of non-standard `_Uelem` from parser (#5592) Co-authored-by: Stephan T. Lavavej --- stl/inc/regex | 237 +++++++++------ tests/std/test.lst | 1 + .../GH_000995_regex_custom_char_types/env.lst | 4 + .../test.cpp | 269 ++++++++++++++++++ 4 files changed, 430 insertions(+), 81 deletions(-) create mode 100644 tests/std/tests/GH_000995_regex_custom_char_types/env.lst create mode 100644 tests/std/tests/GH_000995_regex_custom_char_types/test.cpp diff --git a/stl/inc/regex b/stl/inc/regex index 70a2fae1ab4..3753efd523d 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -1640,7 +1640,7 @@ private: static void _Insert_node(_Node_base*, _Node_base*); _Node_base* _New_node(_Node_type _Kind); void _Add_str_node(); - void _Add_char_to_bitmap(_Elem _Ch); + void _Add_char_to_bitmap(unsigned char _Ch); void _Add_char_to_array(_Elem _Ch); void _Add_elts(_Node_class<_Elem, _RxTraits>*, typename _RxTraits::char_class_type, bool); void _Char_to_elts(const _Elem*, const _Elem*, _Sequence<_Elem>**); @@ -1888,6 +1888,7 @@ private: _Meta_type _Mchar; _Lex_mode _Mode = _Lex_mode::_Default; _Elem _Char; + _Elem _Unescaped_char; }; enum _Lang_flags2 : unsigned long long { // describe language properties @@ -2973,14 +2974,14 @@ void _Builder2<_FwdIt, _Elem, _RxTraits>::_Add_class() { // add bracket expressi } template -void _Builder2<_FwdIt, _Elem, _RxTraits>::_Add_char_to_bitmap(_Elem _Ch) { // add character to accelerator table +void _Builder2<_FwdIt, _Elem, _RxTraits>::_Add_char_to_bitmap(unsigned char _Ch) { // add character to accelerator table _Node_class<_Elem, _RxTraits>* _Node = static_cast<_Node_class<_Elem, _RxTraits>*>(_Current); if (!_Node->_Small) { _Node->_Small = new _Bitmap; } - _Node->_Small->_Mark(static_cast(_Ch)); + _Node->_Small->_Mark(_Ch); } template @@ -3001,37 +3002,37 @@ void _Builder2<_FwdIt, _Elem, _RxTraits>::_Add_char_to_class(_Elem _Ch) { // add _Ch = _Traits.translate(_Ch); } - if (static_cast(_Ch) < _Bmp_max) { - _Add_char_to_bitmap(_Ch); + auto _Uchar = static_cast(_Ch); + if (static_cast<_Elem>(_Uchar) == _Ch) { + _Add_char_to_bitmap(_Uchar); } else { _Add_char_to_array(_Ch); } } template -void _Builder2<_FwdIt, _Elem, _RxTraits>::_Add_range(const _Elem _Arg0, const _Elem _Arg1) { +void _Builder2<_FwdIt, _Elem, _RxTraits>::_Add_range(_Elem _Arg0, const _Elem _Arg1) { // add character range to set - using string_type = typename _RxTraits::string_type; - unsigned int _Ex0 = static_cast(_Arg0); - const unsigned int _Ex1 = static_cast(_Arg1); + using _String_type = typename _RxTraits::string_type; + using _Char_traits_type = typename _String_type::traits_type; _Node_class<_Elem, _RxTraits>* _Node = static_cast<_Node_class<_Elem, _RxTraits>*>(_Current); // set bits and check that the range is non-empty if (_Flags & regex_constants::collate) { _Elem _Ch; - const auto _Ch_ptr = _STD addressof(_Ch); - const auto _Arg0_ptr = _STD addressof(_Arg0); - const auto _Arg1_ptr = _STD addressof(_Arg1); - const string_type _Arg0_key = _Traits.transform(_Arg0_ptr, _Arg0_ptr + 1); - const string_type _Arg1_key = _Traits.transform(_Arg1_ptr, _Arg1_ptr + 1); + const auto _Ch_ptr = _STD addressof(_Ch); + const auto _Arg0_ptr = _STD addressof(_Arg0); + const auto _Arg1_ptr = _STD addressof(_Arg1); + const _String_type _Arg0_key = _Traits.transform(_Arg0_ptr, _Arg0_ptr + 1); + const _String_type _Arg1_key = _Traits.transform(_Arg1_ptr, _Arg1_ptr + 1); if (_Arg0_key > _Arg1_key) { _Xregex_error(regex_constants::error_range); } for (unsigned int _UCh = 0; _UCh < _Bmp_max; ++_UCh) { - _Ch = static_cast<_Elem>(_UCh); - const string_type _Ch_key = _Traits.transform(_Ch_ptr, _Ch_ptr + 1); + _Ch = static_cast<_Elem>(_UCh); + const _String_type _Ch_key = _Traits.transform(_Ch_ptr, _Ch_ptr + 1); if (_Arg0_key <= _Ch_key && _Ch_key <= _Arg1_key) { if (!_Node->_Small) { _Node->_Small = new _Bitmap; @@ -3039,32 +3040,68 @@ void _Builder2<_FwdIt, _Elem, _RxTraits>::_Add_range(const _Elem _Arg0, const _E _Node->_Small->_Mark(_UCh); } } - } else if (_Ex0 > _Ex1) { + } else if (_Char_traits_type::lt(_Arg1, _Arg0)) { _Xregex_error(regex_constants::error_range); } else { - if (!_Node->_Small && _Ex0 < _Bmp_max) { - _Node->_Small = new _Bitmap; - } + unsigned int _Ex0 = static_cast(_Arg0); + if (static_cast<_Elem>(_Ex0) == _Arg0) { + if (!_Node->_Small) { + _Node->_Small = new _Bitmap; + } - for (; _Ex0 <= _Ex1 && _Ex0 < _Bmp_max; ++_Ex0) { - _Node->_Small->_Mark(_Ex0); - } + unsigned int _Ex1 = static_cast(_Arg1); + bool _Extends_beyond_bitmap = static_cast<_Elem>(_Ex1) != _Arg1; + if (_Extends_beyond_bitmap) { + _Ex1 = _Bmp_max - 1; + } - if (_Ex1 - _Ex0 < _ARRAY_THRESHOLD) { for (; _Ex0 <= _Ex1; ++_Ex0) { - _Add_char_to_array(static_cast<_Elem>(_Ex0)); + _Node->_Small->_Mark(_Ex0); + } + + if (!_Extends_beyond_bitmap) { + return; } + + _Arg0 = static_cast<_Elem>(_Ex0); } - } - if ((_Flags & regex_constants::collate) || _Ex1 >= _Ex0) { // store remaining range as pair - if (!_Node->_Ranges) { - _Node->_Ranges = new _Buf<_Elem>; + if constexpr (sizeof(_Elem) > 1U) { + if constexpr (is_integral_v<_Elem> || is_enum_v<_Elem>) { + using _Uelem = make_unsigned_t<_Elem>; + auto _First = static_cast<_Uelem>(_Arg0); + auto _Last = static_cast<_Uelem>(_Arg1); + auto _Count = static_cast<_Uelem>(_Last - _First); + ++_Count; + if (_Count <= _ARRAY_THRESHOLD) { + for (; _Count > 0; ++_First, --_Count) { + _Add_char_to_array(static_cast<_Elem>(_First)); + } + return; + } + } else { + auto _Last = static_cast(_Arg1); + if (static_cast<_Elem>(_Last) == _Arg1) { + auto _First = static_cast(_Arg0); + auto _Count = _Last - _First + 1U; + if (_Count <= _ARRAY_THRESHOLD) { + for (; _Count > 0; ++_First, --_Count) { + _Add_char_to_array(static_cast<_Elem>(_First)); + } + return; + } + } + } } + } - _Node->_Ranges->_Insert2(static_cast<_Elem>(_Ex0)); - _Node->_Ranges->_Insert2(_Arg1); + // store remaining range as pair + if (!_Node->_Ranges) { + _Node->_Ranges = new _Buf<_Elem>; } + + _Node->_Ranges->_Insert2(_Arg0); + _Node->_Ranges->_Insert2(_Arg1); } template @@ -3090,7 +3127,7 @@ void _Builder2<_FwdIt, _Elem, _RxTraits>::_Add_named_class( using _Char_class_type = typename _RxTraits::char_class_type; _Node_class<_Elem, _RxTraits>* _Node = static_cast<_Node_class<_Elem, _RxTraits>*>(_Current); _Add_elts(_Node, _Cl, _Kind != _Rx_char_class_kind::_Positive); - if (_Bmp_max <= _STD _Max_limit()) { + if constexpr (sizeof(_Elem) > 1U) { if (_Kind == _Rx_char_class_kind::_Positive) { auto _Cl_all_bits_set = static_cast<_Char_class_type>(-1); if ((_Node->_Classes != _Cl_all_bits_set && _Cl != _Cl_all_bits_set) @@ -3146,7 +3183,8 @@ void _Builder2<_FwdIt, _Elem, _RxTraits>::_Add_equiv(const _Elem* const _First, _Node->_Small->_Mark(_Ch); } } - if (_Bmp_max < static_cast(_STD _Max_limit<_Elem>())) { // map range + + if constexpr (sizeof(_Elem) > 1U) { // map range _Sequence<_Elem>** _Cur = _STD addressof(_Node->_Equiv); _Char_to_elts(_First, _Last, _Cur); } @@ -4304,8 +4342,12 @@ template template bool _Parser2<_FwdIt, _Elem, _RxTraits>::_Is_esc(_FwdIt _Ch0) const { // assumes _Ch0 != _End return _Mode == _Lex_mode::_Default && ++_Ch0 != _End - && ((!(_L_flags & _L_nex_grp) && (*_Ch0 == _Meta_lpar || *_Ch0 == _Meta_rpar)) - || (!(_L_flags & _L_nex_rep) && (*_Ch0 == _Meta_lbr || *_Ch0 == _Meta_rbr))); + && ((!(_L_flags & _L_nex_grp) + && (*_Ch0 == static_cast<_Elem>(static_cast(_Meta_lpar)) + || *_Ch0 == static_cast<_Elem>(static_cast(_Meta_rpar)))) + || (!(_L_flags & _L_nex_rep) + && (*_Ch0 == static_cast<_Elem>(static_cast(_Meta_lbr)) + || *_Ch0 == static_cast<_Elem>(static_cast(_Meta_rbr))))); } template @@ -4316,16 +4358,23 @@ void _Parser2<_FwdIt, _Elem, _RxTraits>::_Trans() { // map character to meta-cha if (_Pat == _End) { _Mchar = _Meta_eos; - _Char = static_cast<_Elem>(_Meta_eos); + _Char = _Elem{}; } else { // map current character - _Char = *_Pat; - _Mchar = _CSTD strchr(_Meta_map, _Char) ? static_cast<_Meta_type>(_Char) : _Meta_chr; + _Char = *_Pat; + auto _Uchar = static_cast(_Char); + if (static_cast<_Elem>(_Uchar) == _Char && _CSTD strchr(_Meta_map, static_cast(_Uchar))) { + _Mchar = static_cast<_Meta_type>(_Uchar); + } else { + _Mchar = _Meta_chr; + } } - switch (_Char) { // handle special cases + + switch (int{_Mchar}) { // handle special cases case _Meta_esc: if (_Is_esc(_Pat)) { // replace escape sequence _FwdIt _Ch0 = _Pat; - _Mchar = static_cast<_Meta_type>(_Char = *++_Ch0); + _Char = *++_Ch0; + _Mchar = static_cast<_Meta_type>(static_cast(_Char)); } break; @@ -4369,7 +4418,7 @@ void _Parser2<_FwdIt, _Elem, _RxTraits>::_Trans() { // map character to meta-cha { // check if $ is special _FwdIt _Next = _Pat; if ((_L_flags & _L_anch_rstr) && ++_Next != _End) { - const bool _Escaped = *_Next == _Meta_esc && _Is_esc(_Next); + const bool _Escaped = *_Next == static_cast<_Elem>(static_cast(_Meta_esc)) && _Is_esc(_Next); if (_Escaped) { ++_Next; } @@ -4381,9 +4430,9 @@ void _Parser2<_FwdIt, _Elem, _RxTraits>::_Trans() { // map character to meta-cha const _Elem _Ch = *_Next; const bool _Is_end_of_alternative = - ((_L_flags & _L_alt_nl) && _Ch == _Meta_nl + ((_L_flags & _L_alt_nl) && _Ch == static_cast<_Elem>(static_cast(_Meta_nl)) && _Disj_count == 0) // dollar followed by newline '\n' for alternation - || (_Escaped && _Ch == _Meta_rpar + || (_Escaped && _Ch == static_cast<_Elem>(static_cast(_Meta_rpar)) && _Disj_count != 0); // dollar followed by (escaped) right parenthesis ')' closing a group if (!_Is_end_of_alternative) { @@ -4417,7 +4466,7 @@ void _Parser2<_FwdIt, _Elem, _RxTraits>::_Trans() { // map character to meta-cha template void _Parser2<_FwdIt, _Elem, _RxTraits>::_Next() { // advance to next input character if (_Pat != _End) { // advance - if (*_Pat == _Meta_esc && _Is_esc(_Pat)) { + if (*_Pat == static_cast<_Elem>(static_cast(_Meta_esc)) && _Is_esc(_Pat)) { ++_Pat; } @@ -4513,7 +4562,7 @@ _Prs_ret _Parser2<_FwdIt, _Elem, _RxTraits>::_Do_ex_class( if (_Size == 1 && _End_arg == _Meta_dot) { // process single-element collating elements like individual characters - _Val = *_Coll_elem_first; + _Unescaped_char = *_Coll_elem_first; return _Prs_chr; } @@ -4548,10 +4597,10 @@ bool _Parser2<_FwdIt, _Elem, _RxTraits>::_CharacterClassEscape(bool _Addit) { // return false; } - auto _Kind = (_Char == 'W' ? _Rx_char_class_kind::_Negated_w - : _Char == 'S' ? _Rx_char_class_kind::_Negated_s - : _Char == 'D' ? _Rx_char_class_kind::_Negated_d - : _Rx_char_class_kind::_Positive); + auto _Kind = (_Char == static_cast<_Elem>('W') ? _Rx_char_class_kind::_Negated_w + : _Char == static_cast<_Elem>('S') ? _Rx_char_class_kind::_Negated_s + : _Char == static_cast<_Elem>('D') ? _Rx_char_class_kind::_Negated_d + : _Rx_char_class_kind::_Positive); if (_Addit) { _Nfa._Add_class(); @@ -4571,9 +4620,10 @@ bool _Parser2<_FwdIt, _Elem, _RxTraits>::_CharacterClassEscape(bool _Addit) { // template _Prs_ret _Parser2<_FwdIt, _Elem, _RxTraits>::_ClassEscape() { // check for class escape - if ((_L_flags & _L_esc_bsp) && _Char == _Esc_ctrl_b) { // handle backspace escape + if ((_L_flags & _L_esc_bsp) + && _Char == static_cast<_Elem>(static_cast(_Esc_ctrl_b))) { // handle backspace escape _Next(); - _Val = _Meta_bsp; + _Unescaped_char = static_cast<_Elem>(static_cast(_Meta_bsp)); return _Prs_chr; } else if ((_L_flags & (_L_bzr_chr | _L_bckr)) && (_Val = _Traits.value(_Char, 10)) != -1) { // handle \0 and reject other escaped decimal literals @@ -4582,6 +4632,7 @@ _Prs_ret _Parser2<_FwdIt, _Elem, _RxTraits>::_ClassEscape() { // check for class _Error(regex_constants::error_escape); } + _Unescaped_char = _Elem{}; return _Prs_chr; } else if (_CharacterEscape(true)) { return _Prs_chr; @@ -4598,13 +4649,14 @@ _Prs_ret _Parser2<_FwdIt, _Elem, _RxTraits>::_ClassAtom(const bool _Initial) { / _Next(); return _ClassEscape(); } else if (_Mchar == _Meta_lsq) { // check for valid delimited expression + _Elem _Lsq = _Char; _Next(); if (_Mchar == _Meta_colon || _Mchar == _Meta_equal || _Mchar == _Meta_dot) { // handle delimited expression _Meta_type _St = _Mchar; _Next(); return _Do_ex_class(_St); } else { // handle ordinary [ - _Val = _Meta_lsq; + _Unescaped_char = _Lsq; return _Prs_chr; } } else if ((_Mchar == _Meta_rsq @@ -4613,7 +4665,7 @@ _Prs_ret _Parser2<_FwdIt, _Elem, _RxTraits>::_ClassAtom(const bool _Initial) { / || _Mchar == _Meta_eos) { return _Prs_none; } else { // handle ordinary character - _Val = _Char; + _Unescaped_char = _Char; _Next(); return _Prs_chr; } @@ -4630,19 +4682,20 @@ void _Parser2<_FwdIt, _Elem, _RxTraits>::_ClassRanges() { // check for valid cla } _Initial = false; - if (_Ret == _Prs_chr && _Val == 0 && !(_L_flags & _L_bzr_chr)) { + if (_Ret == _Prs_chr && !(_L_flags & _L_bzr_chr) && _Unescaped_char == _Elem{}) { _Error(regex_constants::error_escape); } if (_Mchar == _Meta_dash) { // check for valid range + _Elem _Chr1 = _Unescaped_char; + _Elem _Dash = _Char; _Next(); - _Elem _Chr1 = static_cast<_Elem>(_Val); const bool _Set_preceding = _Ret == _Prs_set; if ((_Ret = _ClassAtom(false)) == _Prs_none) { // treat - as ordinary character if (!_Set_preceding) { _Nfa._Add_char_to_class(_Chr1); } - _Nfa._Add_char_to_class(_Meta_dash); + _Nfa._Add_char_to_class(_Dash); return; } @@ -4650,7 +4703,7 @@ void _Parser2<_FwdIt, _Elem, _RxTraits>::_ClassRanges() { // check for valid cla _Error(regex_constants::error_range); // set precedes or follows dash } - _Elem _Chr2 = static_cast<_Elem>(_Val); + _Elem _Chr2 = _Unescaped_char; // translate ends of range if (_Flags & regex_constants::icase) { @@ -4663,7 +4716,7 @@ void _Parser2<_FwdIt, _Elem, _RxTraits>::_ClassRanges() { // check for valid cla _Nfa._Add_range(_Chr1, _Chr2); } else if (_Ret == _Prs_chr) { - _Nfa._Add_char_to_class(static_cast<_Elem>(_Val)); + _Nfa._Add_char_to_class(_Unescaped_char); } } } @@ -4747,10 +4800,15 @@ bool _Parser2<_FwdIt, _Elem, _RxTraits>::_Wrapped_disjunction() { // add disjunc template bool _Parser2<_FwdIt, _Elem, _RxTraits>::_IsIdentityEscape(bool _In_character_class) const { // check for valid identity escape + auto _Uchar = static_cast(_Char); if (_L_flags & _L_ident_ECMA) { // ECMAScript identity escape characters - switch (_Char) { + if (static_cast<_Elem>(_Uchar) != _Char) { + return true; + } + + switch (_Uchar) { case 'c': // ASCII control character case 'd': // digit case 'D': @@ -4764,7 +4822,11 @@ bool _Parser2<_FwdIt, _Elem, _RxTraits>::_IsIdentityEscape(bool _In_character_cl } } - switch (_Char) { + if (static_cast<_Elem>(_Uchar) != _Char) { + return false; + } + + switch (_Uchar) { case _Meta_esc: // BRE, ERE, awk identity escape characters (anywhere in awk) return true; @@ -4798,7 +4860,7 @@ template bool _Parser2<_FwdIt, _Elem, _RxTraits>::_IdentityEscape(bool _In_character_class) { // check whether an escape is valid, and process it if so if (_IsIdentityEscape(_In_character_class)) { - _Val = _Char; + _Unescaped_char = _Char; _Next(); return true; } else { @@ -4808,15 +4870,15 @@ bool _Parser2<_FwdIt, _Elem, _RxTraits>::_IdentityEscape(bool _In_character_clas template bool _Parser2<_FwdIt, _Elem, _RxTraits>::_Do_ffn(_Elem _Ch) { // check for limited file format escape characters - if (_Ch == _Esc_ctrl_f) { + if (_Ch == static_cast<_Elem>(static_cast(_Esc_ctrl_f))) { _Val = '\f'; - } else if (_Ch == _Esc_ctrl_n) { + } else if (_Ch == static_cast<_Elem>(static_cast(_Esc_ctrl_n))) { _Val = '\n'; - } else if (_Ch == _Esc_ctrl_r) { + } else if (_Ch == static_cast<_Elem>(static_cast(_Esc_ctrl_r))) { _Val = '\r'; - } else if (_Ch == _Esc_ctrl_t) { + } else if (_Ch == static_cast<_Elem>(static_cast(_Esc_ctrl_t))) { _Val = '\t'; - } else if (_Ch == _Esc_ctrl_v) { + } else if (_Ch == static_cast<_Elem>(static_cast(_Esc_ctrl_v))) { _Val = '\v'; } else { return false; @@ -4827,9 +4889,9 @@ bool _Parser2<_FwdIt, _Elem, _RxTraits>::_Do_ffn(_Elem _Ch) { // check for limit template bool _Parser2<_FwdIt, _Elem, _RxTraits>::_Do_ffnx(_Elem _Ch) { // check for the remaining file format escape characters - if (_Ch == _Esc_ctrl_a) { + if (_Ch == static_cast<_Elem>(static_cast(_Esc_ctrl_a))) { _Val = '\a'; - } else if (_Ch == _Esc_ctrl_b) { + } else if (_Ch == static_cast<_Elem>(static_cast(_Esc_ctrl_b))) { _Val = '\b'; } else { return false; @@ -4847,22 +4909,26 @@ bool _Parser2<_FwdIt, _Elem, _RxTraits>::_CharacterEscape(bool _In_character_cla if ((_L_flags & _L_esc_ffn && _Do_ffn(_Char)) || (_L_flags & _L_esc_ffnx && _Do_ffnx(_Char))) { _Next(); - } else if (_Char == _Esc_ctrl && (_L_flags & _L_esc_ctrl)) { // handle control escape sequence + } else if (_Char == static_cast<_Elem>(static_cast(_Esc_ctrl)) + && (_L_flags & _L_esc_ctrl)) { // handle control escape sequence _Next(); - using _Uelem = typename _RxTraits::_Uelem; - _Uelem _UCh = static_cast<_Uelem>(_Char); - if (!((static_cast<_Uelem>('a') <= _UCh && _UCh <= static_cast<_Uelem>('z')) - || (static_cast<_Uelem>('A') <= _UCh && _UCh <= static_cast<_Uelem>('Z')))) { + using _Char_traits_type = typename _RxTraits::string_type::traits_type; + if ((_Char_traits_type::lt(_Char, static_cast<_Elem>('a')) + || _Char_traits_type::lt(static_cast<_Elem>('z'), _Char)) + && (_Char_traits_type::lt(_Char, static_cast<_Elem>('A')) + || _Char_traits_type::lt(static_cast<_Elem>('Z'), _Char))) { _Error(regex_constants::error_escape); } - _Val = static_cast(_Char % 32); + _Val = static_cast(_Char) % 32; _Next(); - } else if (_Char == _Esc_hex && (_L_flags & _L_esc_hex)) { // handle hexadecimal escape sequence + } else if (_Char == static_cast<_Elem>(static_cast(_Esc_hex)) + && (_L_flags & _L_esc_hex)) { // handle hexadecimal escape sequence _Next(); _HexDigits(2); - } else if (_Char == _Esc_uni && (_L_flags & _L_esc_uni)) { // handle Unicode escape sequence + } else if (_Char == static_cast<_Elem>(static_cast(_Esc_uni)) + && (_L_flags & _L_esc_uni)) { // handle Unicode escape sequence _Next(); _HexDigits(4); } else if ((_L_flags & _L_esc_oct) && _OctalDigits()) { // handle octal escape sequence @@ -4873,11 +4939,18 @@ bool _Parser2<_FwdIt, _Elem, _RxTraits>::_CharacterEscape(bool _In_character_cla return _IdentityEscape(_In_character_class); } - if (_STD _Max_limit() < static_cast(_Val)) { + _Unescaped_char = static_cast<_Elem>(static_cast(_Val)); + int _Cast_val; + if constexpr (is_integral_v<_Elem> || is_enum_v<_Elem>) { + _Cast_val = static_cast(static_cast>(_Unescaped_char)); + } else { + _Cast_val = static_cast(static_cast(_Unescaped_char)); + } + + if (_Cast_val != _Val) { _Error(regex_constants::error_escape); } - _Val = static_cast<_Elem>(_Val); return true; } @@ -4905,7 +4978,7 @@ void _Parser2<_FwdIt, _Elem, _RxTraits>::_AtomEscape() { // check for valid atom } } } else if (_CharacterEscape(false)) { - _Nfa._Add_char(static_cast<_Elem>(_Val)); + _Nfa._Add_char(_Unescaped_char); } else if (!(_L_flags & _L_esc_wsd) || !_CharacterClassEscape(true)) { _Error(regex_constants::error_escape); } @@ -4971,11 +5044,13 @@ bool _Parser2<_FwdIt, _Elem, _RxTraits>::_Alternative() { // check for valid alt _Next(); } else if (_Mchar == _Meta_esc) { // check for valid escape sequence _Next(); - if ((_L_flags & _L_asrt_wrd) && _Char == _Esc_word) { // add word assert + if ((_L_flags & _L_asrt_wrd) + && _Char == static_cast<_Elem>(static_cast(_Esc_word))) { // add word assert _Nfa._Add_wbound(); _Next(); _Quant = false; - } else if ((_L_flags & _L_asrt_wrd) && _Char == _Esc_not_word) { // add not-word assert + } else if ((_L_flags & _L_asrt_wrd) + && _Char == static_cast<_Elem>(static_cast(_Esc_not_word))) { // add not-word assert _Nfa._Add_wbound(); _Nfa._Negate(); _Next(); diff --git a/tests/std/test.lst b/tests/std/test.lst index 2ef39ff142a..4a0b4c0864b 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -182,6 +182,7 @@ tests\GH_000935_complex_numerical_accuracy tests\GH_000940_missing_valarray_copy tests\GH_000952_bind_constraints tests\GH_000990_any_link_without_exceptions +tests\GH_000995_regex_custom_char_types tests\GH_001001_random_rejection_rounding tests\GH_001010_filesystem_error_encoding tests\GH_001017_discrete_distribution_out_of_range diff --git a/tests/std/tests/GH_000995_regex_custom_char_types/env.lst b/tests/std/tests/GH_000995_regex_custom_char_types/env.lst new file mode 100644 index 00000000000..f141421b292 --- /dev/null +++ b/tests/std/tests/GH_000995_regex_custom_char_types/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\impure_matrix.lst diff --git a/tests/std/tests/GH_000995_regex_custom_char_types/test.cpp b/tests/std/tests/GH_000995_regex_custom_char_types/test.cpp new file mode 100644 index 00000000000..c9409259f64 --- /dev/null +++ b/tests/std/tests/GH_000995_regex_custom_char_types/test.cpp @@ -0,0 +1,269 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// TRANSITION, GH-5563 +#pragma warning(push) +#pragma warning(disable : 6510) +#include +#pragma warning(pop) + +#include +#include +#include +#include + +using namespace std; + +template +basic_string convert_to_underlying_string(FwdIt first, FwdIt last) { + basic_string str; + for (; first != last; ++first) { + str.push_back(static_cast(*first)); + } + return str; +} + +template +StringType convert_from_underlying_string(const basic_string& str) { + StringType result; + for (const auto& ch : str) { + result.push_back(static_cast(ch)); + } + return result; +} + +template +class test_regex_traits { +private: + using rx_traits = regex_traits; + +public: + using char_type = SourceChar; + using string_type = basic_string; + using locale_type = typename rx_traits::locale_type; + using char_class_type = typename rx_traits::char_class_type; + + test_regex_traits() = default; + + static size_t length(const SourceChar* p) { + return string_type::char_traits::length(p); + } + + SourceChar translate(const SourceChar c) const { + return c; + } + + SourceChar translate_nocase(const SourceChar c) const { + return static_cast(inner.translate_nocase(static_cast(c))); + } + + template + string_type transform(FwdIt first, FwdIt last) const { + auto str = convert_to_underlying_string(first, last); + return convert_from_underlying_string(inner.transform(str.begin(), str.end())); + } + + template + string_type transform_primary(FwdIt first, FwdIt last) const { + auto str = convert_to_underlying_string(first, last); + return convert_from_underlying_string(inner.transform_primary(str.begin(), str.end())); + } + + template + string_type lookup_collatename(FwdIt first, FwdIt last) const { + auto str = convert_to_underlying_string(first, last); + return convert_from_underlying_string(inner.lookup_collatename(str.begin(), str.end())); + } + + template + char_class_type lookup_classname(FwdIt first, FwdIt last, bool icase = false) const { + auto str = convert_to_underlying_string(first, last); + return inner.lookup_classname(str.begin(), str.end(), icase); + } + + bool isctype(SourceChar c, char_class_type f) const { + return inner.isctype(static_cast(c), f); + } + + int value(SourceChar ch, int radix) const { + return inner.value(static_cast(ch), radix); + } + + locale_type imbue(locale_type l) { + return inner.imbue(l); + } + + locale_type getloc() const { + return inner.getloc(); + } + +private: + rx_traits inner; +}; + +enum class signed_wchar_enum : short {}; + +class wrapped_wchar { +public: + wrapped_wchar() = default; + explicit wrapped_wchar(char ch) : character(static_cast(ch)) {} + explicit wrapped_wchar(unsigned char ch) : character(ch) {} + explicit wrapped_wchar(wchar_t w) : character(w) {} + explicit wrapped_wchar(int w) = delete; + explicit wrapped_wchar(unsigned int w) : character(static_cast(w)) {} + + operator unsigned char() const { + return static_cast(character); + } + + operator unsigned int() const { + return static_cast(character); + } + + // to support test_regex_traits + operator wchar_t() const { + return character; + } + + operator char() const = delete; + operator int() const = delete; + + friend bool operator==(const wrapped_wchar& lhs, const wrapped_wchar& rhs) { + return lhs.character == rhs.character; + } + +private: + wchar_t character; +}; + +bool operator!=(const wrapped_wchar& lhs, const wrapped_wchar& rhs) { + return !(lhs == rhs); +} + +template +struct custom_char_traits { + using char_type = Elem; + using int_type = int; + using pos_type = streampos; + using off_type = streamoff; + using state_type = char_traits::state_type; + + static Elem* copy(Elem* const first1, const Elem* const first2, const size_t count) noexcept { + copy_n(first2, count, first1); + return first1; + } + + static Elem* move(Elem* const result, const Elem* const first2, const size_t count) noexcept /* strengthened */ { + if (result == first2) { + // nothing to do + } else if (first2 <= result && result < first2 + count) { + copy_backward(first2, first2 + count, result + count); + } else { + copy_n(first2, count, result); + } + + return result; + } + + static int compare(const Elem* first1, const Elem* first2, size_t count) noexcept { + for (; 0 < count; --count, ++first1, ++first2) { + if (*first1 != *first2) { + return custom_char_traits::lt(*first1, *first2) ? -1 : +1; + } + } + + return 0; + } + + static size_t length(const Elem* first) { + size_t count = 0; + while (*first != Elem{}) { + ++count; + ++first; + } + + return count; + } + + static const Elem* find(const Elem* first, size_t count, const Elem& ch) noexcept /* strengthened */ { + // look for ch in [first, first + count) + for (; 0 < count; --count, ++first) { + if (*first == ch) { + return first; + } + } + + return nullptr; + } + + static Elem* assign(const Elem* const first, size_t count, const Elem ch) { + for (Elem* next = first; count > 0; --count, ++next) { + *next = ch; + } + + return first; + } + + static void assign(Elem& left, const Elem& right) noexcept { + left = right; + } + + static bool eq(const Elem left, const Elem right) noexcept { + return left == right; + } + + static bool lt(const Elem left, const Elem right) noexcept { + return static_cast(left) < static_cast(right); + } + + static Elem to_char_type(const int_type meta) noexcept { + return static_cast(meta); + } + + static int_type to_int_type(const Elem ch) noexcept { + return static_cast(static_cast(ch)); + } + + static bool eq_int_type(const int_type left, const int_type right) noexcept { + return left == right; + } + + static int_type not_eof(const int_type meta) noexcept { + return meta != eof() ? meta : !eof(); + } + + static int_type eof() noexcept { + return static_cast(-1); + } +}; + +template <> +struct char_traits : custom_char_traits {}; + +template <> +struct char_traits : custom_char_traits {}; + +void test_gh_5592() { + // GH-5592: Remove _Uelem from the parser + // This test checks that the parser compiles and doesn't crash + // when user-defined character types are used. + + // This regex is only intended to exert many different paths in the parser, + // but it is not designed to be meaningful. + wstring test_regex = L"^a*[\u00fe-\\u0101][\u0123-\u0146](?:abc|def){0,3}(=.gwer)" + LR"(\b.{6}\B(\.\d\f)\g\1\0\x34(?!gef)[-f[.a.][=b=]c-e-]$)"; + { + auto str = convert_from_underlying_string>(test_regex); + basic_regex> re{str}; + } + + { + auto str = convert_from_underlying_string>(test_regex); + basic_regex> re{str}; + } +} + +int main() { + test_gh_5592(); + return 0; +} From 5bc9e931baafc242a23a08239d310bc4e4caa7ae Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sat, 9 Aug 2025 01:08:16 +0800 Subject: [PATCH 08/24] Implement LWG-4222 `expected` constructor from a single value missing a constraint (#5602) --- stl/inc/expected | 1 + tests/std/tests/P0323R12_expected/test.cpp | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/stl/inc/expected b/stl/inc/expected index 4080ad8b5ef..5f9422c7a5f 100644 --- a/stl/inc/expected +++ b/stl/inc/expected @@ -306,6 +306,7 @@ public: template > requires (!is_same_v, in_place_t> && !is_same_v, expected> + && !is_same_v, unexpect_t> && !_Is_specialization_v, unexpected> && (!is_same_v, bool> #if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-10655311 diff --git a/tests/std/tests/P0323R12_expected/test.cpp b/tests/std/tests/P0323R12_expected/test.cpp index ce2eccd161b..f88662efda9 100644 --- a/tests/std/tests/P0323R12_expected/test.cpp +++ b/tests/std/tests/P0323R12_expected/test.cpp @@ -2509,6 +2509,21 @@ static_assert(!is_assignable_v&, ambiguating_expected_ static_assert(test_lwg_3886()); +// Test LWG-4222 "expected constructor from a single value missing a constraint" + +struct ConstructibleFromEverything { + explicit ConstructibleFromEverything(auto); +}; + +struct ConvertibleFromInt { + ConvertibleFromInt(int); +}; + +static_assert(!is_constructible_v, unexpect_t&>); +static_assert(!is_constructible_v, const unexpect_t&>); +static_assert(!is_constructible_v, unexpect_t>); +static_assert(!is_constructible_v, const unexpect_t>); + int main() { test_unexpected::test_all(); static_assert(test_unexpected::test_all()); From 9ed9371aa15a6a8c48b0400fe11a915a803a5c28 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sat, 9 Aug 2025 01:11:28 +0800 Subject: [PATCH 09/24] Implement LWG-4242 `ranges::distance` does not work with volatile iterators (#5603) --- stl/inc/xutility | 51 ++++++++++++++----- .../test.cpp | 23 ++++++++- 2 files changed, 58 insertions(+), 16 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 5805b9012a6..c262b62aaaa 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3595,29 +3595,34 @@ namespace ranges { _EXPORT_STD inline constexpr _Advance_fn advance; + template + using _Distance_iterator_t = // per LWG-4303 + conditional_t>, decay_t<_Ty>, remove_const_t>>; + class _Distance_fn { public: template _Se> requires (!sized_sentinel_for<_Se, _It>) _NODISCARD _STATIC_CALL_OPERATOR constexpr iter_difference_t<_It> operator()( - _It _First, _Se _Last) _CONST_CALL_OPERATOR - noexcept(noexcept(_Distance_unchecked( - _STD _Get_unwrapped(_STD move(_First)), _STD _Get_unwrapped(_STD move(_Last))))) /* strengthened */ { + _It _First, _Se _Last) _CONST_CALL_OPERATOR noexcept(_Calculate_noexcept<_It, _Se>()) /* strengthened */ { _STD _Adl_verify_range(_First, _Last); return _Distance_unchecked( _RANGES _Unwrap_iter<_Se>(_STD move(_First)), _RANGES _Unwrap_sent<_It>(_STD move(_Last))); } - template > _Se> - _NODISCARD _STATIC_CALL_OPERATOR constexpr iter_difference_t> operator()( - _It&& _First, const _Se _Last) _CONST_CALL_OPERATOR - noexcept(noexcept(_Last - static_cast&>(_First))) /* strengthened */ { - return _Last - static_cast&>(_First); + template > _Se> + _NODISCARD _STATIC_CALL_OPERATOR constexpr iter_difference_t<_Distance_iterator_t<_It>> operator()(_It&& _First, + const _Se _Last) _CONST_CALL_OPERATOR noexcept(_Calculate_noexcept<_It, _Se>()) /* strengthened */ { + if constexpr (!is_array_v>) { + return _Last - _First; + } else { + return _Last - static_cast>(_First); + } } template _NODISCARD _STATIC_CALL_OPERATOR constexpr range_difference_t<_Rng> operator()( - _Rng&& _Range) _CONST_CALL_OPERATOR noexcept(_Nothrow_size<_Rng>) /* strengthened */ { + _Rng&& _Range) _CONST_CALL_OPERATOR noexcept(_Calculate_noexcept<_Rng>()) /* strengthened */ { if constexpr (sized_range<_Rng>) { return static_cast>(_RANGES size(_Range)); } else { @@ -3639,12 +3644,30 @@ namespace ranges { return _Count; } - template - static constexpr bool _Nothrow_size = - noexcept(_Distance_unchecked(_Ubegin(_STD declval<_Rng&>()), _Uend(_STD declval<_Rng&>()))); + template + _NODISCARD static consteval bool _Calculate_noexcept() noexcept { + if constexpr (sized_sentinel_for<_Se, _Distance_iterator_t<_It>>) { + if constexpr (!is_array_v>) { + return noexcept(_STD declval() - _STD declval<_It&>()); + } else { + return noexcept(_STD declval() - _STD declval>()); + } + } else if constexpr (sentinel_for<_Se, _It> && !sized_sentinel_for<_Se, _It>) { + return noexcept(_Distance_unchecked( + _STD _Get_unwrapped(_STD declval<_It>()), _STD _Get_unwrapped(_STD declval<_Se>()))); + } else { + return false; + } + } - template - static constexpr bool _Nothrow_size<_Rng> = noexcept(_RANGES size(_STD declval<_Rng&>())); + template + _NODISCARD static consteval bool _Calculate_noexcept() noexcept { + if constexpr (sized_range<_Rng>) { + return noexcept(_RANGES size(_STD declval<_Rng&>())); + } else { + return noexcept(_Distance_unchecked(_Ubegin(_STD declval<_Rng&>()), _Uend(_STD declval<_Rng&>()))); + } + } }; _EXPORT_STD inline constexpr _Distance_fn distance; diff --git a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp index aa4834cdeb3..e542dfe761c 100644 --- a/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_iterator_machinery/test.cpp @@ -2095,6 +2095,10 @@ struct std::common_type + concept can_call_ranges_difference = + requires(I&& it, S&& se) { distance(std::forward(it), std::forward(se)); }; + constexpr bool test_iter_forms() { { // Call next(i), validating that ++i is called once @@ -3086,7 +3090,6 @@ namespace iter_ops { } { -#ifndef __EDG__ // TRANSITION, VSO-1898890 // Call distance(i, s) with arrays which must be decayed to pointers. // (This behavior was regressed by LWG-3392.) int some_ints[] = {1, 2, 3}; @@ -3104,7 +3107,23 @@ namespace iter_ops { static_assert(noexcept(distance(const_ints + 1, const_ints))); assert(distance(const_ints, const_ints) == 0); static_assert(noexcept(distance(const_ints, const_ints))); -#endif // ^^^ no workaround ^^^ + } + + { // Test LWG-4242 "ranges::distance does not work with volatile iterators" + static_assert(can_call_ranges_difference); + static_assert(can_call_ranges_difference); + + // Per LWG-4303, ranges::distance should be well-constrained for non-pointer volatile iterators. + static_assert( + !can_call_ranges_difference, std::reverse_iterator>); + static_assert( + !can_call_ranges_difference&, std::reverse_iterator>); + + if (!std::is_constant_evaluated()) { + int arr[]{1, 2, 3}; + int* volatile ptr = arr; + assert(distance(ptr, arr + 3) == 3); + } } return true; From fae8c5511fc9673168ea38a6db8a8cbd9e06ed66 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sat, 9 Aug 2025 01:15:19 +0800 Subject: [PATCH 10/24] Implement P3223R2 Making `istream::ignore()` Less Surprising (#5604) --- stl/inc/istream | 5 +++++ stl/inc/yvals_core.h | 1 + .../tests/VSO_0000000_nullptr_stream_out/test.cpp | 13 +++++++++++++ 3 files changed, 19 insertions(+) diff --git a/stl/inc/istream b/stl/inc/istream index 7e820b16e0b..9a931500047 100644 --- a/stl/inc/istream +++ b/stl/inc/istream @@ -528,6 +528,11 @@ public: return *this; } + template , int> = 0> + basic_istream& ignore(const streamsize _Count, const _Elem _Delim) { + return ignore(_Count, _Traits::to_int_type(_Delim)); + } + basic_istream& __CLR_OR_THIS_CALL read(_Elem* _Str, streamsize _Count) { // read up to _Count characters into buffer ios_base::iostate _State = ios_base::goodbit; _Chcount = 0; diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index eab87990663..7de80182ab7 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -80,6 +80,7 @@ // (__cpp_lib_freestanding_algorithm and __cpp_lib_freestanding_array only) // P2937R0 Freestanding Library: Remove strtok // P2968R2 Make std::ignore A First-Class Object +// P3223R2 Making istream::ignore() Less Surprising // P3323R1 Forbid atomic, Specify atomic_ref // (for atomic) diff --git a/tests/std/tests/VSO_0000000_nullptr_stream_out/test.cpp b/tests/std/tests/VSO_0000000_nullptr_stream_out/test.cpp index 52e074acd43..635bce087c2 100644 --- a/tests/std/tests/VSO_0000000_nullptr_stream_out/test.cpp +++ b/tests/std/tests/VSO_0000000_nullptr_stream_out/test.cpp @@ -64,4 +64,17 @@ int main() { STATIC_ASSERT(OstreamInsertable); STATIC_ASSERT(OstreamInsertable); STATIC_ASSERT(!OstreamInsertable); + + { // Test P3223R2 "Making std::istream::ignore Less Surprising" + istringstream in{"\xF0\x9F\xA4\xA1 Clown Face"}; + decltype(auto) ret = in.ignore(100, '\xA1'); + + STATIC_ASSERT(is_same_v); + assert(&ret == &static_cast(in)); + assert(in.gcount() == 4); + + string s; + in >> s; + assert(s == "Clown"); + } } From c2097a16fe75cb47a15154c91d699272342fc137 Mon Sep 17 00:00:00 2001 From: Victor Chernyakin Date: Fri, 8 Aug 2025 10:17:47 -0700 Subject: [PATCH 11/24] ``: qualify `_Ugly` function calls to avoid ADL (#5629) Co-authored-by: Stephan T. Lavavej --- stl/inc/chrono | 254 +++++++++++++++++++++++++------------------------ 1 file changed, 131 insertions(+), 123 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index dd1dec49d1a..9df7808829e 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -46,7 +46,7 @@ _STD_BEGIN #if _HAS_CXX17 _NODISCARD inline int _Check_convert_result(const __std_fs_convert_result _Result) { if (_Result._Err != __std_win_error::_Success) { - _Throw_system_error_from_std_win_error(_Result._Err); + _STD _Throw_system_error_from_std_win_error(_Result._Err); } return _Result._Len; @@ -59,17 +59,17 @@ _NODISCARD basic_string _Convert_w if (!_Input.empty()) { if (!_STD _In_range(_Input.size())) { - _Throw_system_error(errc::invalid_argument); + _STD _Throw_system_error(errc::invalid_argument); } - const int _Len = _Check_convert_result( - __std_fs_convert_wide_to_narrow(_Code_page, _Input.data(), static_cast(_Input.size()), nullptr, 0)); + const int _Len = _STD _Check_convert_result( + ::__std_fs_convert_wide_to_narrow(_Code_page, _Input.data(), static_cast(_Input.size()), nullptr, 0)); _Output.resize(static_cast(_Len)); const auto _Data_as_char = reinterpret_cast(_Output.data()); - (void) _Check_convert_result(__std_fs_convert_wide_to_narrow( + (void) _STD _Check_convert_result(::__std_fs_convert_wide_to_narrow( _Code_page, _Input.data(), static_cast(_Input.size()), _Data_as_char, _Len)); } @@ -86,7 +86,7 @@ namespace chrono { static constexpr bool is_steady = false; _NODISCARD static time_point now() noexcept { // get current time - return time_point(duration(_Xtime_get_ticks())); + return time_point(duration(::_Xtime_get_ticks())); } _NODISCARD static __time64_t to_time_t(const time_point& _Time) noexcept { // convert to __time64_t @@ -157,22 +157,22 @@ namespace chrono { *--_Rnext = 's'; *--_Rnext = ']'; if (_Den != 1) { - _Rnext = _UIntegral_to_buff(_Rnext, static_cast(_Den)); + _Rnext = _STD _UIntegral_to_buff(_Rnext, static_cast(_Den)); *--_Rnext = '/'; } - _Rnext = _UIntegral_to_buff(_Rnext, static_cast(_Num)); + _Rnext = _STD _UIntegral_to_buff(_Rnext, static_cast(_Num)); *--_Rnext = '['; return _Rnext; } template void _Write_unit_suffix(basic_ostream<_CharT, _Traits>& _Os) { - constexpr auto _Suffix = _Get_literal_unit_suffix<_CharT, _Period>(); + constexpr auto _Suffix = _CHRONO _Get_literal_unit_suffix<_CharT, _Period>(); if constexpr (_Suffix == nullptr) { _CharT _Buffer[2 * (numeric_limits::digits10 + 1) + 5] = {}; // 2 numbers + "[/]s\0" const _CharT* const _Begin = - _Get_general_unit_suffix<_CharT>(_STD end(_Buffer), _Period::num, _Period::den); + _CHRONO _Get_general_unit_suffix<_CharT>(_STD end(_Buffer), _Period::num, _Period::den); _Os << _Begin; } else { _Os << _Suffix; @@ -187,7 +187,7 @@ namespace chrono { _Sstr.imbue(_Os.getloc()); _Sstr.precision(_Os.precision()); _Sstr << _Dur.count(); - _Write_unit_suffix<_Period>(_Sstr); + _CHRONO _Write_unit_suffix<_Period>(_Sstr); return _Os << _Sstr.str(); } @@ -831,7 +831,7 @@ namespace chrono { return false; } - return _Day >= _CHRONO day{1} && _Day <= _Last_day(_Year, _Month); + return _Day >= _CHRONO day{1} && _Day <= _CHRONO _Last_day(_Year, _Month); } _NODISCARD constexpr int _Calculate_weekday() const noexcept { @@ -1095,7 +1095,7 @@ namespace chrono { return _Month_day_last; } _NODISCARD constexpr day day() const noexcept { - return _Last_day(year(), month()); + return _CHRONO _Last_day(year(), month()); } _NODISCARD constexpr operator sys_days() const noexcept { @@ -1236,7 +1236,8 @@ namespace chrono { const sys_days _First_of_month = year_month_day{_Year, _Month, day{1}}; const days _First_weekday = weekday() - _CHRONO weekday{_First_of_month} + days{1}; const days _Last = _First_weekday + days{28}; - return static_cast(_Last.count()) <= static_cast(_Last_day(_Year, _Month)); + return static_cast(_Last.count()) + <= static_cast(_CHRONO _Last_day(_Year, _Month)); } private: @@ -1574,10 +1575,11 @@ namespace chrono { } template - constexpr bool - _Duration_underflows_with = !treat_as_floating_point_v - && _From::period::den / _Gcd(_From::period::den, _To::period::den) - > INTMAX_MAX / (_To::period::num / _Gcd(_From::period::num, _To::period::num)); + constexpr bool _Duration_underflows_with = !treat_as_floating_point_v + && _From::period::den / _STD _Gcd(_From::period::den, _To::period::den) + > INTMAX_MAX + / (_To::period::num + / _STD _Gcd(_From::period::num, _To::period::num)); template _NODISCARD constexpr _To _Duration_cast_underflow_to_zero(const _From& _Dur) { @@ -1627,21 +1629,23 @@ namespace chrono { return _Result > 18u ? 6u : _Result; }(); using precision = - duration, ratio<1, _Pow10(fractional_width)>>; + duration, ratio<1, _CHRONO _Pow10(fractional_width)>>; constexpr hh_mm_ss() noexcept : hh_mm_ss{_Duration::zero()} {} constexpr explicit hh_mm_ss(_Duration _Dur) : _Is_neg{_Dur < _Duration::zero()}, - _Hours{_Duration_cast_underflow_to_zero<_CHRONO hours>(_CHRONO abs(_Dur))}, - _Mins{_Duration_cast_underflow_to_zero<_CHRONO minutes>( - _Remove_duration_part<_CHRONO hours>(_CHRONO abs(_Dur)))}, - _Secs{_Duration_cast_underflow_to_zero<_CHRONO seconds>( - _Remove_duration_part<_CHRONO minutes>(_Remove_duration_part<_CHRONO hours>(_CHRONO abs(_Dur))))} { + _Hours{_CHRONO _Duration_cast_underflow_to_zero<_CHRONO hours>(_CHRONO abs(_Dur))}, + _Mins{_CHRONO _Duration_cast_underflow_to_zero<_CHRONO minutes>( + _CHRONO _Remove_duration_part<_CHRONO hours>(_CHRONO abs(_Dur)))}, + _Secs{_CHRONO _Duration_cast_underflow_to_zero<_CHRONO seconds>( + _CHRONO _Remove_duration_part<_CHRONO minutes>( + _CHRONO _Remove_duration_part<_CHRONO hours>(_CHRONO abs(_Dur))))} { if constexpr (treat_as_floating_point_v) { // no need to deal with underflow here, because floating durations allow it _Sub_secs = _CHRONO abs(_Dur) - hours() - minutes() - seconds(); } else { - _Sub_secs = _CHRONO duration_cast(_Remove_duration_part<_CHRONO seconds>(_CHRONO abs(_Dur))); + _Sub_secs = + _CHRONO duration_cast(_CHRONO _Remove_duration_part<_CHRONO seconds>(_CHRONO abs(_Dur))); } } @@ -1777,11 +1781,11 @@ namespace chrono { unique_ptr<_Tzdb_info, _Tzdb_deleter<_Tzdb_info>> _Info{_Raw_ptr}; if (_Info == nullptr) { - _Xbad_alloc(); + _STD _Xbad_alloc(); } else if (_Info->_Err == __std_tzdb_error::_Win_error) { - _XGetLastError(); + _STD _XGetLastError(); } else if (_Info->_Err == __std_tzdb_error::_Icu_error) { - _Xruntime_error("Internal error loading IANA database information"); + _STD _Xruntime_error("Internal error loading IANA database information"); } return _Info; @@ -1818,9 +1822,9 @@ namespace chrono { _NODISCARD sys_time> to_sys(const local_time<_Duration>& _Local) const { const auto _Info = get_info(_Local); if (_Info.result == local_info::nonexistent) { - _Throw_nonexistent_local_time(_Local, _Info); + _CHRONO _Throw_nonexistent_local_time(_Local, _Info); } else if (_Info.result == local_info::ambiguous) { - _Throw_ambiguous_local_time(_Local, _Info); + _CHRONO _Throw_ambiguous_local_time(_Local, _Info); } return sys_time>{_Local.time_since_epoch() - _Info.first.offset}; @@ -1862,8 +1866,8 @@ namespace chrono { const auto _Tz_len = _Name.size(); - const auto _Info = - _Make_unique_tzdb_info<__std_tzdb_get_sys_info>(_Tz_arg.c_str(), _Tz_len, _Internal_dur.count()); + const auto _Info = _CHRONO _Make_unique_tzdb_info<__std_tzdb_get_sys_info>( + _Tz_arg.c_str(), _Tz_len, _Internal_dur.count()); constexpr auto _Min_internal = _CHRONO duration_cast<_Internal_duration>(_Min_seconds.time_since_epoch()).count(); @@ -2098,7 +2102,7 @@ namespace chrono { // [time.zone.db] _NODISCARD inline string _Tzdb_generate_current_zone() { - auto _Info = _Make_unique_tzdb_info<__std_tzdb_get_current_zone>(); + auto _Info = _CHRONO _Make_unique_tzdb_info<__std_tzdb_get_current_zone>(); return _Info->_Tz_name; } @@ -2123,29 +2127,29 @@ namespace chrono { bool _All_ls_positive; _NODISCARD const time_zone* locate_zone(string_view _Tz_name) const { - auto _Tz = _Locate_zone_impl(zones, _Tz_name); + auto _Tz = _CHRONO _Locate_zone_impl(zones, _Tz_name); if (_Tz != nullptr) { return _Tz; } - const auto _Link = _Locate_zone_impl(links, _Tz_name); + const auto _Link = _CHRONO _Locate_zone_impl(links, _Tz_name); if (_Link != nullptr) { - _Tz = _Locate_zone_impl(zones, _Link->target()); + _Tz = _CHRONO _Locate_zone_impl(zones, _Link->target()); if (_Tz != nullptr) { return _Tz; } } - _Xruntime_error("unable to locate time_zone with given name"); + _STD _Xruntime_error("unable to locate time_zone with given name"); } _NODISCARD const time_zone* current_zone() const { - return locate_zone(_Tzdb_generate_current_zone()); + return locate_zone(_CHRONO _Tzdb_generate_current_zone()); } }; _NODISCARD inline tuple, vector> _Tzdb_generate_time_zones() { - auto _Info = _Make_unique_tzdb_info<__std_tzdb_get_time_zones>(); + auto _Info = _CHRONO _Make_unique_tzdb_info<__std_tzdb_get_time_zones>(); vector _Time_zones; vector _Time_zone_links; @@ -2204,12 +2208,12 @@ namespace chrono { size_t _Reg_post_2018_ls_size; // number of post-2018 LSs found in the registry unique_ptr<__std_tzdb_leap_info[], _Tzdb_deleter<__std_tzdb_leap_info[]>> _Reg_ls_data{ - __std_tzdb_get_leap_seconds(_Known_post_2018_ls_size, &_Reg_post_2018_ls_size)}; + ::__std_tzdb_get_leap_seconds(_Known_post_2018_ls_size, &_Reg_post_2018_ls_size)}; if (_Reg_post_2018_ls_size > _Known_post_2018_ls_size && !_Reg_ls_data) { - _Xbad_alloc(); // registry has new data, but failed to allocate storage + _STD _Xbad_alloc(); // registry has new data, but failed to allocate storage } else if (_Reg_post_2018_ls_size == 0 && _Reg_ls_data) { - _XGetLastError(); // allocated storage for registry data, but failed to read + _STD _XGetLastError(); // allocated storage for registry data, but failed to read } const size_t _New_size = _Pre_2018_count + _Reg_post_2018_ls_size; // total size with registry data @@ -2257,8 +2261,8 @@ namespace chrono { tzdb_list& operator=(const tzdb_list&) = delete; explicit tzdb_list(_Secret_tzdb_list_construct_tag) { - auto [_Icu_version, _Zones, _Links] = _Tzdb_generate_time_zones(); - auto [_Leap_sec, _All_ls_positive] = _Tzdb_generate_leap_seconds(0); + auto [_Icu_version, _Zones, _Links] = _CHRONO _Tzdb_generate_time_zones(); + auto [_Leap_sec, _All_ls_positive] = _CHRONO _Tzdb_generate_leap_seconds(0); auto _Version = _Icu_version + "." + _STD to_string(_Leap_sec.size()); // TRANSITION, NVCC (was DevCom-10732572), should call emplace_front with construction arguments _Tzdb_list.emplace_front(tzdb{ @@ -2301,7 +2305,8 @@ namespace chrono { const tzdb& _Reload() { _Unique_lock _Lk(_Tzdb_mutex); - auto [_Leap_sec, _All_ls_positive] = _Tzdb_generate_leap_seconds(_Tzdb_list.front().leap_seconds.size()); + auto [_Leap_sec, _All_ls_positive] = + _CHRONO _Tzdb_generate_leap_seconds(_Tzdb_list.front().leap_seconds.size()); if (!_Leap_sec.empty()) { const auto& _Tzdb = _Tzdb_list.front(); @@ -2317,7 +2322,7 @@ namespace chrono { _Links.emplace_back(_Secret_time_zone_link_construct_tag{}, _Link.name(), _Link.target()); } - auto _Version = _Tzdb_update_version(_Tzdb.version, _Leap_sec.size()); + auto _Version = _CHRONO _Tzdb_update_version(_Tzdb.version, _Leap_sec.size()); // TRANSITION, NVCC (was DevCom-10732572), should call emplace_front with construction arguments _Tzdb_list.emplace_front(tzdb{ _STD move(_Version), _STD move(_Zones), _STD move(_Links), _STD move(_Leap_sec), _All_ls_positive}); @@ -2331,14 +2336,14 @@ namespace chrono { struct _NODISCARD _Shared_lock { explicit _Shared_lock(_Smtx_t& _Mtx_) : _Mtx{&_Mtx_} { - _Smtx_lock_shared(_Mtx); + ::_Smtx_lock_shared(_Mtx); } _Shared_lock(const _Shared_lock&) = delete; _Shared_lock& operator=(const _Shared_lock&) = delete; ~_Shared_lock() { - _Smtx_unlock_shared(_Mtx); + ::_Smtx_unlock_shared(_Mtx); } _Smtx_t* _Mtx; @@ -2346,14 +2351,14 @@ namespace chrono { struct _NODISCARD _Unique_lock { explicit _Unique_lock(_Smtx_t& _Mtx_) : _Mtx{&_Mtx_} { - _Smtx_lock_exclusive(_Mtx); + ::_Smtx_lock_exclusive(_Mtx); } _Unique_lock(const _Unique_lock&) = delete; _Unique_lock& operator=(const _Unique_lock&) = delete; ~_Unique_lock() { - _Smtx_unlock_exclusive(_Mtx); + ::_Smtx_unlock_exclusive(_Mtx); } _Smtx_t* _Mtx; @@ -2368,20 +2373,20 @@ namespace chrono { return *_Tzdb_ptr; } - auto _My_tzdb = static_cast(__std_calloc_crt(1, sizeof(tzdb_list))); + auto _My_tzdb = static_cast(::__std_calloc_crt(1, sizeof(tzdb_list))); if (_My_tzdb == nullptr) { - _Xruntime_error("bad allocation"); // not bad_alloc, see N4950 [time.zone.db.access]/4 + _STD _Xruntime_error("bad allocation"); // not bad_alloc, see N4950 [time.zone.db.access]/4 } _TRY_BEGIN _STD construct_at(_My_tzdb, _Secret_tzdb_list_construct_tag{}); _CATCH(const runtime_error&) - __std_free_crt(_My_tzdb); + ::__std_free_crt(_My_tzdb); _RERAISE; _CATCH(const exception& _Except) #if _HAS_EXCEPTIONS - __std_free_crt(_My_tzdb); - _Xruntime_error(_Except.what()); + ::__std_free_crt(_My_tzdb); + _STD _Xruntime_error(_Except.what()); #endif // _HAS_EXCEPTIONS _CATCH_END @@ -2389,7 +2394,7 @@ namespace chrono { _Tzdb_ptr = _My_tzdb; } else { _STD destroy_at(_My_tzdb); - __std_free_crt(_My_tzdb); + ::__std_free_crt(_My_tzdb); } return *_Tzdb_ptr; @@ -2414,7 +2419,7 @@ namespace chrono { _RERAISE; _CATCH(const exception& _Except) #if _HAS_EXCEPTIONS - _Xruntime_error(_Except.what()); + _STD _Xruntime_error(_Except.what()); #endif // _HAS_EXCEPTIONS _CATCH_END } @@ -2422,8 +2427,8 @@ namespace chrono { _EXPORT_STD _NODISCARD inline string remote_version() { const auto& _Tzdb = _CHRONO get_tzdb(); const auto& _Version = _Tzdb.version; - const auto [_Leap_sec, _Ignored] = _Tzdb_generate_leap_seconds(_Tzdb.leap_seconds.size()); - return _Leap_sec.empty() ? _Version : _Tzdb_update_version(_Version, _Leap_sec.size()); + const auto [_Leap_sec, _Ignored] = _CHRONO _Tzdb_generate_leap_seconds(_Tzdb.leap_seconds.size()); + return _Leap_sec.empty() ? _Version : _CHRONO _Tzdb_update_version(_Version, _Leap_sec.size()); } // [time.zone.zonedtraits] @@ -2837,7 +2842,7 @@ namespace filesystem { static constexpr bool is_steady = false; _NODISCARD static time_point now() noexcept { // get current time; undo epoch adjustment - return time_point(duration(_Xtime_get_ticks() + __std_fs_file_time_epoch_adjustment)); // TRANSITION, ABI + return time_point(duration(::_Xtime_get_ticks() + __std_fs_file_time_epoch_adjustment)); // TRANSITION, ABI } #if _HAS_CXX20 @@ -3061,7 +3066,7 @@ namespace chrono { } template - constexpr auto _Clock_cast_choice = _Choose_clock_cast<_DestClock, _SourceClock, _Duration>(); + constexpr auto _Clock_cast_choice = _CHRONO _Choose_clock_cast<_DestClock, _SourceClock, _Duration>(); _EXPORT_STD template requires (_Clock_cast_choice<_DestClock, _SourceClock, _Duration> != _Clock_cast_strategy::_None) @@ -3665,7 +3670,7 @@ namespace chrono { } } - const auto _Offset = _It == _Ls_vector.begin() ? seconds{0} : _Prev_iter(_It)->_Elapsed(); + const auto _Offset = _It == _Ls_vector.begin() ? seconds{0} : _STD _Prev_iter(_It)->_Elapsed(); _Dur += _CHRONO duration_cast<_DurationType>(_Offset); if (_Original_leap == _Leap_second_rep::_File_time) { @@ -4787,7 +4792,7 @@ public: // same as _Specs_setter constexpr void _On_fill(basic_string_view<_CharT> _Sv) { if (_Sv.size() > _STD size(_Specs._Fill)) { - _Throw_format_error("Invalid fill (too long)."); + _STD _Throw_format_error("Invalid fill (too long)."); } const auto _Pos = _STD _Copy_unchecked(_Sv._Unchecked_begin(), _Sv._Unchecked_end(), _Specs._Fill); @@ -4833,11 +4838,11 @@ public: constexpr void _On_conversion_spec(char _Modifier, _CharT _Type) { if (_Modifier != '\0' && _Modifier != 'E' && _Modifier != 'O') { - _Throw_format_error("Invalid modifier specification."); + _STD _Throw_format_error("Invalid modifier specification."); } if (_Type < 0 || _Type > (numeric_limits::max)()) { - _Throw_format_error("Invalid type specification."); + _STD _Throw_format_error("Invalid type specification."); } _Chrono_spec<_CharT> _Conv_spec{._Modifier = _Modifier, ._Type = static_cast(_Type)}; @@ -4855,7 +4860,7 @@ private: _NODISCARD static constexpr int _Verify_dynamic_arg_index_in_range(const size_t _Idx) { if (!_STD in_range(_Idx)) { - _Throw_format_error("Dynamic width or precision index too large."); + _STD _Throw_format_error("Dynamic width or precision index too large."); } return static_cast(_Idx); @@ -4867,7 +4872,7 @@ template _Callbacks_type> _NODISCARD constexpr const _CharT* _Parse_conversion_specs( const _CharT* _Begin, const _CharT* _End, _Callbacks_type&& _Callbacks) { if (_Begin == _End || *_Begin == '}') { - _Throw_format_error("Invalid format string."); + _STD _Throw_format_error("Invalid format string."); } char _Mod = '\0'; @@ -4877,7 +4882,7 @@ _NODISCARD constexpr const _CharT* _Parse_conversion_specs( _Mod = static_cast(_Ch); ++_Begin; if (_Begin == _End || *_Begin == '}') { - _Throw_format_error("Invalid format string - missing type after modifier."); + _STD _Throw_format_error("Invalid format string - missing type after modifier."); } } @@ -4894,18 +4899,18 @@ _NODISCARD constexpr const _CharT* _Parse_chrono_format_specs( return _Begin; } - _Begin = _Parse_align(_Begin, _End, _Callbacks); + _Begin = _STD _Parse_align(_Begin, _End, _Callbacks); if (_Begin == _End) { return _Begin; } - _Begin = _Parse_width(_Begin, _End, _Callbacks); + _Begin = _STD _Parse_width(_Begin, _End, _Callbacks); if (_Begin == _End) { return _Begin; } if (*_Begin == '.') { - _Begin = _Parse_precision(_Begin, _End, _Callbacks); + _Begin = _STD _Parse_precision(_Begin, _End, _Callbacks); if (_Begin == _End) { return _Begin; } @@ -4920,7 +4925,7 @@ _NODISCARD constexpr const _CharT* _Parse_chrono_format_specs( } if (*_Begin != '}' && *_Begin != '%') { - _Throw_format_error("Invalid format string - chrono-specs must begin with conversion-spec"); + _STD _Throw_format_error("Invalid format string - chrono-specs must begin with conversion-spec"); } // chrono-spec @@ -4929,7 +4934,7 @@ _NODISCARD constexpr const _CharT* _Parse_chrono_format_specs( // because '%' isn't used as a non-lead-byte in any supported multibyte encoding. if (*_Begin == '%') { // conversion-spec if (++_Begin == _End) { - _Throw_format_error("Invalid format string - missing type after %"); + _STD _Throw_format_error("Invalid format string - missing type after %"); } switch (*_Begin) { @@ -4946,7 +4951,7 @@ _NODISCARD constexpr const _CharT* _Parse_chrono_format_specs( ++_Begin; break; default: // some other type - _Begin = _Parse_conversion_specs(_Begin, _End, _Callbacks); + _Begin = _STD _Parse_conversion_specs(_Begin, _End, _Callbacks); break; } } else { // literal-char @@ -5003,7 +5008,8 @@ namespace chrono { template void _Write_seconds(basic_ostream<_CharT, _Traits>& _Os, const hh_mm_ss<_Duration>& _Val) { - _Write_fractional_seconds::fractional_width>(_Os, _Val.seconds(), _Val.subseconds()); + _CHRONO _Write_fractional_seconds::fractional_width>( + _Os, _Val.seconds(), _Val.subseconds()); } template @@ -5015,34 +5021,35 @@ namespace chrono { const hh_mm_ss _Hms{_Val - _Dp}; constexpr auto _Fractional_width = decltype(_Hms)::fractional_width; if (_Lsi.is_leap_second) { - _Write_fractional_seconds<_Fractional_width>(_Os, _Hms.seconds() + seconds{60}, _Hms.subseconds()); + _CHRONO _Write_fractional_seconds<_Fractional_width>( + _Os, _Hms.seconds() + seconds{60}, _Hms.subseconds()); } else { - _Write_fractional_seconds<_Fractional_width>(_Os, _Hms.seconds(), _Hms.subseconds()); + _CHRONO _Write_fractional_seconds<_Fractional_width>(_Os, _Hms.seconds(), _Hms.subseconds()); } } else { const auto _Dp = _CHRONO floor(_Val); - _Write_seconds(_Os, hh_mm_ss{_Val - _Dp}); + _CHRONO _Write_seconds(_Os, hh_mm_ss{_Val - _Dp}); } } template void _Write_seconds(basic_ostream<_CharT, _Traits>& _Os, const _Local_time_format_t<_Duration>& _Val) { - _Write_seconds(_Os, _Val._Time); + _CHRONO _Write_seconds(_Os, _Val._Time); } template void _Write_seconds(basic_ostream<_CharT, _Traits>& _Os, const duration<_Rep, _Period>& _Val) { - _Write_seconds(_Os, hh_mm_ss{_Val}); + _CHRONO _Write_seconds(_Os, hh_mm_ss{_Val}); } template _NODISCARD tm _Fill_tm(const _Ty& _Val) { if constexpr (_Is_specialization_v<_Ty, duration>) { - return _Fill_tm(hh_mm_ss{_Val}); + return _CHRONO _Fill_tm(hh_mm_ss{_Val}); } else if constexpr (_Is_specialization_v<_Ty, _Local_time_format_t>) { - return _Fill_tm(_Val._Time); + return _CHRONO _Fill_tm(_Val._Time); } else if constexpr (_Is_any_of_v<_Ty, year_month_weekday, year_month_weekday_last>) { - auto _Tm = _Fill_tm(year_month_day{_Val}); + auto _Tm = _CHRONO _Fill_tm(year_month_day{_Val}); _Tm.tm_wday = static_cast(_Val.weekday().c_encoding()); return _Tm; } else if constexpr (_Is_any_of_v<_Ty, sys_info, local_info>) { @@ -5051,8 +5058,8 @@ namespace chrono { const auto _Dp = _CHRONO floor(_Val); const year_month_day _Ymd{_Dp}; const hh_mm_ss _Time{_Val - _Dp}; - const auto _Hms = _Fill_tm(_Time); - auto _Tm = _Fill_tm(_Ymd); + const auto _Hms = _CHRONO _Fill_tm(_Time); + auto _Tm = _CHRONO _Fill_tm(_Ymd); _Tm.tm_sec = _Hms.tm_sec; _Tm.tm_min = _Hms.tm_min; _Tm.tm_hour = _Hms.tm_hour; @@ -5241,7 +5248,7 @@ namespace chrono { basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& _Os, const sys_info& _Val) { return _Os << _STD format(_Os.getloc(), _STATICALLY_WIDEN(_CharT, "begin: {}, end: {}, offset: {}, save: {}, abbrev: {}"), // - _Val.begin, _Val.end, _Val.offset, _Val.save, _Widen_string<_CharT>(_Val.abbrev)); + _Val.begin, _Val.end, _Val.offset, _Val.save, _CHRONO _Widen_string<_CharT>(_Val.abbrev)); } _EXPORT_STD template @@ -5332,30 +5339,30 @@ namespace chrono { _NODISCARD constexpr auto _Parse(basic_format_parse_context<_CharT>& _Parse_ctx) { _Chrono_specs_setter<_CharT, basic_format_parse_context<_CharT>> _Callback{_Specs, _Parse_ctx}; const auto _It = - _Parse_chrono_format_specs(_Parse_ctx._Unchecked_begin(), _Parse_ctx._Unchecked_end(), _Callback); + _STD _Parse_chrono_format_specs(_Parse_ctx._Unchecked_begin(), _Parse_ctx._Unchecked_end(), _Callback); const auto _Res_iter = _Parse_ctx.begin() + (_It - _Parse_ctx._Unchecked_begin()); if (_It != _Parse_ctx._Unchecked_end() && *_It != '}') { - _Throw_format_error("Missing '}' in format string."); + _STD _Throw_format_error("Missing '}' in format string."); } if constexpr (_Is_specialization_v<_Ty, duration>) { if constexpr (!treat_as_floating_point_v) { if (_Specs._Precision != -1) { - _Throw_format_error("Precision specification invalid for chrono::duration type with " - "integral representation type, see N4950 [time.format]/1."); + _STD _Throw_format_error("Precision specification invalid for chrono::duration type with " + "integral representation type, see N4950 [time.format]/1."); } } } else { if (_Specs._Precision != -1) { - _Throw_format_error("Precision specification invalid for non-chrono::duration type, " - "see N4950 [time.format]/1."); + _STD _Throw_format_error("Precision specification invalid for non-chrono::duration type, " + "see N4950 [time.format]/1."); } } for (const auto& _Spec : _Specs._Chrono_specs_list) { if (_Spec._Type != '\0' && !_Is_valid_type<_Ty>(_Spec._Type)) { - _Throw_format_error("Invalid type."); + _STD _Throw_format_error("Invalid type."); } _Check_modifier(_Spec._Type, _Spec._Modifier); } @@ -5411,7 +5418,7 @@ namespace chrono { } } - _Throw_format_error("Incompatible modifier for type"); + _STD _Throw_format_error("Incompatible modifier for type"); } template @@ -5497,9 +5504,9 @@ namespace chrono { wchar_t _Fmt_str[4]; _Chrono_spec _Wspec{._Modifier = _Spec._Modifier, ._Type = _Spec._Type}; - _Wstream << _STD put_time(&_Time, _Fmt_string(_Wspec, _Fmt_str)); + _Wstream << _STD put_time(&_Time, _CHRONO _Fmt_string(_Wspec, _Fmt_str)); - _Stream << _Convert_wide_to_narrow>( + _Stream << _STD _Convert_wide_to_narrow>( __std_code_page::_Utf8, _Wstream.view(), allocator{}); continue; @@ -5508,21 +5515,21 @@ namespace chrono { #endif // ^^^ no workaround ^^^ _CharT _Fmt_str[4]; - _Stream << _STD put_time<_CharT>(&_Time, _Fmt_string(_Spec, _Fmt_str)); + _Stream << _STD put_time<_CharT>(&_Time, _CHRONO _Fmt_string(_Spec, _Fmt_str)); } } int _Estimated_width = -1; - (void) _Measure_string_prefix(_Stream.view(), _Estimated_width); + (void) _STD _Measure_string_prefix(_Stream.view(), _Estimated_width); auto _Format_specs = _Specs; if (_Specs._Dynamic_width_index >= 0) { - _Format_specs._Width = _Get_dynamic_specs<_Width_checker>( + _Format_specs._Width = _STD _Get_dynamic_specs<_Width_checker>( _FormatCtx.arg(static_cast(_Specs._Dynamic_width_index))); } - return _Write_aligned(_STD move(_FormatCtx.out()), _Estimated_width, _Format_specs, _Fmt_align::_Left, - [&](auto _Out) { return _Fmt_write(_STD move(_Out), _Stream.view()); }); + return _STD _Write_aligned(_STD move(_FormatCtx.out()), _Estimated_width, _Format_specs, _Fmt_align::_Left, + [&](auto _Out) { return _STD _Fmt_write(_STD move(_Out), _Stream.view()); }); } // This echoes the functionality of put_time, but is able to handle invalid dates (when !ok()) since the @@ -5534,7 +5541,7 @@ namespace chrono { basic_ostream<_CharT>& _Os, const _Chrono_spec<_CharT>& _Spec, const tm& _Time, const _Ty& _Val) const { if constexpr (is_same_v<_Ty, local_info>) { if (_Val.result != local_info::unique) { - _Throw_format_error("Cannot print non-unique local_info"); + _STD _Throw_format_error("Cannot print non-unique local_info"); } } @@ -5547,7 +5554,7 @@ namespace chrono { // Most months have a proper last day, but February depends on the year. if constexpr (is_same_v<_Ty, month_day_last>) { if (_Val.month() == February) { - _Throw_format_error("Cannot print the last day of February without a year"); + _STD _Throw_format_error("Cannot print the last day of February without a year"); } if (!_Val.ok()) { @@ -5568,17 +5575,18 @@ namespace chrono { case 'G': if constexpr (is_same_v<_Ty, year_month>) { if (_Val.month() == January || _Val.month() == December) { - _Throw_format_error( + _STD _Throw_format_error( "The ISO week-based year for a year_month of January or December is ambiguous."); } if (!_Val.ok()) { - _Throw_format_error("The ISO week-based year for an out-of-bounds year_month is ambiguous."); + _STD _Throw_format_error( + "The ISO week-based year for an out-of-bounds year_month is ambiguous."); } const char _Gregorian_type = _Spec._Type == 'g' ? 'y' : 'Y'; _CharT _Fmt_str[4]; - _Os << _STD put_time(&_Time, _Fmt_string({._Type = _Gregorian_type}, _Fmt_str)); + _Os << _STD put_time(&_Time, _CHRONO _Fmt_string({._Type = _Gregorian_type}, _Fmt_str)); return true; } else { return false; @@ -5598,14 +5606,14 @@ namespace chrono { return false; case 'j': if constexpr (_Is_specialization_v<_Ty, duration>) { - _Os << _STD abs(_Duration_cast_underflow_to_zero(_Val).count()); + _Os << _STD abs(_CHRONO _Duration_cast_underflow_to_zero(_Val).count()); return true; } else { return false; } case 'q': if constexpr (_Is_specialization_v<_Ty, duration>) { - _Write_unit_suffix(_Os); + _CHRONO _Write_unit_suffix(_Os); } else { _STL_INTERNAL_CHECK(false); } @@ -5681,18 +5689,18 @@ namespace chrono { if (_Has_modifier) { return false; } - _Write_seconds(_Os, _Val); + _CHRONO _Write_seconds(_Os, _Val); return true; case 'Z': if constexpr (is_same_v<_Ty, sys_info>) { - _Os << _Widen_string<_CharT>(_Val.abbrev); + _Os << _CHRONO _Widen_string<_CharT>(_Val.abbrev); } else if constexpr (is_same_v<_Ty, local_info>) { - _Os << _Widen_string<_CharT>(_Val.first.abbrev); + _Os << _CHRONO _Widen_string<_CharT>(_Val.first.abbrev); } else if constexpr (_Is_specialization_v<_Ty, _Local_time_format_t>) { if (_Val._Abbrev == nullptr) { - _Throw_format_error("Cannot print local-time-format-t with null abbrev."); + _STD _Throw_format_error("Cannot print local-time-format-t with null abbrev."); } - _Os << _Widen_string<_CharT>(*_Val._Abbrev); + _Os << _CHRONO _Widen_string<_CharT>(*_Val._Abbrev); } else { _Os << _Time_zone_abbreviation; } @@ -5707,7 +5715,7 @@ namespace chrono { _Offset = hh_mm_ss{_Val.first.offset}; } else if constexpr (_Is_specialization_v<_Ty, _Local_time_format_t>) { if (_Val._Offset_sec == nullptr) { - _Throw_format_error("Cannot print local-time-format-t with null offset_sec."); + _STD _Throw_format_error("Cannot print local-time-format-t with null offset_sec."); } _Offset = hh_mm_ss{*_Val._Offset_sec}; } else { @@ -5802,12 +5810,12 @@ namespace chrono { case 'j': if constexpr (is_same_v<_Ty, month_day>) { if (_Val.month() > February) { - _Throw_format_error("The day of year for a month_day past February is ambiguous."); + _STD _Throw_format_error("The day of year for a month_day past February is ambiguous."); } return true; } else if constexpr (is_same_v<_Ty, month_day_last>) { if (_Val.month() >= February) { - _Throw_format_error( + _STD _Throw_format_error( "The day of year for a month_day_last other than January is ambiguous"); } return true; @@ -5836,7 +5844,7 @@ namespace chrono { return false; }; if (!_Validate()) { - _Throw_format_error("Cannot localize out-of-bounds time point."); + _STD _Throw_format_error("Cannot localize out-of-bounds time point."); } } } @@ -5854,7 +5862,7 @@ struct _Fill_tm_formatter { template auto format(const _Ty& _Val, _FormatContext& _FormatCtx) const { - return _Impl._Write(_FormatCtx, _Val, _Fill_tm(_Val)); + return _Impl._Write(_FormatCtx, _Val, _CHRONO _Fill_tm(_Val)); } private: @@ -6028,7 +6036,7 @@ struct formatter<_CHRONO sys_time<_Duration>, _CharT> { template auto format(const _CHRONO sys_time<_Duration>& _Val, _FormatContext& _FormatCtx) const { - return _Impl._Write(_FormatCtx, _Val, _Fill_tm(_Val)); + return _Impl._Write(_FormatCtx, _Val, _CHRONO _Fill_tm(_Val)); } private: @@ -6049,7 +6057,7 @@ struct formatter<_CHRONO utc_time<_Duration>, _CharT> { template auto format(const _CHRONO utc_time<_Duration>& _Val, _FormatContext& _FormatCtx) const { const auto _Sys = _CHRONO utc_clock::to_sys(_Val); - return _Impl._Write(_FormatCtx, _Val, _Fill_tm(_Sys)); + return _Impl._Write(_FormatCtx, _Val, _CHRONO _Fill_tm(_Sys)); } private: @@ -6073,7 +6081,7 @@ struct formatter<_CHRONO tai_time<_Duration>, _CharT> { using _Common = common_type_t<_Duration, days>; // slightly optimize by performing conversion at compile time constexpr _Common _Offset{sys_days{year{1970} / January / 1} - sys_days{year{1958} / January / 1}}; const auto _Sys = sys_time<_Duration>{_Val.time_since_epoch()} - _Offset; - return _Impl._Write(_FormatCtx, _Val, _Fill_tm(_Sys)); + return _Impl._Write(_FormatCtx, _Val, _CHRONO _Fill_tm(_Sys)); } private: @@ -6097,7 +6105,7 @@ struct formatter<_CHRONO gps_time<_Duration>, _CharT> { using _Common = common_type_t<_Duration, days>; // slightly optimize by performing conversion at compile time constexpr _Common _Offset{sys_days{year{1980} / January / Sunday[1]} - sys_days{year{1970} / January / 1}}; const auto _Sys = sys_time<_Duration>{_Val.time_since_epoch()} + _Offset; - return _Impl._Write(_FormatCtx, _Val, _Fill_tm(_Sys)); + return _Impl._Write(_FormatCtx, _Val, _CHRONO _Fill_tm(_Sys)); } private: @@ -6119,7 +6127,7 @@ struct formatter<_CHRONO file_time<_Duration>, _CharT> { auto format(const _CHRONO file_time<_Duration>& _Val, _FormatContext& _FormatCtx) const { const auto _Utc = _CHRONO file_clock::to_utc(_Val); const auto _Sys = _CHRONO utc_clock::to_sys(_Utc); - return _Impl._Write(_FormatCtx, _Utc, _Fill_tm(_Sys)); + return _Impl._Write(_FormatCtx, _Utc, _CHRONO _Fill_tm(_Sys)); } private: From a0168f1fe7992de6a7c54768f029ae61d841f7ee Mon Sep 17 00:00:00 2001 From: YexuanXiao Date: Sat, 9 Aug 2025 01:22:07 +0800 Subject: [PATCH 12/24] ``: Use `static_cast` to eliminate implicit conversions between different `charN_t` types (#5653) --- stl/inc/xlocale | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/stl/inc/xlocale b/stl/inc/xlocale index b6c77fd7faa..ab5eda706e6 100644 --- a/stl/inc/xlocale +++ b/stl/inc/xlocale @@ -1505,7 +1505,7 @@ protected: } const char8_t* _Peek = _First1; - char32_t _Code_point = _Lead_byte; + char32_t _Code_point = static_cast(_Lead_byte); do { const char8_t _By = *++_Peek; if ((_By & 0b1100'0000u) != 0b1000'0000u) { // out-of-sequence lead byte @@ -1551,7 +1551,7 @@ protected: return partial; } - char32_t _Code_point = *_First1; + char32_t _Code_point = static_cast(*_First1); if (_Code_point < 0x80u) { // encode 1-byte sequence *_First2 = static_cast(_Code_point); continue; @@ -1613,7 +1613,7 @@ protected: const auto _Old_first1 = _First1; for (; _First1 != _Last1 && _Count > 0u; ++_First1, --_Count) { - char32_t _Code_point = *_First1; + char32_t _Code_point = static_cast(*_First1); if (_Code_point < 0b1000'0000u) { // single-byte sequence continue; } @@ -1755,7 +1755,7 @@ protected: char8_t _Lead_byte = *_First1; if (_Lead_byte < 0b1000'0000u) { // single-byte sequence - *_First2 = _Lead_byte; + *_First2 = static_cast(_Lead_byte); continue; } @@ -1782,7 +1782,7 @@ protected: } const char8_t* _Peek = _First1; - char32_t _Code_point = _Lead_byte; + char32_t _Code_point = static_cast(_Lead_byte); do { const char8_t _By = *++_Peek; if ((_By & 0b1100'0000u) != 0b1000'0000u) { // out-of-sequence lead byte @@ -1868,7 +1868,7 @@ protected: const auto _Old_first1 = _First1; for (; _First1 != _Last1 && _Count > 0u; ++_First1, --_Count) { - char32_t _Code_point = *_First1; + char32_t _Code_point = static_cast(*_First1); if (_Code_point < 0b1000'0000u) { // single-byte sequence continue; } From eadd3be5964b6c5ba561c989d15d26fa304210f1 Mon Sep 17 00:00:00 2001 From: YexuanXiao Date: Sat, 9 Aug 2025 01:31:53 +0800 Subject: [PATCH 13/24] `Dev11_0000000_null_forward_iterators`: Avoid Clang warnings, guard headers for C++17/20 (#5654) Co-authored-by: Stephan T. Lavavej --- .../test.cpp | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/tests/std/tests/Dev11_0000000_null_forward_iterators/test.cpp b/tests/std/tests/Dev11_0000000_null_forward_iterators/test.cpp index 94008bd066a..b548e3d443d 100644 --- a/tests/std/tests/Dev11_0000000_null_forward_iterators/test.cpp +++ b/tests/std/tests/Dev11_0000000_null_forward_iterators/test.cpp @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#define _SILENCE_CXX23_ALIGNED_UNION_DEPRECATION_WARNING #define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING #define _SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING @@ -10,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -18,13 +16,21 @@ #include #include #include -#include #include #include #include #include #include +#if _HAS_CXX17 +#include +#include +#endif // _HAS_CXX17 + +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 + using namespace std; // N3797 24.2.5 [forward.iterators]/2: @@ -91,14 +97,14 @@ void test_iterator() { } { - aligned_union_t<0, FwdIt> au3; - aligned_union_t<0, FwdIt> au4; + alignas(FwdIt) unsigned char buf3[sizeof(FwdIt)]; + alignas(FwdIt) unsigned char buf4[sizeof(FwdIt)]; - FwdIt* p3 = reinterpret_cast(&au3); - FwdIt* p4 = reinterpret_cast(&au4); + memset(buf3, 0xCC, sizeof(FwdIt)); + memset(buf4, 0xDD, sizeof(FwdIt)); - memset(p3, 0xCC, sizeof(FwdIt)); - memset(p4, 0xDD, sizeof(FwdIt)); + FwdIt* p3 = reinterpret_cast(buf3); + FwdIt* p4 = reinterpret_cast(buf4); new (p3) FwdIt{}; new (p4) FwdIt{}; From f9b486eb87279a7b676448bdbe6ec34d58947526 Mon Sep 17 00:00:00 2001 From: YexuanXiao Date: Sat, 9 Aug 2025 01:32:30 +0800 Subject: [PATCH 14/24] `VSO_0000000_initialize_everything`: Avoid compiler warnings (#5655) Co-authored-by: Stephan T. Lavavej --- .../test.cpp | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/tests/std/tests/VSO_0000000_initialize_everything/test.cpp b/tests/std/tests/VSO_0000000_initialize_everything/test.cpp index b5ed1baef83..a41e347078a 100644 --- a/tests/std/tests/VSO_0000000_initialize_everything/test.cpp +++ b/tests/std/tests/VSO_0000000_initialize_everything/test.cpp @@ -53,30 +53,26 @@ inline bool operator!=(const stateful_allocator& lhs, const stateful_allocato return lhs.state != rhs.state; } -// warning C4582: 'garbage_data>::data': constructor is not implicitly called -// warning C4583: 'garbage_data>::data': destructor is not implicitly called -#pragma warning(push) -#pragma warning(disable : 4582 4583) template -struct garbage_data { - union { - T data; - }; - +class garbage_data { +private: + alignas(T) unsigned char buf[sizeof(T)]; bool constructed; + +public: garbage_data() : constructed(false) { - memset(&data, 0xCC, sizeof(data)); + memset(buf, 0xCC, sizeof(T)); } garbage_data(const garbage_data&) = delete; garbage_data& operator=(const garbage_data&) = delete; T& get() { - return data; + return *ptr(); } T* ptr() { - return &data; + return reinterpret_cast(buf); } T* operator->() { @@ -92,11 +88,10 @@ struct garbage_data { template void construct(Args&&... args) { assert(!constructed); - ::new (static_cast(&data)) T(forward(args)...); + ::new (static_cast(buf)) T(forward(args)...); constructed = true; } }; -#pragma warning(pop) template void assert_string_invariants(basic_string, Alloc>& target, const char* const expected) { From 525520719c51d5932338ee1b048ad0906e56136d Mon Sep 17 00:00:00 2001 From: YexuanXiao Date: Sat, 9 Aug 2025 01:33:30 +0800 Subject: [PATCH 15/24] `VSO_0000000_instantiate_iterators_misc`: Suppress deprecated volatile operation warnings (#5656) --- .../VSO_0000000_instantiate_iterators_misc/test.compile.pass.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/std/tests/VSO_0000000_instantiate_iterators_misc/test.compile.pass.cpp b/tests/std/tests/VSO_0000000_instantiate_iterators_misc/test.compile.pass.cpp index 23fb0ef0f53..8352292398b 100644 --- a/tests/std/tests/VSO_0000000_instantiate_iterators_misc/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0000000_instantiate_iterators_misc/test.compile.pass.cpp @@ -17,6 +17,7 @@ #define _SILENCE_CXX20_OLD_SHARED_PTR_ATOMIC_SUPPORT_DEPRECATION_WARNING #define _SILENCE_CXX20_REL_OPS_DEPRECATION_WARNING #define _SILENCE_CXX20_U8PATH_DEPRECATION_WARNING +#define _SILENCE_CXX20_VOLATILE_DEPRECATION_WARNING #define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING #define _SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING #define _SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING From 1d5f1aaeddd69088068fc35567a0205695a72b22 Mon Sep 17 00:00:00 2001 From: YexuanXiao Date: Sat, 9 Aug 2025 01:34:05 +0800 Subject: [PATCH 16/24] `VSO_0180466_algorithm_overhauls`: Suppress `-Wcharacter-conversion` for Clang 21 (#5657) Co-authored-by: Stephan T. Lavavej --- tests/std/tests/VSO_0180466_algorithm_overhauls/test.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/std/tests/VSO_0180466_algorithm_overhauls/test.cpp b/tests/std/tests/VSO_0180466_algorithm_overhauls/test.cpp index 63ecb94d0c3..4a8ce66f58d 100644 --- a/tests/std/tests/VSO_0180466_algorithm_overhauls/test.cpp +++ b/tests/std/tests/VSO_0180466_algorithm_overhauls/test.cpp @@ -6,6 +6,9 @@ #pragma warning(disable : 4389) // signed/unsigned mismatch in arithmetic #ifdef __clang__ +#if __clang_major__ >= 21 // TRANSITION, unconditionally silence this warning when Clang 21 is available +#pragma clang diagnostic ignored "-Wcharacter-conversion" +#endif // __clang_major__ >= 21 #pragma clang diagnostic ignored "-Wsign-compare" #endif // __clang__ From 93dee9e3c61a377ed8e6525ac9830e7329318729 Mon Sep 17 00:00:00 2001 From: "A. Jiang" Date: Sat, 9 Aug 2025 01:39:03 +0800 Subject: [PATCH 17/24] ``: Use designated aggregate initialization when returning `tm` (#5662) Co-authored-by: Stephan T. Lavavej --- stl/inc/chrono | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/stl/inc/chrono b/stl/inc/chrono index 9df7808829e..7bbc0ea063a 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -5122,16 +5122,17 @@ namespace chrono { _Seconds = static_cast(_Val.seconds().count()); } - tm _Time; - _Time.tm_sec = _Seconds; - _Time.tm_min = _Minutes; - _Time.tm_hour = _Hours; - _Time.tm_mday = static_cast(_Day); - _Time.tm_mon = static_cast(_Month) - 1; - _Time.tm_year = _Year - 1900; - _Time.tm_yday = _Yearday; - _Time.tm_wday = _Weekday; - return _Time; + return { + .tm_sec = _Seconds, + .tm_min = _Minutes, + .tm_hour = _Hours, + .tm_mday = static_cast(_Day), + .tm_mon = static_cast(_Month) - 1, + .tm_year = _Year - 1900, + .tm_wday = _Weekday, + .tm_yday = _Yearday, + .tm_isdst{}, + }; } } From 5f8b52546480a01d1d9be6c033e31dfce48d4f13 Mon Sep 17 00:00:00 2001 From: Gabriele Date: Fri, 8 Aug 2025 19:42:28 +0200 Subject: [PATCH 18/24] Update _MSVC_STL_UPDATE macro to August 2025 (#5664) --- stl/inc/yvals_core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 7de80182ab7..56f97244764 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -911,7 +911,7 @@ #define _CPPLIB_VER 650 #define _MSVC_STL_VERSION 145 -#define _MSVC_STL_UPDATE 202507L +#define _MSVC_STL_UPDATE 202508L #ifndef _ALLOW_COMPILER_AND_STL_VERSION_MISMATCH #if defined(__CUDACC__) && defined(__CUDACC_VER_MAJOR__) From f95868e66601c72541ea34a0d3c2fbcde31c597a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 16 Aug 2025 14:39:17 -0700 Subject: [PATCH 19/24] Toolset update: VS 2022 17.14.12 Preview 1 (#5674) --- CMakeLists.txt | 4 ++-- README.md | 4 ++-- azure-devops/config.yml | 2 +- azure-devops/provision-image.ps1 | 2 +- stl/inc/expected | 11 ++--------- tests/std/tests/P0323R12_expected/test.cpp | 2 +- 6 files changed, 9 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f6d1e4dda9..c5663853b04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,8 +6,8 @@ cmake_minimum_required(VERSION 3.31.0) set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) project(msvc_standard_libraries LANGUAGES CXX) -if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.44.35211") - message(FATAL_ERROR "The STL must be built with VS 2022 17.14.8 Preview 1 or later.") +if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.44.35214") + message(FATAL_ERROR "The STL must be built with VS 2022 17.14.12 Preview 1 or later.") endif() include(CheckCXXSourceCompiles) diff --git a/README.md b/README.md index e1172f370c0..ea5eb44ef28 100644 --- a/README.md +++ b/README.md @@ -141,7 +141,7 @@ Just try to follow these rules, so we can spend more time fixing bugs and implem # How To Build With The Visual Studio IDE -1. Install Visual Studio 2022 17.14.8 Preview 1 or later. +1. Install Visual Studio 2022 17.14.12 Preview 1 or later. * Select "Windows 11 SDK (10.0.26100.4188)" in the VS Installer. * Select "MSVC v143 - VS 2022 C++ ARM64/ARM64EC build tools (Latest)" in the VS Installer if you would like to build the ARM64/ARM64EC target. @@ -158,7 +158,7 @@ Just try to follow these rules, so we can spend more time fixing bugs and implem # How To Build With A Native Tools Command Prompt -1. Install Visual Studio 2022 17.14.8 Preview 1 or later. +1. Install Visual Studio 2022 17.14.12 Preview 1 or later. * Select "Windows 11 SDK (10.0.26100.4188)" in the VS Installer. * Select "MSVC v143 - VS 2022 C++ ARM64/ARM64EC build tools (Latest)" in the VS Installer if you would like to build the ARM64/ARM64EC target. diff --git a/azure-devops/config.yml b/azure-devops/config.yml index 7f1207a556b..d377651872b 100644 --- a/azure-devops/config.yml +++ b/azure-devops/config.yml @@ -5,7 +5,7 @@ variables: - name: poolName - value: 'StlBuild-2025-07-09T1240-Pool' + value: 'StlBuild-2025-08-12T1208-Pool' readonly: true - name: poolDemands value: 'EnableSpotVM -equals false' diff --git a/azure-devops/provision-image.ps1 b/azure-devops/provision-image.ps1 index de01c296ff3..a27553e32eb 100644 --- a/azure-devops/provision-image.ps1 +++ b/azure-devops/provision-image.ps1 @@ -42,7 +42,7 @@ foreach ($workload in $VisualStudioWorkloads) { $PowerShellUrl = 'https://github.com/PowerShell/PowerShell/releases/download/v7.5.2/PowerShell-7.5.2-win-x64.msi' $PowerShellArgs = @('/quiet', '/norestart') -$PythonUrl = 'https://www.python.org/ftp/python/3.13.5/python-3.13.5-amd64.exe' +$PythonUrl = 'https://www.python.org/ftp/python/3.13.6/python-3.13.6-amd64.exe' $PythonArgs = @('/quiet', 'InstallAllUsers=1', 'PrependPath=1', 'CompileAll=1', 'Include_doc=0') $CudaUrl = 'https://developer.download.nvidia.com/compute/cuda/12.4.0/local_installers/cuda_12.4.0_551.61_windows.exe' diff --git a/stl/inc/expected b/stl/inc/expected index 5f9422c7a5f..92f5590c529 100644 --- a/stl/inc/expected +++ b/stl/inc/expected @@ -308,18 +308,11 @@ public: requires (!is_same_v, in_place_t> && !is_same_v, expected> && !is_same_v, unexpect_t> && !_Is_specialization_v, unexpected> - && (!is_same_v, bool> -#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-10655311 - || !_Is_specialization_v, expected> -#else // ^^^ no workaround / workaround vvv - || !_Is_specialization_v, _STD expected> -#endif // ^^^ workaround ^^^ - ) + && (!is_same_v, bool> || !_Is_specialization_v, expected>) && is_constructible_v<_Ty, _Uty>) constexpr explicit(!is_convertible_v<_Uty, _Ty>) expected(_Uty&& _Other) noexcept(is_nothrow_constructible_v<_Ty, _Uty>) // strengthened - : _Value(_STD forward<_Uty>(_Other)), _Has_value(true) { - } + : _Value(_STD forward<_Uty>(_Other)), _Has_value(true) {} template requires is_constructible_v<_Err, const _UErr&> diff --git a/tests/std/tests/P0323R12_expected/test.cpp b/tests/std/tests/P0323R12_expected/test.cpp index f88662efda9..f9cdbdaa04f 100644 --- a/tests/std/tests/P0323R12_expected/test.cpp +++ b/tests/std/tests/P0323R12_expected/test.cpp @@ -2445,7 +2445,7 @@ void test_lwg_3886_volatile() { static_assert(copyable>); static_assert(copyable>); -// Test workaround for DevCom-10655311: Class derived from std::expected can't be constructed with bool value type +// Test DevCom-10655311: Class derived from std::expected can't be constructed with bool value type template class DerivedFromExpected : private expected { public: From 1449cee72c86873ef5b34c923f6f7bcc729b260e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20M=C3=BCller?= Date: Sat, 16 Aug 2025 23:41:52 +0200 Subject: [PATCH 20/24] ``: Remove non-standard `_Uelem` from matcher (#5671) Co-authored-by: Stephan T. Lavavej --- stl/inc/regex | 56 ++-- .../GH_000995_regex_custom_char_types/env.lst | 2 +- .../test.cpp | 285 ++++++++++++++---- .../GH_005244_regex_escape_sequences/test.cpp | 3 - .../test.cpp | 3 - 5 files changed, 265 insertions(+), 84 deletions(-) diff --git a/stl/inc/regex b/stl/inc/regex index 3753efd523d..3c337580e7b 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -327,7 +327,6 @@ inline size_t _Regex_transform_primary(_Out_writes_(_Last1 - _First1) _Post_read template class _Regex_traits : public _Regex_traits_base { // base class for regular expression traits public: - using _Uelem = make_unsigned_t<_Elem>; using char_type = _Elem; using size_type = size_t; using string_type = basic_string<_Elem>; @@ -638,11 +637,9 @@ inline bool _Is_word(char _Ch) { template bool _Is_word(_Elem _Ch) { - // assumes 'x' == L'x' for the ASCII range - using _UElem = make_unsigned_t<_Elem>; - - const auto _UCh = static_cast<_UElem>(_Ch); - return _UCh <= static_cast<_UElem>('z') && _Is_word(static_cast(_UCh)); + // assumes that ASCII characters are represented at ASCII code points + const auto _Uchar = static_cast(_Ch); + return static_cast<_Elem>(_Uchar) == _Ch && _Uchar <= 'z' && _Is_word(_Uchar); } _EXPORT_STD template @@ -3727,11 +3724,23 @@ _BidIt1 _Search_translate_left(_BidIt1 _Begin1, _BidIt1 _End1, _BidIt2 _Begin2, } } -template -bool _Lookup_range(unsigned int _Ch, const _Buf<_Elem>* _Bufptr) { // check whether _Ch is in _Buf - using _Uelem = make_unsigned_t<_Elem>; +template +bool _Lookup_range(const _Elem _Ch, const _Buf<_Elem>* const _Bufptr) { // check whether _Ch is in _Buf for (unsigned int _Ix = 0; _Ix < _Bufptr->_Size(); _Ix += 2) { // check current position - if (static_cast<_Uelem>(_Bufptr->_At(_Ix)) <= _Ch && _Ch <= static_cast<_Uelem>(_Bufptr->_At(_Ix + 1))) { + const _Elem _Left = _Bufptr->_At(_Ix); + const _Elem _Right = _Bufptr->_At(_Ix + 1); + + bool _Within_range; + if constexpr (is_same_v<_Char_traits, char_traits>) { + const auto _Uchar = static_cast(_Ch); + _Within_range = static_cast(_Left) <= _Uchar && _Uchar <= static_cast(_Right); + } else if constexpr (is_same_v<_Char_traits, char_traits>) { + _Within_range = _Left <= _Ch && _Ch <= _Right; + } else { + _Within_range = !_Char_traits::lt(_Ch, _Left) && !_Char_traits::lt(_Right, _Ch); + } + + if (_Within_range) { return true; } } @@ -3829,7 +3838,7 @@ _It _Matcher2<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Do_class(_Node_base* _Nx, } else if (_Sflags & regex_constants::collate) { _Ch = _Traits.translate(_Ch); } - const auto _UCh = static_cast(_Ch); + const auto _Uchar = static_cast(_Ch); _It _Res0 = _First; ++_Res0; @@ -3841,11 +3850,12 @@ _It _Matcher2<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Do_class(_Node_base* _Nx, _Res0 = _Resx; _Found = true; } else if (_Node->_Ranges - && (_Sflags & regex_constants::collate ? _STD _Lookup_collating_range(_Ch, _Node->_Ranges, _Traits) - : _STD _Lookup_range(_UCh, _Node->_Ranges))) { + && (_Sflags & regex_constants::collate + ? _STD _Lookup_collating_range(_Ch, _Node->_Ranges, _Traits) + : _STD _Lookup_range(_Ch, _Node->_Ranges))) { _Found = true; - } else if (_UCh < _Bmp_max) { - _Found = _Node->_Small && _Node->_Small->_Find(_UCh); + } else if (static_cast<_Elem>(_Uchar) == _Ch) { + _Found = _Node->_Small && _Node->_Small->_Find(_Uchar); } else if (_Node->_Large && _STD find(_Node->_Large->_Str(), _Node->_Large->_Str() + _Node->_Large->_Size(), _Ch) != _Node->_Large->_Str() + _Node->_Large->_Size()) { @@ -3913,15 +3923,15 @@ bool _Matcher2<_BidIt, _Elem, _RxTraits, _It, _Alloc>::_Is_wbound() const { if ((_Mflags & regex_constants::match_prev_avail) || _Tgt_state._Cur != _Begin) { // if --_Cur is valid, check for preceding word character if (_Tgt_state._Cur == _End) { - return (_Mflags & regex_constants::match_not_eow) == 0 && _Is_word(*_Prev_iter(_Tgt_state._Cur)); + return (_Mflags & regex_constants::match_not_eow) == 0 && _STD _Is_word(*_Prev_iter(_Tgt_state._Cur)); } else { - return _Is_word(*_Prev_iter(_Tgt_state._Cur)) != _Is_word(*_Tgt_state._Cur); + return _STD _Is_word(*_Prev_iter(_Tgt_state._Cur)) != _STD _Is_word(*_Tgt_state._Cur); } } else { // --_Cur is not valid if (_Tgt_state._Cur == _End) { return false; } else { - return (_Mflags & regex_constants::match_not_bow) == 0 && _Is_word(*_Tgt_state._Cur); + return (_Mflags & regex_constants::match_not_bow) == 0 && _STD _Is_word(*_Tgt_state._Cur); } } } @@ -3936,7 +3946,15 @@ typename _RxTraits::char_class_type _Matcher2<_BidIt, _Elem, _RxTraits, _It, _Al template bool _Is_ecmascript_line_terminator(_Elem _Ch) { - return _Ch == _Meta_nl || _Ch == _Meta_cr || _Ch == _Meta_ls || _Ch == _Meta_ps; + if constexpr (is_same_v<_Elem, char>) { + return _Ch == _Meta_nl || _Ch == _Meta_cr; + } else if constexpr (is_same_v<_Elem, wchar_t>) { + return _Ch == _Meta_nl || _Ch == _Meta_cr || _Ch == _Meta_ls || _Ch == _Meta_ps; + } else { + auto _UCh = static_cast(_Ch); + return static_cast<_Elem>(_UCh) == _Ch + && (_UCh == _Meta_nl || _UCh == _Meta_cr || _UCh == _Meta_ls || _UCh == _Meta_ps); + } } template diff --git a/tests/std/tests/GH_000995_regex_custom_char_types/env.lst b/tests/std/tests/GH_000995_regex_custom_char_types/env.lst index f141421b292..19f025bd0e6 100644 --- a/tests/std/tests/GH_000995_regex_custom_char_types/env.lst +++ b/tests/std/tests/GH_000995_regex_custom_char_types/env.lst @@ -1,4 +1,4 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -RUNALL_INCLUDE ..\impure_matrix.lst +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_000995_regex_custom_char_types/test.cpp b/tests/std/tests/GH_000995_regex_custom_char_types/test.cpp index c9409259f64..a057af8f9cb 100644 --- a/tests/std/tests/GH_000995_regex_custom_char_types/test.cpp +++ b/tests/std/tests/GH_000995_regex_custom_char_types/test.cpp @@ -1,24 +1,85 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// TRANSITION, GH-5563 -#pragma warning(push) -#pragma warning(disable : 6510) -#include -#pragma warning(pop) - #include +#include #include +#include #include +#include #include +#include +#include using namespace std; +enum class signed_wchar_enum : short {}; +enum class ullong_enum : unsigned long long {}; + +template +T convert_to(const signed_wchar_enum& char_enum) { + return static_cast(char_enum); +} + +template +T convert_to(const ullong_enum& char_enum) { + return static_cast(char_enum); +} + +template +class wrapped_character { +public: + wrapped_character() = default; + explicit wrapped_character(char ch) : character(static_cast(ch)) {} + explicit wrapped_character(unsigned char ch) : character(ch) {} + explicit wrapped_character(Elem w) : character(w) {} + template , int> = 0> + explicit wrapped_character(wchar_t w) : character(w) {} + explicit wrapped_character(int w) = delete; + explicit wrapped_character(unsigned int w) : character(static_cast(w)) {} + + operator unsigned char() const { + return static_cast(character); + } + + operator unsigned int() const { + return static_cast(character); + } + + operator char() const = delete; + operator wchar_t() const = delete; + operator int() const = delete; + operator unsigned long long() const = delete; + + friend bool operator==(const wrapped_character& lhs, const wrapped_character& rhs) { + return lhs.character == rhs.character; + } + + template + friend T convert_to(const wrapped_character& wrapped_char) { + return static_cast(wrapped_char.character); + } + +private: + Elem character; +}; + +template +T convert_to(const wrapped_character& wrapped_char); + +template +bool operator!=(const wrapped_character& lhs, const wrapped_character& rhs) { + return !(lhs == rhs); +} + +using wrapped_wchar = wrapped_character; +using wrapped_ullong = wrapped_character; + template basic_string convert_to_underlying_string(FwdIt first, FwdIt last) { basic_string str; for (; first != last; ++first) { - str.push_back(static_cast(*first)); + str.push_back(convert_to(*first)); } return str; } @@ -54,7 +115,7 @@ class test_regex_traits { } SourceChar translate_nocase(const SourceChar c) const { - return static_cast(inner.translate_nocase(static_cast(c))); + return static_cast(inner.translate_nocase(convert_to(c))); } template @@ -65,8 +126,10 @@ class test_regex_traits { template string_type transform_primary(FwdIt first, FwdIt last) const { - auto str = convert_to_underlying_string(first, last); - return convert_from_underlying_string(inner.transform_primary(str.begin(), str.end())); + if (distance(first, last) == 1) { + return string_type{first, last}; + } + return string_type{}; } template @@ -82,11 +145,11 @@ class test_regex_traits { } bool isctype(SourceChar c, char_class_type f) const { - return inner.isctype(static_cast(c), f); + return inner.isctype(convert_to(c), f); } int value(SourceChar ch, int radix) const { - return inner.value(static_cast(ch), radix); + return inner.value(convert_to(ch), radix); } locale_type imbue(locale_type l) { @@ -101,52 +164,13 @@ class test_regex_traits { rx_traits inner; }; -enum class signed_wchar_enum : short {}; - -class wrapped_wchar { -public: - wrapped_wchar() = default; - explicit wrapped_wchar(char ch) : character(static_cast(ch)) {} - explicit wrapped_wchar(unsigned char ch) : character(ch) {} - explicit wrapped_wchar(wchar_t w) : character(w) {} - explicit wrapped_wchar(int w) = delete; - explicit wrapped_wchar(unsigned int w) : character(static_cast(w)) {} - - operator unsigned char() const { - return static_cast(character); - } - - operator unsigned int() const { - return static_cast(character); - } - - // to support test_regex_traits - operator wchar_t() const { - return character; - } - - operator char() const = delete; - operator int() const = delete; - - friend bool operator==(const wrapped_wchar& lhs, const wrapped_wchar& rhs) { - return lhs.character == rhs.character; - } - -private: - wchar_t character; -}; - -bool operator!=(const wrapped_wchar& lhs, const wrapped_wchar& rhs) { - return !(lhs == rhs); -} - -template +template struct custom_char_traits { using char_type = Elem; - using int_type = int; + using int_type = conditional_t; using pos_type = streampos; using off_type = streamoff; - using state_type = char_traits::state_type; + using state_type = mbstate_t; static Elem* copy(Elem* const first1, const Elem* const first2, const size_t count) noexcept { copy_n(first2, count, first1); @@ -213,7 +237,7 @@ struct custom_char_traits { } static bool lt(const Elem left, const Elem right) noexcept { - return static_cast(left) < static_cast(right); + return convert_to(left) < convert_to(right); } static Elem to_char_type(const int_type meta) noexcept { @@ -238,13 +262,20 @@ struct custom_char_traits { }; template <> -struct char_traits : custom_char_traits {}; +struct char_traits : custom_char_traits {}; + +template <> +struct char_traits : custom_char_traits {}; + +template <> +struct char_traits : custom_char_traits {}; template <> -struct char_traits : custom_char_traits {}; +struct char_traits : custom_char_traits {}; + void test_gh_5592() { - // GH-5592: Remove _Uelem from the parser + // GH-5592: Remove non-standard _Uelem from parser // This test checks that the parser compiles and doesn't crash // when user-defined character types are used. @@ -263,7 +294,145 @@ void test_gh_5592() { } } +template +void test_regex_for_custom_char(const wstring& pattern, const wstring& input, bool should_match) { + auto converted_pattern = convert_from_underlying_string>(pattern); + auto converted_input = convert_from_underlying_string>(input); + basic_regex re{converted_pattern}; + assert(regex_match(converted_input, re) == should_match); +} + +void test_regex_on_custom_wchars(const wstring& pattern, const wstring& input, bool should_match) { + test_regex_for_custom_char>( + pattern, input, should_match); + test_regex_for_custom_char>(pattern, input, should_match); +} + +template +void test_regex_for_custom_char(const UnderlyingChar* pat_first, const UnderlyingChar* pat_last, + const UnderlyingChar* input_first, const UnderlyingChar* input_last, bool should_match) { + basic_string converted_pattern; + basic_string converted_input; + transform(pat_first, pat_last, back_inserter(converted_pattern), + [](UnderlyingChar val) { return static_cast(val); }); + transform(input_first, input_last, back_inserter(converted_input), + [](UnderlyingChar val) { return static_cast(val); }); + basic_regex re{converted_pattern}; + assert(regex_match(converted_input, re) == should_match); +} + +void test_regex_on_custom_ullongs(const unsigned long long* pat_first, const unsigned long long* pat_last, + const unsigned long long* input_first, const unsigned long long* input_last, bool should_match) { + test_regex_for_custom_char>( + pat_first, pat_last, input_first, input_last, should_match); + test_regex_for_custom_char>( + pat_first, pat_last, input_first, input_last, should_match); +} + +void test_gh_5671_single_character_patterns() { + // test patterns matching single characters + // simple smoke test + test_regex_on_custom_wchars(L"a", L"a", true); + test_regex_on_custom_wchars(L"a", L"b", false); + + // no truncation issues at 0x100 + test_regex_on_custom_wchars(L"\u0100", L"\u0100", true); // U+0100 LATIN CAPITAL LETTER A WITH MACRON + test_regex_on_custom_wchars(wstring(1, L'\0'), L"\u0100", false); // U+0100 LATIN CAPITAL LETTER A WITH MACRON + test_regex_on_custom_wchars(L"\u0100", wstring(1, L'\0'), false); // U+0100 LATIN CAPITAL LETTER A WITH MACRON + + // no issues with signed character values + test_regex_on_custom_wchars( + L"\uf000", L"\uf000", true); // U+F000 in private use area (signed for signed_wchar_enum) + + // matching for very large values + { + const unsigned long long pattern = 0xabababababULL; + + test_regex_on_custom_ullongs(&pattern, &pattern + 1, &pattern, &pattern + 1, true); + + for (const unsigned long long unmatched : {0xabULL, 0xababULL, 0xababababULL, 0xababababaaULL, 0xababababacULL, + 0x9bababababULL, 0xbbababababULL, 0xffffffababababULL}) { + test_regex_on_custom_ullongs(&pattern, &pattern + 1, &unmatched, &unmatched + 1, false); + } + } +} + +void test_gh_5671_line_terminators() { + // test line terminator handling for dot + test_regex_on_custom_wchars(L".", L"a", true); + test_regex_on_custom_wchars(L".", L"\r", false); + test_regex_on_custom_wchars(L".", L"\n", false); + test_regex_on_custom_wchars(L".", L"\u2028", false); // U+2028 LINE SEPARATOR + test_regex_on_custom_wchars(L".", L"\u2029", false); // U+2029 PARAGRAPH SEPARATOR + test_regex_on_custom_wchars(L".", L"\u2c60", true); // U+2C60 LATIN CAPITAL LETTER L WITH DOUBLE BAR + + { + const unsigned long long dot = L'.'; + + for (const unsigned long long line_terminator : {L'\r', L'\n', L'\u2028', L'\u2029'}) { + test_regex_on_custom_ullongs(&dot, &dot + 1, &line_terminator, &line_terminator + 1, false); + + const unsigned long long shifted_line_terminator = 0x100000000ULL + line_terminator; + test_regex_on_custom_ullongs(&dot, &dot + 1, &shifted_line_terminator, &shifted_line_terminator + 1, true); + } + } +} + +void test_gh_5671_word_boundaries() { + // test word boundaries + test_regex_on_custom_wchars(LR"(a\b.)", L"a\u0141", true); // U+0141 LATIN CAPITAL LETTER L WITH STROKE + test_regex_on_custom_wchars(LR"(a\B.)", L"a\u0141", false); // U+0141 LATIN CAPITAL LETTER L WITH STROKE +} + +void test_gh_5671_character_ranges() { + // test simple ranges + test_regex_on_custom_wchars(LR"([b-\u0141])", L"a", false); // U+0141 LATIN CAPITAL LETTER L WITH STROKE + test_regex_on_custom_wchars(LR"([b-\u0141])", L"b", true); + test_regex_on_custom_wchars(LR"([b-\u0141])", L"\u0141", true); + test_regex_on_custom_wchars(LR"([b-\u0141])", L"\u0142", false); // U+0142 LATIN SMALL LETTER L WITH STROKE + + // test that GH-5437 fix for small ranges near U+0100 remains in place for custom types + test_regex_on_custom_wchars(LR"([\u00ff-\u0100])", L"\u00FE", false); // U+00FE LATIN SMALL LETTER THORN + test_regex_on_custom_wchars(LR"([\u00ff-\u0100])", L"\u00FF", true); // U+00FF LATIN SMALL LETTER Y WITH DIAERESIS + test_regex_on_custom_wchars(LR"([\u00ff-\u0100])", L"\u0100", true); // U+0100 LATIN CAPITAL LETTER A WITH MACRON + test_regex_on_custom_wchars(LR"([\u00ff-\u0100])", L"\u0101", false); // U+0101 LATIN SMALL LETTER A WITH MACRON + + // test ranges with negative upper boundary when signed + test_regex_on_custom_wchars( + LR"([\u7fed-\u8123])", L"\u7fed", true); // U+7FED CJK UNIFIED IDEOGRAPH-7FED, U+8123 CJK UNIFIED IDEOGRAPH-8123 + test_regex_on_custom_wchars(LR"([\u7fed-\u8123])", L"\u8123", true); + test_regex_on_custom_wchars(LR"([\u7fed-\u8123])", L"\u8001", true); // U+8001 CJK UNIFIED IDEOGRAPH-8001 + test_regex_on_custom_wchars(LR"([\u7fed-\u8123])", L"\u7fec", false); // U+7FEC CJK UNIFIED IDEOGRAPH-7FEC + test_regex_on_custom_wchars(LR"([\u7fed-\u8123])", L"\u8124", false); // U+8124 CJK UNIFIED IDEOGRAPH-8124 + + // test ranges with boundaries exceeding UINT_MAX for small and large ranges + for (unsigned long long upper_bound : {0x100000006ULL, 0x100000036ULL}) { + const unsigned long long pattern[] = {L'[', 0x100000004ULL, L'-', upper_bound, L']'}; + for (unsigned long long matched = 0x100000004ULL; matched <= upper_bound; ++matched) { + test_regex_on_custom_ullongs(begin(pattern), end(pattern), &matched, &matched + 1, true); + } + + for (const unsigned long long unmatched : + {0x00000004ULL, upper_bound & 0xffffffffULL, 0x100000003ULL, upper_bound + 1ULL}) { + test_regex_on_custom_ullongs(begin(pattern), end(pattern), &unmatched, &unmatched + 1, false); + } + } +} + +void test_gh_5671() { + // GH-5671: Remove non-standard _Uelem from matcher + // + // The following tests check that internal narrowing conversions to integers in parser and matcher + // as well as signedness of the character type are handled appropriately + // even when using (weird) custom character types. + test_gh_5671_single_character_patterns(); + test_gh_5671_line_terminators(); + test_gh_5671_word_boundaries(); + test_gh_5671_character_ranges(); +} + int main() { test_gh_5592(); + test_gh_5671(); return 0; } diff --git a/tests/std/tests/GH_005244_regex_escape_sequences/test.cpp b/tests/std/tests/GH_005244_regex_escape_sequences/test.cpp index 7e66ca640a5..ba4b87d6d5f 100644 --- a/tests/std/tests/GH_005244_regex_escape_sequences/test.cpp +++ b/tests/std/tests/GH_005244_regex_escape_sequences/test.cpp @@ -24,9 +24,6 @@ class test_regex_traits { using locale_type = typename rx_traits::locale_type; using char_class_type = typename rx_traits::char_class_type; - // TRANSITION, GH-995 - using _Uelem = typename rx_traits::_Uelem; - test_regex_traits() = default; static size_t length(const charT* p) { diff --git a/tests/std/tests/GH_005553_regex_character_translation/test.cpp b/tests/std/tests/GH_005553_regex_character_translation/test.cpp index 33c5521ad49..5eadb05693e 100644 --- a/tests/std/tests/GH_005553_regex_character_translation/test.cpp +++ b/tests/std/tests/GH_005553_regex_character_translation/test.cpp @@ -26,9 +26,6 @@ class nonidempotent_translate_regex_traits : private regex_traits { using char_class_type = typename rx_traits::char_class_type; using uchar_type = make_unsigned_t; - // TRANSITION, GH-995 - using _Uelem = typename rx_traits::_Uelem; - nonidempotent_translate_regex_traits() = default; using rx_traits::length; From 8aa8f972bcf106efb9fdf5e27324712747e347e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julian=20M=C3=BCller?= Date: Sat, 16 Aug 2025 23:45:14 +0200 Subject: [PATCH 21/24] ``: Speed up skip optimization for default `regex_traits` in `collate` mode (#5672) Co-authored-by: Stephan T. Lavavej --- benchmarks/src/regex_search.cpp | 6 +++-- stl/inc/regex | 6 +++++ .../std/tests/VSO_0000000_regex_use/test.cpp | 27 +++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/benchmarks/src/regex_search.cpp b/benchmarks/src/regex_search.cpp index bc6a3d12537..19018fc80f2 100644 --- a/benchmarks/src/regex_search.cpp +++ b/benchmarks/src/regex_search.cpp @@ -8,13 +8,14 @@ #include "lorem.hpp" using namespace std; +using namespace regex_constants; -void bm_lorem_search(benchmark::State& state, const char* pattern) { +void bm_lorem_search(benchmark::State& state, const char* pattern, syntax_option_type syntax = ECMAScript) { string repeated_lorem{lorem_ipsum}; for (long long i = 0; i < state.range(); ++i) { repeated_lorem += repeated_lorem; } - regex re{pattern}; + regex re{pattern, syntax}; for (auto _ : state) { benchmark::DoNotOptimize(repeated_lorem); @@ -33,6 +34,7 @@ void bm_lorem_search(benchmark::State& state, const char* pattern) { BENCHMARK_CAPTURE(bm_lorem_search, "^bibe", "^bibe")->Arg(2)->Arg(3)->Arg(4); BENCHMARK_CAPTURE(bm_lorem_search, "bibe", "bibe")->Arg(2)->Arg(3)->Arg(4); +BENCHMARK_CAPTURE(bm_lorem_search, "bibe".collate, "bibe", regex_constants::collate)->Arg(2)->Arg(3)->Arg(4); BENCHMARK_CAPTURE(bm_lorem_search, "(bibe)", "(bibe)")->Arg(2)->Arg(3)->Arg(4); BENCHMARK_CAPTURE(bm_lorem_search, "(bibe)+", "(bibe)+")->Arg(2)->Arg(3)->Arg(4); BENCHMARK_CAPTURE(bm_lorem_search, "(?:bibe)+", "(?:bibe)+")->Arg(2)->Arg(3)->Arg(4); diff --git a/stl/inc/regex b/stl/inc/regex index 3c337580e7b..499d1124be8 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -3690,6 +3690,8 @@ _BidIt1 _Compare_translate_both(_BidIt1 _Begin1, _BidIt1 _End1, _BidIt2 _Begin2, // compare character ranges, translating characters in both ranges according to syntax options if (_Sflags & regex_constants::icase) { return _STD _Cmp_chrange(_Begin1, _End1, _Begin2, _End2, _Cmp_icase<_RxTraits>{_Traits}); + } else if constexpr (_Is_any_of_v<_RxTraits, regex_traits, regex_traits>) { + return _STD _Cmp_chrange(_Begin1, _End1, _Begin2, _End2, equal_to{}); } else if (_Sflags & regex_constants::collate) { return _STD _Cmp_chrange(_Begin1, _End1, _Begin2, _End2, _Cmp_collate<_RxTraits>{_Traits}); } else { @@ -3703,6 +3705,8 @@ _BidIt1 _Compare_translate_left(_BidIt1 _Begin1, _BidIt1 _End1, _BidIt2 _Begin2, // compare character ranges, translating characters in the left range according to syntax options if (_Sflags & regex_constants::icase) { return _STD _Cmp_chrange(_Begin1, _End1, _Begin2, _End2, _Cmp_icase_translateleft<_RxTraits>{_Traits}); + } else if constexpr (_Is_any_of_v<_RxTraits, regex_traits, regex_traits>) { + return _STD _Cmp_chrange(_Begin1, _End1, _Begin2, _End2, equal_to{}); } else if (_Sflags & regex_constants::collate) { return _STD _Cmp_chrange(_Begin1, _End1, _Begin2, _End2, _Cmp_collate_translateleft<_RxTraits>{_Traits}); } else { @@ -3717,6 +3721,8 @@ _BidIt1 _Search_translate_left(_BidIt1 _Begin1, _BidIt1 _End1, _BidIt2 _Begin2, // after translating characters in the left sequence according to syntax options if (_Sflags & regex_constants::icase) { return _STD search(_Begin1, _End1, _Begin2, _End2, _Cmp_icase_translateleft<_RxTraits>{_Traits}); + } else if constexpr (_Is_any_of_v<_RxTraits, regex_traits, regex_traits>) { + return _STD search(_Begin1, _End1, _Begin2, _End2, equal_to{}); } else if (_Sflags & regex_constants::collate) { return _STD search(_Begin1, _End1, _Begin2, _End2, _Cmp_collate_translateleft<_RxTraits>{_Traits}); } else { diff --git a/tests/std/tests/VSO_0000000_regex_use/test.cpp b/tests/std/tests/VSO_0000000_regex_use/test.cpp index a1e41b84421..ea6e246adb6 100644 --- a/tests/std/tests/VSO_0000000_regex_use/test.cpp +++ b/tests/std/tests/VSO_0000000_regex_use/test.cpp @@ -2103,6 +2103,32 @@ void test_gh_5576() { match_default, "AbGweEXfFlXlLLlffflEXlF"); } +void test_gh_5672() { + // GH-5672: Speed up skip optimization for default `regex_traits` in `collate` mode + // The PR added a faster branch in the skip optimization when matching in collate mode + // for default `regex_traits` and `regex_traits`. + // The following tests check that searching still works correctly when the faster branch is engaged. + { + test_regex collating_re(&g_regexTester, "g", regex_constants::collate); + + collating_re.should_search_match("abcdefghijklmnopqrstuvwxyz", "g"); + collating_re.should_search_fail("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + collating_re.should_search_match("zyxwvutsrqponmlkjihgfedcba", "g"); + collating_re.should_search_fail("ZYXWVUTSRQPONMLKJIHGFEDCBA"); + collating_re.should_search_fail("zyxwvutsrqponmlkjihedcba"); + } + + { + test_wregex collating_re(&g_regexTester, L"g", regex_constants::collate); + + collating_re.should_search_match(L"abcdefghijklmnopqrstuvwxyz", L"g"); + collating_re.should_search_fail(L"ABCDEFGHIJKLMNOPQRSTUVWXYZ"); + collating_re.should_search_match(L"zyxwvutsrqponmlkjihgfedcba", L"g"); + collating_re.should_search_fail(L"ZYXWVUTSRQPONMLKJIHGFEDCBA"); + collating_re.should_search_fail(L"zyxwvutsrqponmlkjihedcba"); + } +} + int main() { test_dev10_449367_case_insensitivity_should_work(); test_dev11_462743_regex_collate_should_not_disable_regex_icase(); @@ -2153,6 +2179,7 @@ int main() { test_gh_5490(); test_gh_5509(); test_gh_5576(); + test_gh_5672(); return g_regexTester.result(); } From 67ff5c3ab7c742b2b3fcaed3210e8b410c881016 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 16 Aug 2025 15:04:33 -0700 Subject: [PATCH 22/24] P0811R3_midpoint_lerp: Suppress warning C4756 (#5676) --- tests/std/tests/P0811R3_midpoint_lerp/test.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/std/tests/P0811R3_midpoint_lerp/test.cpp b/tests/std/tests/P0811R3_midpoint_lerp/test.cpp index 130a41576fd..7534f719859 100644 --- a/tests/std/tests/P0811R3_midpoint_lerp/test.cpp +++ b/tests/std/tests/P0811R3_midpoint_lerp/test.cpp @@ -1031,6 +1031,8 @@ bool test_lerp() { return true; } +#pragma warning(push) +#pragma warning(disable : 4756) // ignore constant arithmetic overflow warning void test_gh_1917() { // GH-1917 : lerp(1e+308, 5e+307, 4.0) spuriously overflows using bit_type = unsigned long long; @@ -1110,6 +1112,7 @@ void test_gh_1917() { } #endif // _M_FP_STRICT } +#pragma warning(pop) constexpr bool test_gh_2112() { // GH-2112 : std::lerp is missing Arithmetic overloads From d7df896aec2051ce9ce5bf831fdcd2c8cf6e98d5 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 16 Aug 2025 15:06:41 -0700 Subject: [PATCH 23/24] ``: Avoid integer overflow in `stable_sort()` and `ranges::stable_sort` for huge inputs on x86 (#5677) --- stl/inc/algorithm | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index e8afb54e353..10203201835 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -395,6 +395,7 @@ _STD_BEGIN static_assert(_Is_ranges_bidi_iter_v<_Iter>, "This algorithm requires bidirectional iterators or stronger.") _INLINE_VAR constexpr int _ISORT_MAX = 32; // maximum size for insertion sort +// If _ISORT_MAX is ever changed from 32 == 2^5, re-analyze the implementation of stable_sort for integer overflow. template constexpr _Iter_diff_t<_It> _Isort_max{_ISORT_MAX}; @@ -2274,8 +2275,8 @@ namespace ranges { _First = _STD move(_Mid); ++_First; - using _Uty = _Make_unsigned_like_t>; - if (static_cast<_Uty>(_Skip) <= (static_cast<_Uty>(-1) >> 1)) { + constexpr auto _Half_max = (numeric_limits>::max)() / 2; + if (_Skip <= _Half_max) { _Skip <<= 1; } } @@ -9220,13 +9221,24 @@ void _Buffered_merge_sort_unchecked(const _BidIt _First, const _BidIt _Last, con auto _Chunk = _Isort_max<_BidIt>; for (;;) { // unconditionally merge elements back into the source buffer + + // _Chunk starts at 2^5 and is doubled twice in this loop. + // The first doubling (to 2^6, 2^8, 2^10, ...) doesn't check for overflow as it doesn't pose a risk. + // The second doubling (to 2^7, 2^9, 2^11, ..., 2^15, ..., 2^31, ...) defends against overflow below. _Chunk <<= 1; _STD _Chunked_merge_unchecked(_Temp_ptr, _Temp_ptr + _Count, _First, static_cast(_Chunk), static_cast(_Count), _Pred); - _Chunk <<= 1; - if (_Count <= _Chunk) { // if the input would be a single chunk, it's already sorted and we're done - return; + + // This is equivalent to doubling _Chunk followed by returning when `_Count <= _Chunk`, + // except that it doesn't risk overflowing. + // Note that returning when `_Count / 2 <= _Chunk` before doubling _Chunk + // would behave differently for odd _Count due to truncating integer division. + // Returning when `(_Count - 1) / 2 < _Chunk` correctly handles both even and odd _Count. + // We have an early return for small _Count above, so `_Count - 1` is safe to form. + if ((_Count - 1) / 2 < _Chunk) { + return; // if the input would be a single chunk, it's already sorted and we're done } + _Chunk <<= 1; // more merges necessary; merge to temporary buffer _STD _Chunked_merge_unchecked(_First, _Last, _Temp_ptr, _Chunk, _Count, _Pred); @@ -9392,12 +9404,23 @@ namespace ranges { ptrdiff_t _Chunk_size = _ISORT_MAX; for (;;) { // unconditionally merge elements back into the source buffer + + // _Chunk_size starts at 2^5 and is doubled twice in this loop. + // The first doubling (to 2^6, 2^8, 2^10, ...) doesn't check for overflow as it doesn't pose a risk. + // The second doubling (to 2^7, 2^9, 2^11, ..., 2^15, ..., 2^31, ...) defends against overflow below. _Chunk_size <<= 1; _Chunked_merge_common(_Temp_ptr, _Temp_ptr + _Count, _First, _Chunk_size, _Count, _Pred, _Proj); - _Chunk_size <<= 1; - if (_Count <= _Chunk_size) { // if the input would be a single chunk, it's already sorted and we're done - return; + + // This is equivalent to doubling _Chunk_size followed by returning when `_Count <= _Chunk_size`, + // except that it doesn't risk overflowing. + // Note that returning when `_Count / 2 <= _Chunk_size` before doubling _Chunk_size + // would behave differently for odd _Count due to truncating integer division. + // Returning when `(_Count - 1) / 2 < _Chunk_size` correctly handles both even and odd _Count. + // We have an early return for small _Count above, so `_Count - 1` is safe to form. + if ((_Count - 1) / 2 < _Chunk_size) { + return; // if the input would be a single chunk, it's already sorted and we're done } + _Chunk_size <<= 1; // more merges necessary; merge to temporary buffer _Chunked_merge_common(_First, _Last, _Temp_ptr, _Chunk_size, _Count, _Pred, _Proj); From a2686a066284a6475502730bdba87329bc7e167e Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Sat, 16 Aug 2025 15:43:27 -0700 Subject: [PATCH 24/24] ``: Compare against `char` directly (#5675) --- stl/inc/regex | 54 ++++++------- .../test.cpp | 76 +++++++++++++++++-- 2 files changed, 92 insertions(+), 38 deletions(-) diff --git a/stl/inc/regex b/stl/inc/regex index 499d1124be8..817a8db8709 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -4367,11 +4367,9 @@ template bool _Parser2<_FwdIt, _Elem, _RxTraits>::_Is_esc(_FwdIt _Ch0) const { // assumes _Ch0 != _End return _Mode == _Lex_mode::_Default && ++_Ch0 != _End && ((!(_L_flags & _L_nex_grp) - && (*_Ch0 == static_cast<_Elem>(static_cast(_Meta_lpar)) - || *_Ch0 == static_cast<_Elem>(static_cast(_Meta_rpar)))) + && (*_Ch0 == static_cast(_Meta_lpar) || *_Ch0 == static_cast(_Meta_rpar))) || (!(_L_flags & _L_nex_rep) - && (*_Ch0 == static_cast<_Elem>(static_cast(_Meta_lbr)) - || *_Ch0 == static_cast<_Elem>(static_cast(_Meta_rbr))))); + && (*_Ch0 == static_cast(_Meta_lbr) || *_Ch0 == static_cast(_Meta_rbr)))); } template @@ -4442,7 +4440,7 @@ void _Parser2<_FwdIt, _Elem, _RxTraits>::_Trans() { // map character to meta-cha { // check if $ is special _FwdIt _Next = _Pat; if ((_L_flags & _L_anch_rstr) && ++_Next != _End) { - const bool _Escaped = *_Next == static_cast<_Elem>(static_cast(_Meta_esc)) && _Is_esc(_Next); + const bool _Escaped = *_Next == static_cast(_Meta_esc) && _Is_esc(_Next); if (_Escaped) { ++_Next; } @@ -4454,9 +4452,9 @@ void _Parser2<_FwdIt, _Elem, _RxTraits>::_Trans() { // map character to meta-cha const _Elem _Ch = *_Next; const bool _Is_end_of_alternative = - ((_L_flags & _L_alt_nl) && _Ch == static_cast<_Elem>(static_cast(_Meta_nl)) + ((_L_flags & _L_alt_nl) && _Ch == static_cast(_Meta_nl) && _Disj_count == 0) // dollar followed by newline '\n' for alternation - || (_Escaped && _Ch == static_cast<_Elem>(static_cast(_Meta_rpar)) + || (_Escaped && _Ch == static_cast(_Meta_rpar) && _Disj_count != 0); // dollar followed by (escaped) right parenthesis ')' closing a group if (!_Is_end_of_alternative) { @@ -4490,7 +4488,7 @@ void _Parser2<_FwdIt, _Elem, _RxTraits>::_Trans() { // map character to meta-cha template void _Parser2<_FwdIt, _Elem, _RxTraits>::_Next() { // advance to next input character if (_Pat != _End) { // advance - if (*_Pat == static_cast<_Elem>(static_cast(_Meta_esc)) && _Is_esc(_Pat)) { + if (*_Pat == static_cast(_Meta_esc) && _Is_esc(_Pat)) { ++_Pat; } @@ -4621,10 +4619,10 @@ bool _Parser2<_FwdIt, _Elem, _RxTraits>::_CharacterClassEscape(bool _Addit) { // return false; } - auto _Kind = (_Char == static_cast<_Elem>('W') ? _Rx_char_class_kind::_Negated_w - : _Char == static_cast<_Elem>('S') ? _Rx_char_class_kind::_Negated_s - : _Char == static_cast<_Elem>('D') ? _Rx_char_class_kind::_Negated_d - : _Rx_char_class_kind::_Positive); + auto _Kind = (_Char == 'W' ? _Rx_char_class_kind::_Negated_w + : _Char == 'S' ? _Rx_char_class_kind::_Negated_s + : _Char == 'D' ? _Rx_char_class_kind::_Negated_d + : _Rx_char_class_kind::_Positive); if (_Addit) { _Nfa._Add_class(); @@ -4644,8 +4642,7 @@ bool _Parser2<_FwdIt, _Elem, _RxTraits>::_CharacterClassEscape(bool _Addit) { // template _Prs_ret _Parser2<_FwdIt, _Elem, _RxTraits>::_ClassEscape() { // check for class escape - if ((_L_flags & _L_esc_bsp) - && _Char == static_cast<_Elem>(static_cast(_Esc_ctrl_b))) { // handle backspace escape + if ((_L_flags & _L_esc_bsp) && _Char == static_cast(_Esc_ctrl_b)) { // handle backspace escape _Next(); _Unescaped_char = static_cast<_Elem>(static_cast(_Meta_bsp)); return _Prs_chr; @@ -4894,15 +4891,15 @@ bool _Parser2<_FwdIt, _Elem, _RxTraits>::_IdentityEscape(bool _In_character_clas template bool _Parser2<_FwdIt, _Elem, _RxTraits>::_Do_ffn(_Elem _Ch) { // check for limited file format escape characters - if (_Ch == static_cast<_Elem>(static_cast(_Esc_ctrl_f))) { + if (_Ch == static_cast(_Esc_ctrl_f)) { _Val = '\f'; - } else if (_Ch == static_cast<_Elem>(static_cast(_Esc_ctrl_n))) { + } else if (_Ch == static_cast(_Esc_ctrl_n)) { _Val = '\n'; - } else if (_Ch == static_cast<_Elem>(static_cast(_Esc_ctrl_r))) { + } else if (_Ch == static_cast(_Esc_ctrl_r)) { _Val = '\r'; - } else if (_Ch == static_cast<_Elem>(static_cast(_Esc_ctrl_t))) { + } else if (_Ch == static_cast(_Esc_ctrl_t)) { _Val = '\t'; - } else if (_Ch == static_cast<_Elem>(static_cast(_Esc_ctrl_v))) { + } else if (_Ch == static_cast(_Esc_ctrl_v)) { _Val = '\v'; } else { return false; @@ -4913,9 +4910,9 @@ bool _Parser2<_FwdIt, _Elem, _RxTraits>::_Do_ffn(_Elem _Ch) { // check for limit template bool _Parser2<_FwdIt, _Elem, _RxTraits>::_Do_ffnx(_Elem _Ch) { // check for the remaining file format escape characters - if (_Ch == static_cast<_Elem>(static_cast(_Esc_ctrl_a))) { + if (_Ch == static_cast(_Esc_ctrl_a)) { _Val = '\a'; - } else if (_Ch == static_cast<_Elem>(static_cast(_Esc_ctrl_b))) { + } else if (_Ch == static_cast(_Esc_ctrl_b)) { _Val = '\b'; } else { return false; @@ -4933,8 +4930,7 @@ bool _Parser2<_FwdIt, _Elem, _RxTraits>::_CharacterEscape(bool _In_character_cla if ((_L_flags & _L_esc_ffn && _Do_ffn(_Char)) || (_L_flags & _L_esc_ffnx && _Do_ffnx(_Char))) { _Next(); - } else if (_Char == static_cast<_Elem>(static_cast(_Esc_ctrl)) - && (_L_flags & _L_esc_ctrl)) { // handle control escape sequence + } else if (_Char == static_cast(_Esc_ctrl) && (_L_flags & _L_esc_ctrl)) { // handle control escape sequence _Next(); using _Char_traits_type = typename _RxTraits::string_type::traits_type; @@ -4947,12 +4943,10 @@ bool _Parser2<_FwdIt, _Elem, _RxTraits>::_CharacterEscape(bool _In_character_cla _Val = static_cast(_Char) % 32; _Next(); - } else if (_Char == static_cast<_Elem>(static_cast(_Esc_hex)) - && (_L_flags & _L_esc_hex)) { // handle hexadecimal escape sequence + } else if (_Char == static_cast(_Esc_hex) && (_L_flags & _L_esc_hex)) { // handle hexadecimal escape sequence _Next(); _HexDigits(2); - } else if (_Char == static_cast<_Elem>(static_cast(_Esc_uni)) - && (_L_flags & _L_esc_uni)) { // handle Unicode escape sequence + } else if (_Char == static_cast(_Esc_uni) && (_L_flags & _L_esc_uni)) { // handle Unicode escape sequence _Next(); _HexDigits(4); } else if ((_L_flags & _L_esc_oct) && _OctalDigits()) { // handle octal escape sequence @@ -5068,13 +5062,11 @@ bool _Parser2<_FwdIt, _Elem, _RxTraits>::_Alternative() { // check for valid alt _Next(); } else if (_Mchar == _Meta_esc) { // check for valid escape sequence _Next(); - if ((_L_flags & _L_asrt_wrd) - && _Char == static_cast<_Elem>(static_cast(_Esc_word))) { // add word assert + if ((_L_flags & _L_asrt_wrd) && _Char == static_cast(_Esc_word)) { // add word assert _Nfa._Add_wbound(); _Next(); _Quant = false; - } else if ((_L_flags & _L_asrt_wrd) - && _Char == static_cast<_Elem>(static_cast(_Esc_not_word))) { // add not-word assert + } else if ((_L_flags & _L_asrt_wrd) && _Char == static_cast(_Esc_not_word)) { // add not-word assert _Nfa._Add_wbound(); _Nfa._Negate(); _Next(); diff --git a/tests/std/tests/GH_000995_regex_custom_char_types/test.cpp b/tests/std/tests/GH_000995_regex_custom_char_types/test.cpp index a057af8f9cb..c3a19ec46c3 100644 --- a/tests/std/tests/GH_000995_regex_custom_char_types/test.cpp +++ b/tests/std/tests/GH_000995_regex_custom_char_types/test.cpp @@ -13,8 +13,53 @@ using namespace std; -enum class signed_wchar_enum : short {}; -enum class ullong_enum : unsigned long long {}; +namespace signed_wchar_ns { + enum class signed_wchar_enum : short {}; + + bool operator==(const signed_wchar_enum swe, const char ch) { + return static_cast(swe) == static_cast(ch); + } + +#if !_HAS_CXX20 + bool operator!=(const signed_wchar_enum swe, const char ch) { + return !(swe == ch); + } + + bool operator==(const char ch, const signed_wchar_enum swe) { + return swe == ch; + } + + bool operator!=(const char ch, const signed_wchar_enum swe) { + return !(swe == ch); + } +#endif // !_HAS_CXX20 +} // namespace signed_wchar_ns + +using signed_wchar_ns::signed_wchar_enum; + +namespace ullong_ns { + enum class ullong_enum : unsigned long long {}; + + bool operator==(const ullong_enum ull, const char ch) { + return static_cast(ull) == static_cast(ch); + } + +#if !_HAS_CXX20 + bool operator!=(const ullong_enum ull, const char ch) { + return !(ull == ch); + } + + bool operator==(const char ch, const ullong_enum ull) { + return ull == ch; + } + + bool operator!=(const char ch, const ullong_enum ull) { + return !(ull == ch); + } +#endif // !_HAS_CXX20 +} // namespace ullong_ns + +using ullong_ns::ullong_enum; template T convert_to(const signed_wchar_enum& char_enum) { @@ -55,6 +100,28 @@ class wrapped_character { return lhs.character == rhs.character; } + friend bool operator==(const wrapped_character& wc, const char ch) { + return wc.character == static_cast(ch); + } + +#if !_HAS_CXX20 + friend bool operator!=(const wrapped_character& lhs, const wrapped_character& rhs) { + return !(lhs == rhs); + } + + friend bool operator!=(const wrapped_character& wc, const char ch) { + return !(wc == ch); + } + + friend bool operator==(const char ch, const wrapped_character& wc) { + return wc == ch; + } + + friend bool operator!=(const char ch, const wrapped_character& wc) { + return !(wc == ch); + } +#endif // !_HAS_CXX20 + template friend T convert_to(const wrapped_character& wrapped_char) { return static_cast(wrapped_char.character); @@ -67,11 +134,6 @@ class wrapped_character { template T convert_to(const wrapped_character& wrapped_char); -template -bool operator!=(const wrapped_character& lhs, const wrapped_character& rhs) { - return !(lhs == rhs); -} - using wrapped_wchar = wrapped_character; using wrapped_ullong = wrapped_character;