diff --git a/docs/import_library.md b/docs/import_library.md new file mode 100644 index 00000000000..124b8f3016c --- /dev/null +++ b/docs/import_library.md @@ -0,0 +1,52 @@ + + + +# Filenames + +Mode | Import Library | DLL (VS) | DLL (GitHub) | +--------|----------------|-----------------|---------------------| +Release | `msvcprt.lib` | `msvcp140.dll` | `msvcp140_oss.dll` | +Debug | `msvcprtd.lib` | `msvcp140d.dll` | `msvcp140d_oss.dll` | + +# Import Libraries + +An import library is a `.lib` file that defines its symbols as imported from a DLL. + +Usually there is one `.lib` file for one `.dll` file, with the same name. +The names are different for MSVC because it started encoding its ABI version into the DLL's filename, +but there was no reason to change the import library's filename. + +Also, an import library usually only contains references to DLL symbols and doesn't define anything on its own. +However, this is purely a convention - nothing technically stops an import library +from containing object files that are effectively statically linked. + +## Advantages of Injecting Additional Code + +This is what the STL's import library does - it defines some functions and variables on its own. +This allows us to: + +* Extend the STL implementation without altering the DLL export surface. + + This has been critical in allowing us to implement C++17 `` and much more. +* Separately compile functions and constant data for improved throughput. + + ``'s lookup tables are a notable example. + +## Limitations + +The caveats of this technique are: + +* It effectively defeats the purpose of the `/MD` and `/MDd` options by embedding part of + the STL implementation into the resulting user binaries, rather than staying in the STL's DLL. +* Due to the duplication in each user binary that links to the import library, + variables in the import library **cannot represent shared global state**. + + This limitation is subtle (not readily apparent from the source code) and critical. + If shared global state is necessary, our only option while preserving bincompat is adding a satellite DLL. +* Due to having just two flavors of the import library (debug and release), + we cannot use anything that depends on `_CONTAINER_DEBUG_LEVEL` or `_ITERATOR_DEBUG_LEVEL`. + +For these reasons, especially the last one, we need to strictly control what is used by the import library. +In particular, `basic_string` must not be used there. + +## Core Headers + +Restricting the import library to including core headers only is an effective way to avoid problems. +`locale0.cpp`'s inclusion of `` is currently a special case and should be treated with extreme caution. diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index 7cc688172a3..5fd721392a9 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -577,7 +577,7 @@ function(add_stl_dlls D_SUFFIX REL_OR_DBG) target_stl_compile_options(msvcp${D_SUFFIX}_eha_objects ${REL_OR_DBG}) add_library(msvcp${D_SUFFIX} SHARED) - target_link_libraries(msvcp${D_SUFFIX} PRIVATE msvcp${D_SUFFIX}_eha_objects msvcp${D_SUFFIX}_objects msvcp${D_SUFFIX}_init_objects "${TOOLSET_LIB}/vcruntime${D_SUFFIX}.lib" "${TOOLSET_LIB}/msvcrt${D_SUFFIX}.lib" "ucrt${D_SUFFIX}.lib" "ole32.lib") + target_link_libraries(msvcp${D_SUFFIX} PRIVATE msvcp${D_SUFFIX}_eha_objects msvcp${D_SUFFIX}_objects msvcp${D_SUFFIX}_init_objects "${TOOLSET_LIB}/vcruntime${D_SUFFIX}.lib" "${TOOLSET_LIB}/msvcrt${D_SUFFIX}.lib" "ucrt${D_SUFFIX}.lib") set_target_properties(msvcp${D_SUFFIX} PROPERTIES ARCHIVE_OUTPUT_NAME "msvcp140_base${D_SUFFIX}${VCLIBS_SUFFIX}") set_target_properties(msvcp${D_SUFFIX} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") set_target_properties(msvcp${D_SUFFIX} PROPERTIES OUTPUT_NAME "msvcp140${D_SUFFIX}${VCLIBS_SUFFIX}") @@ -585,7 +585,7 @@ function(add_stl_dlls D_SUFFIX REL_OR_DBG) # import library 'statics' add_library(msvcp${D_SUFFIX}_implib_objects OBJECT ${IMPLIB_SOURCES}) - target_compile_definitions(msvcp${D_SUFFIX}_implib_objects PRIVATE _DLL) + target_compile_definitions(msvcp${D_SUFFIX}_implib_objects PRIVATE _DLL _ENFORCE_ONLY_CORE_HEADERS) target_compile_options(msvcp${D_SUFFIX}_implib_objects PRIVATE /EHsc) # No /GL! target_stl_compile_options(msvcp${D_SUFFIX}_implib_objects ${REL_OR_DBG}) diff --git a/stl/inc/atomic b/stl/inc/atomic index 6d25543e92f..ebef69084d8 100644 --- a/stl/inc/atomic +++ b/stl/inc/atomic @@ -524,8 +524,8 @@ inline void _Atomic_lock_acquire(long& _Spinlock) noexcept { // Algorithm from Intel(R) 64 and IA-32 Architectures Optimization Reference Manual, May 2020 // Example 2-4. Contended Locks with Increasing Back-off Example - Improved Version, page 2-22 // The code in mentioned manual is covered by the 0BSD license. - int _Current_backoff = 1; - const int _Max_backoff = 64; + int _Current_backoff = 1; + constexpr int _Max_backoff = 64; while (_InterlockedExchange(&_Spinlock, 1) != 0) { while (__iso_volatile_load32(&reinterpret_cast(_Spinlock)) != 0) { for (int _Count_down = _Current_backoff; _Count_down != 0; --_Count_down) { diff --git a/stl/inc/charconv b/stl/inc/charconv index 6a71bdf4152..f2326f7e15e 100644 --- a/stl/inc/charconv +++ b/stl/inc/charconv @@ -980,7 +980,7 @@ void _Assemble_floating_point_infinity(const bool _Is_negative, _FloatingType& _ _Uint_type _Sign_component = _Is_negative; _Sign_component <<= _Floating_traits::_Sign_shift; - const _Uint_type _Exponent_component = _Floating_traits::_Shifted_exponent_mask; + constexpr _Uint_type _Exponent_component = _Floating_traits::_Shifted_exponent_mask; _Result = _Bit_cast<_FloatingType>(_Sign_component | _Exponent_component); } @@ -1227,12 +1227,12 @@ _NODISCARD errc _Assemble_floating_point_value_from_big_integer_flt(const _Big_i _FloatingType& _Result) noexcept { using _Traits = _Floating_type_traits<_FloatingType>; - const int32_t _Base_exponent = _Traits::_Mantissa_bits - 1; + constexpr int32_t _Base_exponent = _Traits::_Mantissa_bits - 1; // Very fast case: If we have 64 bits of precision or fewer, // we can just take the two low order elements from the _Big_integer_flt: if (_Integer_bits_of_precision <= 64) { - const int32_t _Exponent = _Base_exponent; + constexpr int32_t _Exponent = _Base_exponent; const uint32_t _Mantissa_low = _Integer_value._Myused > 0 ? _Integer_value._Mydata[0] : 0; const uint32_t _Mantissa_high = _Integer_value._Myused > 1 ? _Integer_value._Mydata[1] : 0; @@ -1335,7 +1335,7 @@ _NODISCARD errc _Convert_decimal_string_to_floating_type( // To generate an N bit mantissa we require N + 1 bits of precision. The extra bit is used to correctly round // the mantissa (if there are fewer bits than this available, then that's totally okay; // in that case we use what we have and we don't need to round). - const uint32_t _Required_bits_of_precision = static_cast(_Traits::_Mantissa_bits + 1); + constexpr uint32_t _Required_bits_of_precision = static_cast(_Traits::_Mantissa_bits + 1); // The input is of the form 0.mantissa * 10^exponent, where 'mantissa' are the decimal digits of the mantissa // and 'exponent' is the decimal exponent. We decompose the mantissa into two parts: an integer part and a @@ -2167,7 +2167,7 @@ _NODISCARD to_chars_result _Floating_to_chars_hex_shortest( // C11 7.21.6.1 "The fprintf function"/8: "If the value is zero, the exponent is zero." // Special-casing zero is necessary because of the exponent. const char* const _Str = "0p+0"; - const size_t _Len = 4; + constexpr size_t _Len = 4; if (_Last - _First < static_cast(_Len)) { return {_Last, errc::value_too_large}; diff --git a/stl/inc/chrono b/stl/inc/chrono index ce864da0181..202b9aa847b 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -18,7 +18,6 @@ #if _HAS_CXX20 #include <__msvc_tzdb.hpp> -#include #include #include #include @@ -2250,14 +2249,19 @@ namespace chrono { auto [_Leap_sec, _All_ls_positive] = _Tzdb_generate_leap_seconds(_Tzdb_list.front().leap_seconds.size()); if (!_Leap_sec.empty()) { const auto& _Tzdb = _Tzdb_list.front(); + vector _Zones; - _STD transform(_Tzdb.zones.begin(), _Tzdb.zones.end(), _STD back_inserter(_Zones), - [](const auto& _Tz) { return time_zone{_Tz.name()}; }); + _Zones.reserve(_Tzdb.zones.size()); + for (const auto& _Tz : _Tzdb.zones) { + _Zones.emplace_back(_Tz.name()); + } + vector _Links; - _STD transform( - _Tzdb.links.begin(), _Tzdb.links.end(), _STD back_inserter(_Links), [](const auto& _Link) { - return time_zone_link{_Link.name(), _Link.target()}; - }); + _Links.reserve(_Tzdb.links.size()); + for (const auto& _Link : _Tzdb.links) { + _Links.emplace_back(_Link.name(), _Link.target()); + } + auto _Version = _Tzdb_update_version(_Tzdb.version, _Leap_sec.size()); _Tzdb_list.emplace_front(tzdb{ _STD move(_Version), _STD move(_Zones), _STD move(_Links), _STD move(_Leap_sec), _All_ls_positive}); diff --git a/stl/inc/compare b/stl/inc/compare index b556d805167..c01e004c92e 100644 --- a/stl/inc/compare +++ b/stl/inc/compare @@ -13,7 +13,6 @@ _EMIT_STL_WARNING(STL4038, "The contents of are available only with C++20 or later."); #else // ^^^ !_HAS_CXX20 / _HAS_CXX20 vvv #ifdef __cpp_lib_concepts -#include #include #else // ^^^ __cpp_lib_concepts / !__cpp_lib_concepts vvv #include @@ -427,8 +426,8 @@ namespace _Strong_order { using _Uint_type = typename _Traits::_Uint_type; using _Sint_type = make_signed_t<_Uint_type>; - const auto _Left_uint = _STD bit_cast<_Uint_type>(_Left); - const auto _Right_uint = _STD bit_cast<_Uint_type>(_Right); + const auto _Left_uint = _Bit_cast<_Uint_type>(_Left); + const auto _Right_uint = _Bit_cast<_Uint_type>(_Right); // 1. Ultra-fast path: equal representations are equal. if (_Left_uint == _Right_uint) { @@ -534,8 +533,8 @@ namespace _Weak_order { using _Uint_type = typename _Traits::_Uint_type; using _Sint_type = make_signed_t<_Uint_type>; - auto _Left_uint = _STD bit_cast<_Uint_type>(_Left); - auto _Right_uint = _STD bit_cast<_Uint_type>(_Right); + auto _Left_uint = _Bit_cast<_Uint_type>(_Left); + auto _Right_uint = _Bit_cast<_Uint_type>(_Right); // 1. Ultra-fast path: equal representations are equivalent. if (_Left_uint == _Right_uint) { diff --git a/stl/inc/coroutine b/stl/inc/coroutine index 071b4d948cc..9a9d16e1133 100644 --- a/stl/inc/coroutine +++ b/stl/inc/coroutine @@ -219,7 +219,7 @@ struct coroutine_handle { _NODISCARD noop_coroutine_promise& promise() const noexcept { // Returns a reference to the associated promise - return *reinterpret_cast(__builtin_coro_promise(_Ptr, 0, false)); + return *static_cast(__builtin_coro_promise(_Ptr, 0, false)); } _NODISCARD constexpr void* address() const noexcept { diff --git a/stl/inc/execution b/stl/inc/execution index 87423960973..e0b4df6e338 100644 --- a/stl/inc/execution +++ b/stl/inc/execution @@ -2813,9 +2813,9 @@ inline size_t _Get_stable_sort_tree_height(const size_t _Count, const size_t _Hw const auto _Ideal_chunks = _Hw_threads * _Oversubscription_multiplier; const size_t _Log_ideal_chunks = _Floor_of_log_2(_Ideal_chunks); #ifdef _WIN64 - const size_t _Max_tree_height = 62; // to avoid ptrdiff_t overflow + constexpr size_t _Max_tree_height = 62; // to avoid ptrdiff_t overflow #else // ^^^ _WIN64 / !_WIN64 vvv - const size_t _Max_tree_height = 30; + constexpr size_t _Max_tree_height = 30; #endif // _WIN64 const size_t _Clamped_ideal_chunks = (_STD min)(_Max_tree_height, _Log_ideal_chunks); diff --git a/stl/inc/format b/stl/inc/format index 9c0121b7fe3..8787d5857ad 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -44,6 +44,7 @@ _EMIT_STL_WARNING(STL4038, "The contents of are available only with C++ #else // ^^^ !defined(__cpp_lib_concepts) / defined(__cpp_lib_concepts) vvv #include <__msvc_format_ucd_tables.hpp> +#include #include #include #include @@ -1982,7 +1983,7 @@ private: template _NODISCARD static auto _Get_value_from_memory(const unsigned char* const _Val) noexcept { auto& _Temp = *reinterpret_cast(_Val); - return _Bit_cast<_Ty>(_Temp); + return _STD bit_cast<_Ty>(_Temp); } size_t _Num_args = 0; diff --git a/stl/inc/mdspan b/stl/inc/mdspan index 5506590b7c4..2fb443e92ec 100644 --- a/stl/inc/mdspan +++ b/stl/inc/mdspan @@ -12,6 +12,7 @@ _EMIT_STL_WARNING(STL4038, "The contents of are available only with C++23 or later."); #else // ^^^ not supported / supported language mode vvv #include +#include #include #include diff --git a/stl/inc/memory_resource b/stl/inc/memory_resource index b008571c42a..dc21f3aa59f 100644 --- a/stl/inc/memory_resource +++ b/stl/inc/memory_resource @@ -372,7 +372,7 @@ namespace pmr { #endif // _DEBUG } - _Oversized_header* _Hdr = reinterpret_cast<_Oversized_header*>(reinterpret_cast(_Ptr) + _Bytes) - 1; + _Oversized_header* _Hdr = reinterpret_cast<_Oversized_header*>(static_cast(_Ptr) + _Bytes) - 1; _STL_ASSERT(_Hdr->_Size == _Bytes && _Hdr->_Align == _Align, "Cannot deallocate memory not allocated by this memory pool."); diff --git a/stl/inc/optional b/stl/inc/optional index a9a4ee02945..093cbb659b7 100644 --- a/stl/inc/optional +++ b/stl/inc/optional @@ -18,8 +18,8 @@ _EMIT_STL_WARNING(STL4038, "The contents of are available only with C #include #include #include -#include #include +#include #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) @@ -104,7 +104,7 @@ struct _Optional_destruct_base<_Ty, false> { // either contains a value of _Ty o _CONSTEXPR20 ~_Optional_destruct_base() noexcept { if (_Has_value) { - _Destroy_in_place(_Value); + _Value.~_Ty(); } } @@ -129,7 +129,7 @@ struct _Optional_destruct_base<_Ty, false> { // either contains a value of _Ty o _CONSTEXPR20 void reset() noexcept { if (_Has_value) { - _Destroy_in_place(_Value); + _Value.~_Ty(); _Has_value = false; } } diff --git a/stl/inc/ranges b/stl/inc/ranges index 2617b7c4c4a..e3499c2189e 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -9254,7 +9254,7 @@ namespace ranges { is_nothrow_move_constructible_v<_Inner_iterator<_Const>>) // strengthened : _Parent(_STD addressof(_Parent_)), _Inner(_STD move(_Inner_)) {} - _NODISCARD static consteval auto _Get_iterator_category() noexcept { + _NODISCARD static _CONSTEVAL auto _Get_iterator_category() noexcept { if constexpr (!is_reference_v<_Invoke_result_with_repeated_type<_Maybe_const<_Const, _Fn>&, range_reference_t<_Base>, _Nx>>) { return input_iterator_tag{}; @@ -9274,7 +9274,7 @@ namespace ranges { } template - _NODISCARD static consteval bool _Is_indirection_nothrow(index_sequence<_Indices...>) noexcept { + _NODISCARD static _CONSTEVAL bool _Is_indirection_nothrow(index_sequence<_Indices...>) noexcept { return noexcept(_STD invoke(_STD declval<_Maybe_const<_Const, _Fn>&>(), *_STD get<_Indices>(_STD declval&>()._Current)...)); } @@ -9577,6 +9577,606 @@ namespace ranges { _EXPORT_STD inline constexpr _Adjacent_transform_fn<2> pairwise_transform; } // namespace views + template <_Integer_like _Int> + _NODISCARD constexpr bool _Add_with_overflow_check(const _Int _Left, const _Int _Right, _Int& _Out) { +#ifdef __clang__ + if constexpr (integral<_Int>) { + return __builtin_add_overflow(_Left, _Right, &_Out); + } else +#endif // __clang__ + { + if constexpr (!_Signed_integer_like<_Int>) { + _Out = static_cast<_Int>(_Left + _Right); + return _Out < _Left || _Out < _Right; + } else { + using _UInt = _Make_unsigned_like_t<_Int>; + _Out = static_cast<_Int>(static_cast<_UInt>(_Left) + static_cast<_UInt>(_Right)); + return (_Left > 0 && _Right > 0 && _Out <= 0) || (_Left < 0 && _Right < 0 && _Out >= 0); + } + } + } + + template <_Integer_like _Int> + _NODISCARD constexpr bool _Multiply_with_overflow_check(const _Int _Left, const _Int _Right, _Int& _Out) { +#ifdef __clang__ + if constexpr (integral<_Int>) { + return __builtin_mul_overflow(_Left, _Right, &_Out); + } else +#endif // __clang__ + { + if constexpr (!_Signed_integer_like<_Int>) { + _Out = static_cast<_Int>(_Left * _Right); + return _Left != 0 && _Right > (numeric_limits<_Int>::max)() / _Left; + } else { + // vvv Based on llvm::MulOverflow vvv + // https://github.com/llvm/llvm-project/blob/88e5206/llvm/include/llvm/Support/MathExtras.h#L725-L750 + //===----------------------------------------------------------------------===// + // + // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. + // See https://llvm.org/LICENSE.txt for license information. + // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + // + //===----------------------------------------------------------------------===// + using _UInt = _Make_unsigned_like_t<_Int>; + const _UInt _ULeft = _Left < 0 ? (0 - static_cast<_UInt>(_Left)) : static_cast<_UInt>(_Left); + const _UInt _URight = _Right < 0 ? (0 - static_cast<_UInt>(_Right)) : static_cast<_UInt>(_Right); + const _UInt _UResult = static_cast<_UInt>(_ULeft * _URight); + + const bool _Negative = (_Left < 0) != (_Right < 0); + _Out = static_cast<_Int>(_Negative ? (0 - _UResult) : _UResult); + if (_ULeft == 0 || _URight == 0) { + return false; + } + + if (_Negative) { + return _ULeft > (static_cast<_UInt>((numeric_limits<_Int>::max)()) + _UInt{1}) / _URight; + } else { + return _ULeft > static_cast<_UInt>((numeric_limits<_Int>::max)()) / _URight; + } + // ^^^ Based on llvm::MulOverflow ^^^ + } + } + } + + template + concept _Cartesian_product_is_random_access = (random_access_range<_Maybe_const<_Const, _First>> && ... + && (random_access_range<_Maybe_const<_Const, _Rest>> + && sized_range<_Maybe_const<_Const, _Rest>>) ); + + template + concept _Cartesian_product_common_arg = common_range<_Rng> || (sized_range<_Rng> && random_access_range<_Rng>); + + template + concept _Cartesian_product_is_bidirectional = (bidirectional_range<_Maybe_const<_Const, _First>> && ... + && (bidirectional_range<_Maybe_const<_Const, _Rest>> + && _Cartesian_product_common_arg<_Maybe_const<_Const, _Rest>>) ); + + template + concept _Cartesian_product_is_common = _Cartesian_product_common_arg<_First>; + + template + concept _Cartesian_product_is_sized = (sized_range<_Rngs> && ...); + + template class _FirstSent, class _First, class... _Rest> + concept _Cartesian_is_sized_sentinel = + (sized_sentinel_for<_FirstSent<_Maybe_const<_Const, _First>>, iterator_t<_Maybe_const<_Const, _First>>> && ... + && (sized_range<_Maybe_const<_Const, _Rest>> + && sized_sentinel_for>, + iterator_t<_Maybe_const<_Const, _Rest>>>) ); + + template <_Cartesian_product_common_arg _Rng> + _NODISCARD constexpr auto _Cartesian_common_arg_end(_Rng& _Range) { + if constexpr (common_range<_Rng>) { + return _RANGES end(_Range); + } else { + return _RANGES begin(_Range) + _RANGES distance(_Range); + } + } + + template + inline constexpr auto _Compile_time_max_size = (numeric_limits>::max)(); + + template + concept _Constant_sized_range = + sized_range<_Ty> && requires { typename _Require_constant::size()>; }; + + template <_Constant_sized_range _Ty> + inline constexpr auto _Compile_time_max_size<_Ty> = remove_reference_t<_Ty>::size(); + + template + inline constexpr auto _Compile_time_max_size> = _Size; + + template + inline constexpr auto _Compile_time_max_size> = _Size; + + template + requires (_Extent != dynamic_extent) + inline constexpr auto _Compile_time_max_size> = _Extent; + + template + requires (_Extent != dynamic_extent) + inline constexpr auto _Compile_time_max_size> = _Extent; + + template + inline constexpr auto _Compile_time_max_size> = _Compile_time_max_size<_Ty>; + + template + requires sized_range + inline constexpr auto _Compile_time_max_size> = _Compile_time_max_size; + + template + inline constexpr auto _Compile_time_max_size> = _Compile_time_max_size<_Ty>; + + template + requires sized_range + inline constexpr auto _Compile_time_max_size> = _Compile_time_max_size; + + template + _NODISCARD _CONSTEVAL int _Cartesian_product_max_size_bit_width() noexcept { + if constexpr (sized_range<_Rng>) { + if constexpr (requires(range_size_t<_Rng> _Val) { _STD bit_width(_Val); }) { + return _STD bit_width(_Compile_time_max_size<_Rng>); + } else { + return numeric_limits>::digits; + } + } else { + return numeric_limits<_Make_unsigned_like_t>>::digits; + } + } + + template + _NODISCARD _CONSTEVAL auto _Cartesian_product_optimal_size_type() noexcept { + constexpr int _Optimal_size_type_bit_width = + (_Cartesian_product_max_size_bit_width<_First>() + ... + _Cartesian_product_max_size_bit_width<_Rest>()); + if constexpr (_Optimal_size_type_bit_width <= 8) { + return uint_least8_t{}; + } else if constexpr (_Optimal_size_type_bit_width <= 16) { + return uint_least16_t{}; + } else if constexpr (_Optimal_size_type_bit_width <= 32) { + return uint_least32_t{}; + } else if constexpr (_Optimal_size_type_bit_width <= 64) { + return uint_least64_t{}; + } else { + return _Unsigned128{}; + } + } + + _EXPORT_STD template + requires (view<_First> && ... && view<_Rest>) + class cartesian_product_view : public view_interface> { + private: + template + using _Optimal_size_type = decltype(_Cartesian_product_optimal_size_type<_Maybe_const<_Const, _First>, + _Maybe_const<_Const, _Rest>...>()); + + template + using _Difference_type = common_type_t<_Make_signed_like_t<_Optimal_size_type<_Const>>, + range_difference_t<_Maybe_const<_Const, _First>>, range_difference_t<_Maybe_const<_Const, _Rest>>...>; + + template + using _Size_type = common_type_t<_Optimal_size_type<_Const>, range_size_t<_Maybe_const<_Const, _First>>, + range_size_t<_Maybe_const<_Const, _Rest>>...>; + + /* [[no_unique_address]] */ tuple<_First, _Rest...> _Bases; + + template + class _Iterator { + private: + friend cartesian_product_view; + using _Parent_t = _Maybe_const<_Const, cartesian_product_view>; + + _Parent_t* _Parent = nullptr; + tuple>, iterator_t<_Maybe_const<_Const, _Rest>>...> _Current; + + template + constexpr void _Next() { + auto& _It = _STD get<_Index>(_Current); + ++_It; + if constexpr (_Index > 0) { + auto& _Range = _STD get<_Index>(_Parent->_Bases); + if (_It == _RANGES end(_Range)) { + _It = _RANGES begin(_Range); + _Next<_Index - 1>(); + } + } + } + + template + constexpr void _Prev() { + auto& _It = _STD get<_Index>(_Current); + if constexpr (_Index > 0) { + auto& _Range = _STD get<_Index>(_Parent->_Bases); + if (_It == _RANGES begin(_Range)) { + _It = _Cartesian_common_arg_end(_Range); + _Prev<_Index - 1>(); + } + } + --_It; + } + + template + _NODISCARD constexpr bool _Entire_tail_at_begin(index_sequence<_Indices...>) const { + return ((_STD get<_Indices + 1>(_Current) == _RANGES begin(_STD get<_Indices + 1>(_Parent->_Bases))) + && ...); + } + + template + constexpr void _Advance(const _Difference_type<_Const> _Off) { + if (_Off == 0) { + return; + } + + auto& _Range = _STD get<_Index>(_Parent->_Bases); + auto& _It = _STD get<_Index>(_Current); + using _Iter = remove_reference_t; + using _Diff = _Difference_type<_Const>; + + if constexpr (_Index > 0) { + const auto _Size = static_cast<_Diff>(_RANGES ssize(_Range)); + const auto _Begin = _RANGES begin(_Range); + const auto _It_off = static_cast<_Diff>(_It - _Begin); + const auto _It_new_off = static_cast<_Diff>((_It_off + _Off) % _Size); + const auto _Next_off = static_cast<_Diff>((_It_off + _Off) / _Size); + if (_It_new_off < 0) { + _It = _Begin + static_cast>(_It_new_off + _Size); + _Advance<_Index - 1>(_Next_off - 1); + } else { + _It = _Begin + static_cast>(_It_new_off); + _Advance<_Index - 1>(_Next_off); + } + } else { +#if _ITERATOR_DEBUG_LEVEL != 0 + const auto _It_off = static_cast<_Diff>(_It - _RANGES begin(_Range)); + _STL_VERIFY(_It_off + _Off >= 0, "Cannot advance cartesian_product_view iterator before begin " + "(N4928 [range.cartesian.iterator]/19)."); + if constexpr (sized_range) { + const auto _Size = static_cast<_Diff>(_RANGES ssize(_Range)); + _STL_VERIFY(_It_off + _Off < _Size + || (_It_off + _Off == _Size + && _Entire_tail_at_begin(make_index_sequence{})), + "Cannot advance cartesian_product_view iterator past end (N4928 " + "[range.cartesian.iterator]/19)."); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _It += static_cast>(_Off); + } + } + + template + _NODISCARD constexpr bool _Is_end(index_sequence<_Indices...>) const { + return ((_STD get<_Indices>(_Current) == _RANGES end(_STD get<_Indices>(_Parent->_Bases))) || ...); + } + + template + _NODISCARD constexpr auto _End_tuple(index_sequence<_Indices...>) const { + return tuple>, iterator_t<_Maybe_const<_Const, _Rest>>...>{ + _RANGES end(_STD get<0>(_Parent->_Bases)), + _RANGES begin(_STD get<_Indices + 1>(_Parent->_Bases))...}; + } + + template + _NODISCARD constexpr _Difference_type<_Const> _Distance_from(const _Tuple& _Tpl) const { + const auto _Diff = + static_cast<_Difference_type<_Const>>(_STD get<_Index>(_Current) - _STD get<_Index>(_Tpl)); + if constexpr (_Index > 0) { + _Difference_type<_Const> _Result{1}; + const auto _Size = + static_cast<_Difference_type<_Const>>(_RANGES size(_STD get<_Index>(_Parent->_Bases))); + [[maybe_unused]] const bool _Overflow = + _Multiply_with_overflow_check(_Size, _Distance_from<_Index - 1>(_Tpl), _Result) + || _Add_with_overflow_check(_Result, _Diff, _Result); +#if _ITERATOR_DEBUG_LEVEL != 0 + _STL_VERIFY(!_Overflow, "Scaled-sum cannot be represented by difference_type (N4928 " + "[range.cartesian.iterator]/8)."); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _Result; + } else { + return _Diff; + } + } + + template + _NODISCARD static _CONSTEVAL bool _Is_iter_move_nothrow(index_sequence<_Indices...>) noexcept { + return conjunction_v< + is_nothrow_move_constructible>>, + is_nothrow_move_constructible>>...> + && (noexcept(_RANGES iter_move(_STD get<_Indices>(_STD declval()._Current))) + && ...); + } + + template + _NODISCARD static _CONSTEVAL bool _Is_iter_swap_nothrow(index_sequence<_Indices...>) noexcept { + return (noexcept(_RANGES iter_swap(_STD get<_Indices>(_STD declval()._Current), + _STD get<_Indices>(_STD declval()._Current))) + && ...); + } + + constexpr _Iterator(_Parent_t& _Parent_, + tuple>, iterator_t<_Maybe_const<_Const, _Rest>>...> _Current_) + : _Parent(_STD addressof(_Parent_)), _Current(_STD move(_Current_)) {} + + public: + using iterator_category = input_iterator_tag; + using iterator_concept = conditional_t<_Cartesian_product_is_random_access<_Const, _First, _Rest...>, + random_access_iterator_tag, + conditional_t<_Cartesian_product_is_bidirectional<_Const, _First, _Rest...>, bidirectional_iterator_tag, + conditional_t>, forward_iterator_tag, + input_iterator_tag>>>; + using value_type = + tuple>, range_value_t<_Maybe_const<_Const, _Rest>>...>; + using reference = tuple>, + range_reference_t<_Maybe_const<_Const, _Rest>>...>; + using difference_type = _Difference_type<_Const>; + + _Iterator() = default; + + constexpr _Iterator(_Iterator _It) + requires _Const + && (convertible_to, iterator_t> && ... + && convertible_to, iterator_t>) + : _Parent(_It._Parent), _Current(_STD move(_It._Current)) {} + + _NODISCARD constexpr auto operator*() const { + return _RANGES _Tuple_transform([](auto& _It) -> decltype(auto) { return *_It; }, _Current); + } + + constexpr _Iterator& operator++() { + _Next(); + return *this; + } + + constexpr void operator++(int) { + ++*this; + } + + constexpr _Iterator operator++(int) + requires forward_range<_Maybe_const<_Const, _First>> + { + auto _Tmp = *this; + ++*this; + return _Tmp; + } + + constexpr _Iterator& operator--() + requires _Cartesian_product_is_bidirectional<_Const, _First, _Rest...> + { + _Prev(); + return *this; + } + + constexpr _Iterator operator--(int) + requires _Cartesian_product_is_bidirectional<_Const, _First, _Rest...> + { + auto _Tmp = *this; + --*this; + return _Tmp; + } + + constexpr _Iterator& operator+=(const difference_type _Off) + requires _Cartesian_product_is_random_access<_Const, _First, _Rest...> + { + _Advance(_Off); + return *this; + } + + constexpr _Iterator& operator-=(const difference_type _Off) + requires _Cartesian_product_is_random_access<_Const, _First, _Rest...> + { + _Advance(-_Off); + return *this; + } + + _NODISCARD constexpr reference operator[](const difference_type _Off) const + requires _Cartesian_product_is_random_access<_Const, _First, _Rest...> + { + return *(*this + _Off); + } + + _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) + requires equality_comparable>> + { + return _Left._Current == _Right._Current; + } + + _NODISCARD_FRIEND constexpr bool operator==(const _Iterator& _It, default_sentinel_t) { + return _It._Is_end(make_index_sequence<1 + sizeof...(_Rest)>{}); + } + + _NODISCARD_FRIEND constexpr auto operator<=>(const _Iterator& _Left, const _Iterator& _Right) + requires _All_random_access<_Const, _First, _Rest...> + { + return _Left._Current <=> _Right._Current; + } + + _NODISCARD_FRIEND constexpr _Iterator operator+(const _Iterator& _It, const difference_type _Off) + requires _Cartesian_product_is_random_access<_Const, _First, _Rest...> + { + return _Iterator{_It} += _Off; + } + + _NODISCARD_FRIEND constexpr _Iterator operator+(const difference_type _Off, const _Iterator& _It) + requires _Cartesian_product_is_random_access<_Const, _First, _Rest...> + { + return _It + _Off; + } + + _NODISCARD_FRIEND constexpr _Iterator operator-(const _Iterator& _It, const difference_type _Off) + requires _Cartesian_product_is_random_access<_Const, _First, _Rest...> + { + return _Iterator{_It} -= _Off; + } + + _NODISCARD_FRIEND constexpr difference_type operator-(const _Iterator& _Left, const _Iterator& _Right) + requires _Cartesian_is_sized_sentinel<_Const, iterator_t, _First, _Rest...> + { + return _Left._Distance_from(_Right._Current); + } + + _NODISCARD_FRIEND constexpr difference_type operator-(const _Iterator& _It, default_sentinel_t) + requires _Cartesian_is_sized_sentinel<_Const, sentinel_t, _First, _Rest...> + { + return _It._Distance_from(_It._End_tuple(make_index_sequence{})); + } + + _NODISCARD_FRIEND constexpr difference_type operator-(default_sentinel_t _Se, const _Iterator& _It) + requires _Cartesian_is_sized_sentinel<_Const, sentinel_t, _First, _Rest...> + { + return -(_It - _Se); + } + + _NODISCARD_FRIEND constexpr auto iter_move(const _Iterator& _It) noexcept( + _Is_iter_move_nothrow(make_index_sequence<1 + sizeof...(_Rest)>{})) { + return _RANGES _Tuple_transform(_RANGES iter_move, _It._Current); + } + + friend constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) noexcept( + _Is_iter_swap_nothrow(make_index_sequence<1 + sizeof...(_Rest)>{})) + requires (indirectly_swappable>> && ... + && indirectly_swappable>>) + { + return [&](index_sequence<_Indices...>) { + return (_RANGES iter_swap(_STD get<_Indices>(_Left._Current), _STD get<_Indices>(_Right._Current)), + ...); + } + (make_index_sequence<1 + sizeof...(_Rest)>{}); + } + }; + + template + _NODISCARD constexpr auto _Begin_or_first_end([[maybe_unused]] const bool _Is_empty) { + if constexpr (_Index == 0) { + return _Is_empty ? _RANGES begin(_STD get<_Index>(_Bases)) + : _Cartesian_common_arg_end(_STD get<_Index>(_Bases)); + } else { + return _RANGES begin(_STD get<_Index>(_Bases)); + } + } + + template + _NODISCARD constexpr auto _Begin_or_first_end([[maybe_unused]] const bool _Is_empty) const { + if constexpr (_Index == 0) { + return _Is_empty ? _RANGES begin(_STD get<_Index>(_Bases)) + : _Cartesian_common_arg_end(_STD get<_Index>(_Bases)); + } else { + return _RANGES begin(_STD get<_Index>(_Bases)); + } + } + + public: + constexpr cartesian_product_view() = default; + + constexpr explicit cartesian_product_view(_First _First_base, _Rest... _Other_bases) noexcept( + (is_nothrow_move_constructible_v<_First> && ... && is_nothrow_move_constructible_v<_Rest>) ) // strengthened + : _Bases(_STD move(_First_base), _STD move(_Other_bases)...) {} + + _NODISCARD constexpr _Iterator begin() + requires (!_Simple_view<_First> || ... || !_Simple_view<_Rest>) + { + return _Iterator{*this, _RANGES _Tuple_transform(_RANGES begin, _Bases)}; + } + + _NODISCARD constexpr _Iterator begin() const + requires (range && ... && range) + { + return _Iterator{*this, _RANGES _Tuple_transform(_RANGES begin, _Bases)}; + } + + _NODISCARD constexpr _Iterator end() + requires ((!_Simple_view<_First> || ... || !_Simple_view<_Rest>) && _Cartesian_product_is_common<_First>) + { + const bool _Is_empty = [&](index_sequence<_Indices...>) { + return (_RANGES empty(_STD get<_Indices + 1>(_Bases)) || ...); + } + (make_index_sequence{}); + + const auto _Make_iter_tuple = [&](index_sequence<_Indices...>) { + return tuple, iterator_t<_Rest>...>{_Begin_or_first_end<_Indices>(_Is_empty)...}; + }; + return _Iterator{*this, _Make_iter_tuple(make_index_sequence<1 + sizeof...(_Rest)>{})}; + } + + _NODISCARD constexpr _Iterator end() const + requires _Cartesian_product_is_common + { + const bool _Is_empty = [&](index_sequence<_Indices...>) { + return (_RANGES empty(_STD get<_Indices + 1>(_Bases)) || ...); + } + (make_index_sequence{}); + + const auto _Make_iter_tuple = [&](index_sequence<_Indices...>) { + return tuple, iterator_t...>{ + _Begin_or_first_end<_Indices>(_Is_empty)...}; + }; + return _Iterator{*this, _Make_iter_tuple(make_index_sequence<1 + sizeof...(_Rest)>{})}; + } + + _NODISCARD constexpr default_sentinel_t end() const noexcept { + return default_sentinel; + } + + _NODISCARD constexpr auto size() + requires _Cartesian_product_is_sized<_First, _Rest...> + { + return [&](index_sequence<_Indices...>) { +#if _CONTAINER_DEBUG_LEVEL > 0 + _Size_type _Product{1}; + const bool _Overflow = + (_Multiply_with_overflow_check( + _Product, static_cast<_Size_type>(_RANGES size(_STD get<_Indices>(_Bases))), _Product) + || ...); + _STL_VERIFY(!_Overflow, "Size of cartesian product cannot be represented by size type (N4928 " + "[range.cartesian.view]/10)."); + return _Product; +#else // ^^^ _CONTAINER_DEBUG_LEVEL > 0 / _CONTAINER_DEBUG_LEVEL == 0 vvv + return (static_cast<_Size_type>(_RANGES size(_STD get<_Indices>(_Bases))) * ...); +#endif // ^^^ _CONTAINER_DEBUG_LEVEL == 0 ^^^ + } + (make_index_sequence<1 + sizeof...(_Rest)>{}); + } + + _NODISCARD constexpr auto size() const + requires _Cartesian_product_is_sized + { + return [&](index_sequence<_Indices...>) { +#if _CONTAINER_DEBUG_LEVEL > 0 + _Size_type _Product{1}; + const bool _Overflow = + (_Multiply_with_overflow_check( + _Product, static_cast<_Size_type>(_RANGES size(_STD get<_Indices>(_Bases))), _Product) + || ...); + _STL_VERIFY(!_Overflow, "Size of cartesian product cannot be represented by size type (N4928 " + "[range.cartesian.view]/10)."); + return _Product; +#else // ^^^ _CONTAINER_DEBUG_LEVEL > 0 / _CONTAINER_DEBUG_LEVEL == 0 vvv + return (static_cast<_Size_type>(_RANGES size(_STD get<_Indices>(_Bases))) * ...); +#endif // ^^^ _CONTAINER_DEBUG_LEVEL == 0 ^^^ + } + (make_index_sequence<1 + sizeof...(_Rest)>{}); + } + }; + + template + cartesian_product_view(_Rngs&&...) -> cartesian_product_view...>; + + namespace views { + class _Cartesian_product_fn { + public: + _NODISCARD constexpr auto operator()() const noexcept { + return views::single(tuple{}); + } + + template + _NODISCARD constexpr auto operator()(_Rngs&&... _Ranges) const + noexcept(noexcept(cartesian_product_view...>{_STD forward<_Rngs>(_Ranges)...})) + requires requires { cartesian_product_view...>{_STD forward<_Rngs>(_Ranges)...}; } + { + return cartesian_product_view...>{_STD forward<_Rngs>(_Ranges)...}; + } + }; + + _EXPORT_STD inline constexpr _Cartesian_product_fn cartesian_product; + } // namespace views + #ifdef __cpp_lib_ranges_to_container // clang-format off template diff --git a/stl/inc/regex b/stl/inc/regex index 05cb7465d3b..7053b6173f8 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -178,6 +178,38 @@ struct _Cl_names { // structure to associate class name with mask value } }; +template +struct _Char_traits_eq { + using _Elem = typename _Traits::char_type; + + bool operator()(_Elem _Left, _Elem _Right) const noexcept { + return _Traits::eq(_Left, _Right); + } +}; + +template +struct _Char_traits_lt { + using _Elem = typename _Traits::char_type; + + bool operator()(_Elem _Left, _Elem _Right) const noexcept { + return _Traits::lt(_Left, _Right); + } +}; + +// library-provided char_traits::eq behaves like equal_to<_Elem> +// TRANSITION: This should not be activated for user-defined specializations of char_traits +template +_INLINE_VAR constexpr bool _Can_memcmp_elements_with_pred<_Elem, _Elem, _Char_traits_eq>> = + _Can_memcmp_elements<_Elem, _Elem>; + +// library-provided char_traits::lt behaves like less> +// TRANSITION: This should not be activated for user-defined specializations of char_traits +template +struct _Lex_compare_memcmp_classify_pred<_Elem, _Elem, _Char_traits_lt>> { + using _UElem = make_unsigned_t<_Elem>; + using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_UElem, _UElem>, less, void>; +}; + template struct _Cmp_cs { // functor to compare two character values for equality using _Elem = typename _RxTraits::char_type; diff --git a/stl/inc/type_traits b/stl/inc/type_traits index 679c42f7a18..749ce928c2d 100644 --- a/stl/inc/type_traits +++ b/stl/inc/type_traits @@ -2344,6 +2344,14 @@ struct _Floating_type_traits : _Floating_type_traits {}; // ^^^^^^^^^^ DERIVED FROM corecrt_internal_fltintrn.h ^^^^^^^^^^ +template , is_trivially_copyable<_To>, + is_trivially_copyable<_From>>, + int> = 0> +_NODISCARD constexpr _To _Bit_cast(const _From& _Val) noexcept { + return __builtin_bit_cast(_To, _Val); +} + #if _HAS_TR1_NAMESPACE _STL_DISABLE_DEPRECATED_WARNING namespace _DEPRECATE_TR1_NAMESPACE tr1 { diff --git a/stl/inc/typeindex b/stl/inc/typeindex index a3ec721f22d..1392573ef79 100644 --- a/stl/inc/typeindex +++ b/stl/inc/typeindex @@ -12,6 +12,7 @@ #if _HAS_CXX20 #include +#include #endif // _HAS_CXX20 #pragma pack(push, _CRT_PACKING) @@ -40,9 +41,12 @@ public: #if _HAS_CXX20 _NODISCARD strong_ordering operator<=>(const type_index& _Right) const noexcept { - return *_Tptr == *_Right._Tptr ? strong_ordering::equal - : _Tptr->before(*_Right._Tptr) ? strong_ordering::less - : strong_ordering::greater; + // TRANSITION, DevCom-10326599, should rely on a stable interface + if (_Tptr == _Right._Tptr) { + return strong_ordering::equal; + } + + return _CSTD strcmp(_Tptr->raw_name() + 1, _Right._Tptr->raw_name() + 1) <=> 0; } #else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD bool operator!=(const type_index& _Right) const noexcept { diff --git a/stl/inc/variant b/stl/inc/variant index 670c5b4af3e..478de8c34ad 100644 --- a/stl/inc/variant +++ b/stl/inc/variant @@ -12,13 +12,15 @@ #if !_HAS_CXX17 _EMIT_STL_WARNING(STL4038, "The contents of are available only with C++17 or later."); #else // ^^^ !_HAS_CXX17 / _HAS_CXX17 vvv +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 #include #include #include #include -#include #include -#include +#include #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) @@ -693,10 +695,12 @@ _NODISCARD constexpr _Variant_raw_visit_t<_Fn, _Storage> _Variant_raw_visit( template class _Variant_base; +inline constexpr size_t _Schar_max_as_size = static_cast(-1) / 2; +inline constexpr size_t _Short_max_as_size = static_cast(-1) / 2; + template using _Variant_index_t = // signed so that conversion of -1 to size_t can cheaply sign extend - conditional_t<(_Count < static_cast((numeric_limits::max)())), signed char, - conditional_t<(_Count < static_cast((numeric_limits::max)())), short, int>>; + conditional_t<(_Count < _Schar_max_as_size), signed char, conditional_t<(_Count < _Short_max_as_size), short, int>>; template struct _Variant_construct_visitor { // visitor that constructs the same alternative in a target _Variant_base as is @@ -808,8 +812,9 @@ public: _CONSTEXPR20 void _Destroy() noexcept { // destroy the contained value // pre: _Idx == index() - if constexpr (_Idx != variant_npos && !is_trivially_destructible_v<_Meta_at_c, _Idx>>) { - _STD _Destroy_in_place(_STD _Variant_raw_get<_Idx>(_Storage())); + using _Indexed_value_type = remove_cv_t<_Meta_at_c, _Idx>>; + if constexpr (_Idx != variant_npos && !is_trivially_destructible_v<_Indexed_value_type>) { + _STD _Variant_raw_get<_Idx>(_Storage()).~_Indexed_value_type(); } } @@ -817,7 +822,8 @@ public: if constexpr (!conjunction_v...>) { _STD _Variant_raw_visit(index(), _Storage(), [](auto _Ref) noexcept { if constexpr (decltype(_Ref)::_Idx != variant_npos) { - _STD _Destroy_in_place(_Ref._Val); + using _Indexed_value_type = _Remove_cvref_t; + _Ref._Val.~_Indexed_value_type(); } }); } diff --git a/stl/inc/xcharconv_tables.h b/stl/inc/xcharconv_tables.h index 7e43ab8b83c..1ffb2ae7624 100644 --- a/stl/inc/xcharconv_tables.h +++ b/stl/inc/xcharconv_tables.h @@ -1,4 +1,4 @@ -// xcharconv_tables.h internal header +// xcharconv_tables.h internal header (core) // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception diff --git a/stl/inc/xfacet b/stl/inc/xfacet index ee4663bbae9..6a0807d4abf 100644 --- a/stl/inc/xfacet +++ b/stl/inc/xfacet @@ -3,6 +3,10 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// This header is used to compile the import library (via locale0_implib.cpp => locale0.cpp => xfacet). +// MAJOR LIMITATIONS apply to what can be included here! +// Before editing this file, read: /docs/import_library.md + #pragma once #ifndef _XFACET_ #define _XFACET_ @@ -16,11 +20,6 @@ _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new -// This must be as small as possible, because its contents are -// injected into the msvcprt.lib and msvcprtd.lib import libraries. -// Do not include or define anything else here. -// In particular, basic_string must not be included here. - _STD_BEGIN extern "C++" class _CRTIMP2_PURE_IMPORT _Facet_base { // code for reference counting a facet public: diff --git a/stl/inc/xstring b/stl/inc/xstring index 80453b0d412..aacda3476e5 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -535,38 +535,6 @@ basic_ostream<_Elem, _Traits>& _Insert_string( return _Ostr; } -template -struct _Char_traits_eq { - using _Elem = typename _Traits::char_type; - - bool operator()(_Elem _Left, _Elem _Right) const noexcept { - return _Traits::eq(_Left, _Right); - } -}; - -template -struct _Char_traits_lt { - using _Elem = typename _Traits::char_type; - - bool operator()(_Elem _Left, _Elem _Right) const noexcept { - return _Traits::lt(_Left, _Right); - } -}; - -// library-provided char_traits::eq behaves like equal_to<_Elem> -// TRANSITION: This should not be activated for user-defined specializations of char_traits -template -_INLINE_VAR constexpr bool _Can_memcmp_elements_with_pred<_Elem, _Elem, _Char_traits_eq>> = - _Can_memcmp_elements<_Elem, _Elem>; - -// library-provided char_traits::lt behaves like less> -// TRANSITION: This should not be activated for user-defined specializations of char_traits -template -struct _Lex_compare_memcmp_classify_pred<_Elem, _Elem, _Char_traits_lt>> { - using _UElem = make_unsigned_t<_Elem>; - using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_UElem, _UElem>, less, void>; -}; - template using _Traits_ch_t = typename _Traits::char_type; diff --git a/stl/inc/xutility b/stl/inc/xutility index 398013dc7ea..047456fe00f 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -186,14 +186,6 @@ _Ty* __std_max_element(_Ty* _First, _Ty* _Last) noexcept { _STD_BEGIN -template , is_trivially_copyable<_To>, - is_trivially_copyable<_From>>, - int> = 0> -_NODISCARD constexpr _To _Bit_cast(const _From& _Val) noexcept { - return __builtin_bit_cast(_To, _Val); -} - template struct _Get_first_parameter; diff --git a/stl/inc/yvals.h b/stl/inc/yvals.h index 4f9cf55d90d..59107cf8135 100644 --- a/stl/inc/yvals.h +++ b/stl/inc/yvals.h @@ -3,6 +3,10 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// This header is used to compile the import library (via locale0_implib.cpp => locale0.cpp => xfacet => yvals.h). +// MAJOR LIMITATIONS apply to what can be included here! +// Before editing this file, read: /docs/import_library.md + #pragma once #ifndef _YVALS #define _YVALS diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 7500521e8f1..d93ac3dacba 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -341,6 +341,7 @@ // P2302R4 ranges::contains, ranges::contains_subrange // P2321R2 zip // P2322R6 ranges::fold_left, ranges::fold_right, Etc. +// P2374R4 views::cartesian_product // P2387R3 Pipe Support For User-Defined Range Adaptors // P2404R3 Move-Only Types For Comparison Concepts // P2417R2 More constexpr bitset @@ -358,6 +359,7 @@ // P2499R0 string_view Range Constructor Should Be explicit // P2505R5 Monadic Functions For expected // P2539R4 Synchronizing print() With The Underlying Stream +// P2540R1 Empty Product For Certain Views // P2549R1 unexpected::error() // P2599R2 mdspan: index_type, size_type // P2604R0 mdspan: data_handle_type, data_handle(), exhaustive @@ -1728,24 +1730,25 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #define __cpp_lib_move_only_function 202110L #ifdef __cpp_lib_concepts -#define __cpp_lib_out_ptr 202106L -#define __cpp_lib_print 202207L -#define __cpp_lib_ranges_as_const 202207L -#define __cpp_lib_ranges_as_rvalue 202207L -#define __cpp_lib_ranges_chunk 202202L -#define __cpp_lib_ranges_chunk_by 202202L -#define __cpp_lib_ranges_contains 202207L -#define __cpp_lib_ranges_enumerate 202302L -#define __cpp_lib_ranges_find_last 202207L -#define __cpp_lib_ranges_fold 202207L -#define __cpp_lib_ranges_iota 202202L -#define __cpp_lib_ranges_join_with 202202L -#define __cpp_lib_ranges_repeat 202207L -#define __cpp_lib_ranges_slide 202202L -#define __cpp_lib_ranges_starts_ends_with 202106L -#define __cpp_lib_ranges_stride 202207L -#define __cpp_lib_ranges_to_container 202202L -#define __cpp_lib_ranges_zip 202110L +#define __cpp_lib_out_ptr 202106L +#define __cpp_lib_print 202207L +#define __cpp_lib_ranges_as_const 202207L +#define __cpp_lib_ranges_as_rvalue 202207L +#define __cpp_lib_ranges_cartesian_product 202207L +#define __cpp_lib_ranges_chunk 202202L +#define __cpp_lib_ranges_chunk_by 202202L +#define __cpp_lib_ranges_contains 202207L +#define __cpp_lib_ranges_enumerate 202302L +#define __cpp_lib_ranges_find_last 202207L +#define __cpp_lib_ranges_fold 202207L +#define __cpp_lib_ranges_iota 202202L +#define __cpp_lib_ranges_join_with 202202L +#define __cpp_lib_ranges_repeat 202207L +#define __cpp_lib_ranges_slide 202202L +#define __cpp_lib_ranges_starts_ends_with 202106L +#define __cpp_lib_ranges_stride 202207L +#define __cpp_lib_ranges_to_container 202202L +#define __cpp_lib_ranges_zip 202110L #endif // __cpp_lib_concepts #define __cpp_lib_spanstream 202106L diff --git a/stl/msbuild/stl_base/msvcp.settings.targets b/stl/msbuild/stl_base/msvcp.settings.targets index c3b50a526f3..f136b69304b 100644 --- a/stl/msbuild/stl_base/msvcp.settings.targets +++ b/stl/msbuild/stl_base/msvcp.settings.targets @@ -51,8 +51,6 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -opt:ref,noicf $(LinkAdditionalOptions) -nodefaultlib:libcpmt$(BuildSuffix).lib $(LinkAdditionalOptions) -nodefaultlib:$(LibOutputFile) $(LinkAdditionalOptions) - true - ole32.lib $(LinkAdditionalOptions) true true diff --git a/stl/src/charconv.cpp b/stl/src/charconv.cpp index f400a440854..cae8561b4ba 100644 --- a/stl/src/charconv.cpp +++ b/stl/src/charconv.cpp @@ -1,11 +1,6 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// This must be as small as possible, because its contents are -// injected into the msvcprt.lib and msvcprtd.lib import libraries. -// Do not include or define anything else here. -// In particular, basic_string must not be included here. - #include // Generated by /tools/scripts/charconv_generate.cpp diff --git a/stl/src/filesystem.cpp b/stl/src/filesystem.cpp index ce0819b010b..959f40be8fd 100644 --- a/stl/src/filesystem.cpp +++ b/stl/src/filesystem.cpp @@ -4,11 +4,6 @@ // filesystem.cpp -- C++17 implementation // (see filesys.cpp for implementation) -// This must be as small as possible, because its contents are -// injected into the msvcprt.lib and msvcprtd.lib import libraries. -// Do not include or define anything else here. -// In particular, basic_string must not be included here. - #include #include #include diff --git a/stl/src/format.cpp b/stl/src/format.cpp index 66a999bda33..67a91356a9d 100644 --- a/stl/src/format.cpp +++ b/stl/src/format.cpp @@ -3,11 +3,6 @@ // Implements a win32 API wrapper for -// This must be as small as possible, because its contents are -// injected into the msvcprt.lib and msvcprtd.lib import libraries. -// Do not include or define anything else here. -// In particular, basic_string must not be included here. - #include <__msvc_xlocinfo_types.hpp> #include diff --git a/stl/src/locale0.cpp b/stl/src/locale0.cpp index cc3e7be9bf9..42c67242c65 100644 --- a/stl/src/locale0.cpp +++ b/stl/src/locale0.cpp @@ -3,16 +3,17 @@ // class locale basic member functions +// This file is compiled into the import library (via locale0_implib.cpp => locale0.cpp). +// MAJOR LIMITATIONS apply to what can be included here! +// Before editing this file, read: /docs/import_library.md + +#undef _ENFORCE_ONLY_CORE_HEADERS // TRANSITION, should be a core header + #include #include #include #include -// This must be as small as possible, because its contents are -// injected into the msvcprt.lib and msvcprtd.lib import libraries. -// Do not include or define anything else here. -// In particular, basic_string must not be included here. - // This should probably go to a compiler section just after the locks - unfortunately we have per-appdomain // and per-process variables to initialize #pragma warning(disable : 4073) diff --git a/stl/src/nothrow.cpp b/stl/src/nothrow.cpp index d12dcdcf336..9124a1585a7 100644 --- a/stl/src/nothrow.cpp +++ b/stl/src/nothrow.cpp @@ -11,8 +11,10 @@ #undef MRTDLL #endif -#include -_STD_BEGIN +#include + +#include -const nothrow_t nothrow = nothrow_t(); // define nothrow +_STD_BEGIN +const nothrow_t nothrow = nothrow_t(); _STD_END diff --git a/stl/src/ppltasks.cpp b/stl/src/ppltasks.cpp index 006235d0cd6..721a2ea2941 100644 --- a/stl/src/ppltasks.cpp +++ b/stl/src/ppltasks.cpp @@ -7,6 +7,7 @@ #include +#if defined(_CRT_APP) || defined(UNDOCKED_WINDOWS_UCRT) #ifndef UNDOCKED_WINDOWS_UCRT #pragma warning(push) #pragma warning(disable : 4265) // non-virtual destructor in base class @@ -18,8 +19,7 @@ #include #include #include - -#pragma comment(lib, "ole32") +#endif // This IID is exported by ole32.dll; we cannot depend on ole32.dll on OneCore. static GUID const Local_IID_ICallbackWithNoReentrancyToApplicationSTA = { @@ -219,6 +219,7 @@ namespace Concurrency { _CRTIMP2 void __thiscall _TaskEventLogger::_LogWorkItemCompleted() {} #endif +#if defined(_CRT_APP) || defined(UNDOCKED_WINDOWS_UCRT) using namespace ABI::Windows::Foundation; using namespace ABI::Windows::Foundation::Diagnostics; using namespace Microsoft::WRL; @@ -289,13 +290,6 @@ namespace Concurrency { } _CRTIMP2 bool __cdecl _Task_impl_base::_IsNonBlockingThread() { -// TRANSITION, ABI: This preprocessor directive attempts to fix VSO-1684985 (a bincompat issue affecting VS 2015 code) -// while preserving as much of GH-2654 as possible. When we can break ABI, we should: -// * Remove this preprocessor directive - it should be unnecessary after was changed on 2018-01-12. -// * In , reconsider whether _Task_impl_base::_Wait() should throw invalid_operation; -// it's questionable whether that's conforming, and if users want to block their UI threads, we should let them. -// * Investigate whether we can avoid the ppltasks dependency entirely, making all of these issues irrelevant. -#if defined(_CRT_APP) || defined(UNDOCKED_WINDOWS_UCRT) APTTYPE _AptType; APTTYPEQUALIFIER _AptTypeQualifier; @@ -321,10 +315,28 @@ namespace Concurrency { break; } } -#endif // defined(_CRT_APP) || defined(UNDOCKED_WINDOWS_UCRT) + return false; + } + +#else + _CRTIMP2 void __thiscall _ContextCallback::_CallInContext(_CallbackFunction _Func, bool) const { + _Func(); + } + _CRTIMP2 void __thiscall _ContextCallback::_Capture() {} + + _CRTIMP2 void __thiscall _ContextCallback::_Reset() {} + + _CRTIMP2 void __thiscall _ContextCallback::_Assign(void*) {} + + _CRTIMP2 bool __cdecl _ContextCallback::_IsCurrentOriginSTA() { return false; } + + _CRTIMP2 bool __cdecl _Task_impl_base::_IsNonBlockingThread() { + return false; + } +#endif } // namespace details #ifdef _CRT_APP diff --git a/stl/src/print.cpp b/stl/src/print.cpp index 02ae5d6fa41..74819b8f3b5 100644 --- a/stl/src/print.cpp +++ b/stl/src/print.cpp @@ -3,11 +3,6 @@ // print.cpp -- C++23 implementation -// This must be as small as possible, because its contents are -// injected into the msvcprt.lib and msvcprtd.lib import libraries. -// Do not include or define anything else here. -// In particular, basic_string must not be included here. - #include <__msvc_print.hpp> #include #include diff --git a/stl/src/sharedmutex.cpp b/stl/src/sharedmutex.cpp index d506db98edd..b678cf8c724 100644 --- a/stl/src/sharedmutex.cpp +++ b/stl/src/sharedmutex.cpp @@ -3,11 +3,6 @@ #include -// This must be as small as possible, because its contents are -// injected into the msvcprt.lib and msvcprtd.lib import libraries. -// Do not include or define anything else here. -// In particular, basic_string must not be included here. - // these declarations must be in sync with those in xthreads.h using _Smtx_t = void*; diff --git a/stl/src/stacktrace.cpp b/stl/src/stacktrace.cpp index 6d77f1084c7..7dfae0d0b5d 100644 --- a/stl/src/stacktrace.cpp +++ b/stl/src/stacktrace.cpp @@ -1,11 +1,6 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// This must be as small as possible, because its contents are -// injected into the msvcprt.lib and msvcprtd.lib import libraries. -// Do not include or define anything else here. -// In particular, basic_string must not be included here. - #include #include diff --git a/stl/src/syserror_import_lib.cpp b/stl/src/syserror_import_lib.cpp index 31fdb7573db..7ffc78d7288 100644 --- a/stl/src/syserror_import_lib.cpp +++ b/stl/src/syserror_import_lib.cpp @@ -1,11 +1,6 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// This must be as small as possible, because its contents are -// injected into the msvcprt.lib and msvcprtd.lib import libraries. -// Do not include or define anything else here. -// In particular, basic_string must not be included here. - #include <__msvc_system_error_abi.hpp> #include diff --git a/stl/src/vector_algorithms.cpp b/stl/src/vector_algorithms.cpp index a38d93c281f..7e2979e544e 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -1,11 +1,6 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// This must be as small as possible, because its contents are -// injected into the msvcprt.lib and msvcprtd.lib import libraries. -// Do not include or define anything else here. -// In particular, basic_string must not be included here. - #ifdef _M_CEE_PURE #error _M_CEE_PURE should not be defined when compiling vector_algorithms.cpp. #endif @@ -66,6 +61,19 @@ namespace { void _Advance_bytes(const void*& _Target, ptrdiff_t _Offset) noexcept { _Target = static_cast(_Target) + _Offset; } + + // TRANSITION, DevCom-10331414 + struct [[nodiscard]] _Zeroupper_on_exit { + _Zeroupper_on_exit() = default; + + _Zeroupper_on_exit(const _Zeroupper_on_exit&) = delete; + _Zeroupper_on_exit& operator=(const _Zeroupper_on_exit&) = delete; + + ~_Zeroupper_on_exit() { + _mm256_zeroupper(); + } + }; + } // unnamed namespace extern "C" { @@ -89,6 +97,8 @@ __declspec(noalias) void __cdecl __std_swap_ranges_trivially_swappable_noalias( _Advance_bytes(_First1, 32); _Advance_bytes(_First2, 32); } while (_First1 != _Stop_at); + + _mm256_zeroupper(); // TRANSITION, DevCom-10331414 } constexpr size_t _Mask_16 = ~((static_cast(1) << 4) - 1); @@ -174,6 +184,8 @@ __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_1(void* _Firs _mm256_storeu_si256(static_cast<__m256i*>(_Last), _Left_reversed); _Advance_bytes(_First, 32); } while (_First != _Stop_at); + + _mm256_zeroupper(); // TRANSITION, DevCom-10331414 } if (_Byte_length(_First, _Last) >= 32 && _Use_sse42()) { @@ -214,6 +226,8 @@ __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_2(void* _Firs _mm256_storeu_si256(static_cast<__m256i*>(_Last), _Left_reversed); _Advance_bytes(_First, 32); } while (_First != _Stop_at); + + _mm256_zeroupper(); // TRANSITION, DevCom-10331414 } if (_Byte_length(_First, _Last) >= 32 && _Use_sse42()) { @@ -250,6 +264,8 @@ __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_4(void* _Firs _mm256_storeu_si256(static_cast<__m256i*>(_Last), _Left_reversed); _Advance_bytes(_First, 32); } while (_First != _Stop_at); + + _mm256_zeroupper(); // TRANSITION, DevCom-10331414 } if (_Byte_length(_First, _Last) >= 32 && _Use_sse2()) { @@ -284,6 +300,8 @@ __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_8(void* _Firs _mm256_storeu_si256(static_cast<__m256i*>(_Last), _Left_reversed); _Advance_bytes(_First, 32); } while (_First != _Stop_at); + + _mm256_zeroupper(); // TRANSITION, DevCom-10331414 } if (_Byte_length(_First, _Last) >= 32 && _Use_sse2()) { @@ -320,6 +338,8 @@ __declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_1( _mm256_storeu_si256(static_cast<__m256i*>(_Dest), _Block_reversed); _Advance_bytes(_Dest, 32); } while (_Dest != _Stop_at); + + _mm256_zeroupper(); // TRANSITION, DevCom-10331414 } if (_Byte_length(_First, _Last) >= 16 && _Use_sse42()) { @@ -355,6 +375,8 @@ __declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_2( _mm256_storeu_si256(static_cast<__m256i*>(_Dest), _Block_reversed); _Advance_bytes(_Dest, 32); } while (_Dest != _Stop_at); + + _mm256_zeroupper(); // TRANSITION, DevCom-10331414 } if (_Byte_length(_First, _Last) >= 16 && _Use_sse42()) { @@ -387,6 +409,8 @@ __declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_4( _mm256_storeu_si256(static_cast<__m256i*>(_Dest), _Block_reversed); _Advance_bytes(_Dest, 32); } while (_Dest != _Stop_at); + + _mm256_zeroupper(); // TRANSITION, DevCom-10331414 } if (_Byte_length(_First, _Last) >= 16 && _Use_sse2()) { @@ -417,6 +441,8 @@ __declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_8( _mm256_storeu_si256(static_cast<__m256i*>(_Dest), _Block_reversed); _Advance_bytes(_Dest, 32); } while (_Dest != _Stop_at); + + _mm256_zeroupper(); // TRANSITION, DevCom-10331414 } if (_Byte_length(_First, _Last) >= 16 && _Use_sse2()) { @@ -884,7 +910,8 @@ namespace { _BitScanForward(&_H_pos, _Mask); // lgtm [cpp/conditionallyuninitializedvariable] const auto _V_pos = _Traits::_Get_v_pos(_Cur_idx_min, _H_pos); // Extract its vertical index - _Res._Min = _Base + _V_pos * 16 + _H_pos; // Finally, compute the pointer + _Res._Min = + _Base + static_cast(_V_pos) * 16 + _H_pos; // Finally, compute the pointer } } @@ -930,7 +957,8 @@ namespace { } const auto _V_pos = _Traits::_Get_v_pos(_Cur_idx_max, _H_pos); // Extract its vertical index - _Res._Max = _Base + _V_pos * 16 + _H_pos; // Finally, compute the pointer + _Res._Max = + _Base + static_cast(_V_pos) * 16 + _H_pos; // Finally, compute the pointer } } // Horizontal part done, results are saved, now need to see if there is another portion to process @@ -1200,6 +1228,8 @@ namespace { template const void* __stdcall __std_find_trivial_unsized(const void* _First, const _Ty _Val) noexcept { if (_Use_avx2()) { + _Zeroupper_on_exit _Guard; // TRANSITION, DevCom-10331414 + // We read by vector-sized pieces, and we align pointers to vector-sized boundary. // From start partial piece we mask out matches that don't belong to the range. // This makes sure we never cross page boundary, thus we read 'as if' sequentially. @@ -1282,6 +1312,8 @@ namespace { const size_t _Avx_size = _Size_bytes & ~size_t{0x1F}; if (_Avx_size != 0 && _Use_avx2()) { + _Zeroupper_on_exit _Guard; // TRANSITION, DevCom-10331414 + const __m256i _Comparand = _Traits::_Set_avx(_Val); const void* _Stop_at = _First; _Advance_bytes(_Stop_at, _Avx_size); @@ -1341,6 +1373,8 @@ namespace { _Advance_bytes(_First, 32); } while (_First != _Stop_at); _Size_bytes &= 0x1F; + + _mm256_zeroupper(); // TRANSITION, DevCom-10331414 } const size_t _Sse_size = _Size_bytes & ~size_t{0xF}; diff --git a/stl/src/xcharconv_ryu_tables.cpp b/stl/src/xcharconv_ryu_tables.cpp index f116ddbe111..e8a7dd260d5 100644 --- a/stl/src/xcharconv_ryu_tables.cpp +++ b/stl/src/xcharconv_ryu_tables.cpp @@ -30,11 +30,6 @@ // DEALINGS IN THE SOFTWARE. -// This must be as small as possible, because its contents are -// injected into the msvcprt.lib and msvcprtd.lib import libraries. -// Do not include or define anything else here. -// In particular, basic_string must not be included here. - #include namespace std { diff --git a/stl/src/xcharconv_tables_double.cpp b/stl/src/xcharconv_tables_double.cpp index 00f53c5bc8a..a9f348ab5ab 100644 --- a/stl/src/xcharconv_tables_double.cpp +++ b/stl/src/xcharconv_tables_double.cpp @@ -1,11 +1,6 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// This must be as small as possible, because its contents are -// injected into the msvcprt.lib and msvcprtd.lib import libraries. -// Do not include or define anything else here. -// In particular, basic_string must not be included here. - #include namespace std { diff --git a/stl/src/xcharconv_tables_float.cpp b/stl/src/xcharconv_tables_float.cpp index abe60e416c1..c68de40447d 100644 --- a/stl/src/xcharconv_tables_float.cpp +++ b/stl/src/xcharconv_tables_float.cpp @@ -1,11 +1,6 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// This must be as small as possible, because its contents are -// injected into the msvcprt.lib and msvcprtd.lib import libraries. -// Do not include or define anything else here. -// In particular, basic_string must not be included here. - #include namespace std { diff --git a/stl/src/xonce2.cpp b/stl/src/xonce2.cpp index 17c9a3c3a76..e90c6236025 100644 --- a/stl/src/xonce2.cpp +++ b/stl/src/xonce2.cpp @@ -6,11 +6,6 @@ #include #include -// This must be as small as possible, because its contents are -// injected into the msvcprt.lib and msvcprtd.lib import libraries. -// Do not include or define anything else here. -// In particular, basic_string must not be included here. - // Provides forwarders for InitOnceBeginInitialize and InitOnceComplete for // environments that can't use /ALTERNATENAME. // They were originally specific to /clr but are now used in other scenarios. diff --git a/tests/std/include/test_header_units_and_modules.hpp b/tests/std/include/test_header_units_and_modules.hpp index ebd07763d14..f439720a118 100644 --- a/tests/std/include/test_header_units_and_modules.hpp +++ b/tests/std/include/test_header_units_and_modules.hpp @@ -661,7 +661,7 @@ constexpr bool impl_test_source_location() { using namespace std; const auto sl = source_location::current(); assert(sl.line() == __LINE__ - 1); -#ifdef _MSVC_INTERNAL_TESTING // TRANSITION, VS 2022 17.6 Preview 2 +#if defined(_MSVC_INTERNAL_TESTING) || _MSC_FULL_VER >= 193632502 // TRANSITION, VS 2022 17.6 Preview 2 assert(sl.column() == 38); #else // ^^^ no workaround / workaround vvv assert(sl.column() == 1); diff --git a/tests/std/include/test_min_max_element_support.hpp b/tests/std/include/test_min_max_element_support.hpp new file mode 100644 index 00000000000..2d764b3dbf0 --- /dev/null +++ b/tests/std/include/test_min_max_element_support.hpp @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once + +#include +#include +#include +#include +#include + +#ifdef __cpp_lib_concepts +#include +#endif + +template +FwdIt last_known_good_min_element(FwdIt first, FwdIt last) { + FwdIt result = first; + + for (; first != last; ++first) { + if (*first < *result) { + result = first; + } + } + + return result; +} + +template +FwdIt last_known_good_max_element(FwdIt first, FwdIt last) { + FwdIt result = first; + + for (; first != last; ++first) { + if (*result < *first) { + result = first; + } + } + + return result; +} + +template +std::pair last_known_good_minmax_element(FwdIt first, FwdIt last) { + // find smallest and largest elements + std::pair found(first, first); + + if (first != last) { + while (++first != last) { // process one or two elements + FwdIt next = first; + if (++next == last) { // process last element + if (*first < *found.first) { + found.first = first; + } else if (!(*first < *found.second)) { + found.second = first; + } + } else { // process next two elements + if (*next < *first) { // test next for new smallest + if (*next < *found.first) { + found.first = next; + } + + if (!(*first < *found.second)) { + found.second = first; + } + } else { // test first for new smallest + if (*first < *found.first) { + found.first = first; + } + + if (!(*next < *found.second)) { + found.second = next; + } + } + first = next; + } + } + } + + return found; +} + +template +void test_case_min_max_element(const std::vector& input) { + auto expected_min = last_known_good_min_element(input.begin(), input.end()); + auto expected_max = last_known_good_max_element(input.begin(), input.end()); + auto expected_minmax = last_known_good_minmax_element(input.begin(), input.end()); + auto actual_min = std::min_element(input.begin(), input.end()); + auto actual_max = std::max_element(input.begin(), input.end()); + auto actual_minmax = std::minmax_element(input.begin(), input.end()); + assert(expected_min == actual_min); + assert(expected_max == actual_max); + assert(expected_minmax == actual_minmax); +#ifdef __cpp_lib_concepts + using std::ranges::views::take, std::ptrdiff_t; + + auto actual_min_range = std::ranges::min_element(input); + auto actual_max_range = std::ranges::max_element(input); + auto actual_minmax_range = std::ranges::minmax_element(input); + auto actual_min_sized_range = std::ranges::min_element(take(input, static_cast(input.size()))); + auto actual_max_sized_range = std::ranges::max_element(take(input, static_cast(input.size()))); + auto actual_minmax_sized_range = std::ranges::minmax_element(take(input, static_cast(input.size()))); + assert(expected_min == actual_min_range); + assert(expected_max == actual_max_range); + assert(expected_minmax.first == actual_minmax_range.min); + assert(expected_minmax.second == actual_minmax_range.max); + assert(expected_min == actual_min_sized_range); + assert(expected_max == actual_max_sized_range); + assert(expected_minmax.first == actual_minmax_sized_range.min); + assert(expected_minmax.second == actual_minmax_sized_range.max); +#endif // __cpp_lib_concepts +} diff --git a/tests/std/test.lst b/tests/std/test.lst index a6bbfee0801..88d477d8777 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -220,6 +220,7 @@ tests\GH_003022_substr_allocator tests\GH_003105_piecewise_densities tests\GH_003119_error_category_ctor tests\GH_003246_cmath_narrowing +tests\GH_003617_vectorized_meow_element tests\LWG2597_complex_branch_cut tests\LWG3018_shared_ptr_function tests\LWG3121_constrained_tuple_forwarding_ctor @@ -584,6 +585,8 @@ tests\P2321R2_views_adjacent_transform tests\P2321R2_views_zip tests\P2321R2_views_zip_transform tests\P2322R6_ranges_alg_fold +tests\P2374R4_views_cartesian_product +tests\P2374R4_views_cartesian_product_death tests\P2387R3_bind_back tests\P2387R3_pipe_support_for_user_defined_range_adaptors tests\P2401R0_conditional_noexcept_for_exchange diff --git a/tests/std/tests/Dev09_056375_locale_cleanup/custom_format.py b/tests/std/tests/Dev09_056375_locale_cleanup/custom_format.py index 1792181ec4c..39f2c05c22e 100644 --- a/tests/std/tests/Dev09_056375_locale_cleanup/custom_format.py +++ b/tests/std/tests/Dev09_056375_locale_cleanup/custom_format.py @@ -11,7 +11,7 @@ def getBuildSteps(self, test, litConfig, shared): exeSource = test.getSourcePath() dllSource = os.path.join(os.path.dirname(exeSource), 'TestDll.cpp') - outputDir, outputBase = test.getTempPaths() + outputDir, _ = test.getTempPaths() dllOutput = os.path.join(outputDir, 'TestDll.DLL') cmd = [test.cxx, dllSource, *test.flags, *test.compileFlags, '/Fe' + dllOutput, diff --git a/tests/std/tests/Dev09_172666_tr1_tuple_odr/custom_format.py b/tests/std/tests/Dev09_172666_tr1_tuple_odr/custom_format.py index fa2efc4372f..910e2111fb6 100644 --- a/tests/std/tests/Dev09_172666_tr1_tuple_odr/custom_format.py +++ b/tests/std/tests/Dev09_172666_tr1_tuple_odr/custom_format.py @@ -12,7 +12,7 @@ def getBuildSteps(self, test, litConfig, shared): exeSource = test.getSourcePath() test2Source = os.path.join(os.path.dirname(exeSource), 'test2.cpp') - outputDir, outputBase = test.getTempPaths() + _, outputBase = test.getTempPaths() if TestType.COMPILE in test.testType: cmd = [test.cxx, '/c', exeSource, test2Source, *test.flags, *test.compileFlags] diff --git a/tests/std/tests/GH_000431_equal_memcmp_is_safe/test.compile.pass.cpp b/tests/std/tests/GH_000431_equal_memcmp_is_safe/test.compile.pass.cpp index 7d1a69b6dd8..5159486e50b 100644 --- a/tests/std/tests/GH_000431_equal_memcmp_is_safe/test.compile.pass.cpp +++ b/tests/std/tests/GH_000431_equal_memcmp_is_safe/test.compile.pass.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp b/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp index 84aabcc3414..2d0e1b77179 100644 --- a/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp +++ b/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/tests/std/tests/GH_001411_core_headers/test.cpp b/tests/std/tests/GH_001411_core_headers/test.cpp index f2004ac7b63..0d5907989c5 100644 --- a/tests/std/tests/GH_001411_core_headers/test.cpp +++ b/tests/std/tests/GH_001411_core_headers/test.cpp @@ -15,6 +15,7 @@ #include #if _HAS_CXX17 +#include #include #endif // _HAS_CXX17 diff --git a/tests/std/tests/GH_003617_vectorized_meow_element/env.lst b/tests/std/tests/GH_003617_vectorized_meow_element/env.lst new file mode 100644 index 00000000000..288bc01fbe0 --- /dev/null +++ b/tests/std/tests/GH_003617_vectorized_meow_element/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\fast_matrix.lst diff --git a/tests/std/tests/GH_003617_vectorized_meow_element/test.cpp b/tests/std/tests/GH_003617_vectorized_meow_element/test.cpp new file mode 100644 index 00000000000..5d2378150b5 --- /dev/null +++ b/tests/std/tests/GH_003617_vectorized_meow_element/test.cpp @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// REQUIRES: x64 + +#ifdef _M_X64 + +#include +#include +#include + +#include "test_min_max_element_support.hpp" + +using namespace std; + +extern "C" long __isa_enabled; + +void disable_instructions(ISA_AVAILABILITY isa) { + __isa_enabled &= ~(1UL << static_cast(isa)); +} + +void test_gh_3617() { + // Test GH-3617 ": Silent bad codegen for vectorized meow_element() above 4 GB". + constexpr size_t n = 0x4000'0010; + + vector v(n, 25); + v[n - 2] = 24; + v[n - 1] = 26; + + test_case_min_max_element(v); +} + +int main() { + test_gh_3617(); + + disable_instructions(__ISA_AVAILABLE_AVX2); + test_gh_3617(); + + disable_instructions(__ISA_AVAILABLE_SSE42); + test_gh_3617(); +} +#else // ^^^ x64 / other architectures vvv +int main() {} +#endif // ^^^ other architectures ^^^ diff --git a/tests/std/tests/P0607R0_inline_variables/custom_format.py b/tests/std/tests/P0607R0_inline_variables/custom_format.py index fa2efc4372f..910e2111fb6 100644 --- a/tests/std/tests/P0607R0_inline_variables/custom_format.py +++ b/tests/std/tests/P0607R0_inline_variables/custom_format.py @@ -12,7 +12,7 @@ def getBuildSteps(self, test, litConfig, shared): exeSource = test.getSourcePath() test2Source = os.path.join(os.path.dirname(exeSource), 'test2.cpp') - outputDir, outputBase = test.getTempPaths() + _, outputBase = test.getTempPaths() if TestType.COMPILE in test.testType: cmd = [test.cxx, '/c', exeSource, test2Source, *test.flags, *test.compileFlags] diff --git a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp index 3f77e227e47..98f05c4f4a9 100644 --- a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp @@ -121,6 +121,25 @@ STATIC_ASSERT(test_cpo(ranges::views::take_while)); STATIC_ASSERT(test_cpo(ranges::views::transform)); STATIC_ASSERT(test_cpo(ranges::views::values)); +#if _HAS_CXX23 +STATIC_ASSERT(test_cpo(ranges::views::adjacent<3>)); +STATIC_ASSERT(test_cpo(ranges::views::adjacent_transform<3>)); +STATIC_ASSERT(test_cpo(ranges::views::as_const)); +STATIC_ASSERT(test_cpo(ranges::views::as_rvalue)); +STATIC_ASSERT(test_cpo(ranges::views::cartesian_product)); +STATIC_ASSERT(test_cpo(ranges::views::chunk)); +STATIC_ASSERT(test_cpo(ranges::views::chunk_by)); +STATIC_ASSERT(test_cpo(ranges::views::enumerate)); +STATIC_ASSERT(test_cpo(ranges::views::join_with)); +STATIC_ASSERT(test_cpo(ranges::views::pairwise)); +STATIC_ASSERT(test_cpo(ranges::views::pairwise_transform)); +STATIC_ASSERT(test_cpo(ranges::views::repeat)); +STATIC_ASSERT(test_cpo(ranges::views::slide)); +STATIC_ASSERT(test_cpo(ranges::views::stride)); +STATIC_ASSERT(test_cpo(ranges::views::zip)); +STATIC_ASSERT(test_cpo(ranges::views::zip_transform)); +#endif // _HAS_CXX23 + void test_cpo_ambiguity() { using namespace std::ranges; diff --git a/tests/std/tests/P0896R4_ranges_ref_view/test.cpp b/tests/std/tests/P0896R4_ranges_ref_view/test.cpp index 8eaa06f81f7..3cc6b2b5e40 100644 --- a/tests/std/tests/P0896R4_ranges_ref_view/test.cpp +++ b/tests/std/tests/P0896R4_ranges_ref_view/test.cpp @@ -86,6 +86,26 @@ struct instantiator { STATIC_ASSERT(noexcept(as_const(test_view).end()) == noexcept(wrapped_input.end())); } +#if _HAS_CXX23 + if constexpr (ranges::input_range) { // const iterators (from view_interface) + R wrapped_input{input}; + ref_view test_view{wrapped_input}; + const same_as> auto cfirst = as_const(test_view).cbegin(); + if constexpr (_Is_specialization_v, basic_const_iterator>) { + assert(cfirst.base().peek() == begin(input)); + } else { + assert(cfirst.peek() == begin(input)); + } + + const same_as> auto clast = as_const(test_view).cend(); + if constexpr (_Is_specialization_v, basic_const_iterator>) { + assert(clast.base().peek() == end(input)); + } else { + assert(clast.peek() == end(input)); + } + } +#endif // _HAS_CXX23 + { // state STATIC_ASSERT(can_size> == ranges::sized_range); if constexpr (ranges::sized_range) { diff --git a/tests/std/tests/P0896R4_ranges_subrange/test.compile.pass.cpp b/tests/std/tests/P0896R4_ranges_subrange/test.compile.pass.cpp index d35611879fe..e3b810556a9 100644 --- a/tests/std/tests/P0896R4_ranges_subrange/test.compile.pass.cpp +++ b/tests/std/tests/P0896R4_ranges_subrange/test.compile.pass.cpp @@ -1036,6 +1036,15 @@ namespace test_subrange { STATIC_ASSERT(HasMemberEmpty); STATIC_ASSERT(!copyable || range); +#if _HAS_CXX23 // Validate cbegin/cend + if constexpr (ranges::input_range) { + STATIC_ASSERT(CanMemberCBegin); + STATIC_ASSERT(CanMemberCBegin == ranges::input_range); + STATIC_ASSERT(CanMemberCEnd); + STATIC_ASSERT(CanMemberCEnd == ranges::input_range); + } +#endif // _HAS_CXX23 + // Validate size STATIC_ASSERT(sized == HasMemberSize); diff --git a/tests/std/tests/P0896R4_views_common/test.cpp b/tests/std/tests/P0896R4_views_common/test.cpp index 5f5ebf7c66d..e3ea2748a91 100644 --- a/tests/std/tests/P0896R4_views_common/test.cpp +++ b/tests/std/tests/P0896R4_views_common/test.cpp @@ -63,6 +63,46 @@ void non_literal_parts(R& r, E& expected) { } } } + +#if _HAS_CXX23 + using ranges::const_iterator_t; + + const same_as> auto cfirst = r.cbegin(); + if (!is_empty) { + assert(*cfirst == *begin(expected)); + } + + if constexpr (copyable) { + auto r2 = r; + const same_as> auto cfirst2 = r2.cbegin(); + if (!is_empty) { + assert(*cfirst2 == *cfirst); + } + } + + if constexpr (CanCBegin) { + const same_as> auto cfirst3 = as_const(r).cbegin(); + if (!is_empty) { + assert(*cfirst3 == *cfirst); + } + } + + const same_as> auto clast = r.cend(); + if constexpr (bidirectional_range) { + if (!is_empty) { + assert(*prev(clast) == *prev(end(expected))); + } + } + + if constexpr (CanCEnd) { + const same_as> auto clast2 = as_const(r).cend(); + if constexpr (bidirectional_range) { + if (!is_empty) { + assert(*prev(clast2) == *prev(end(expected))); + } + } + } +#endif // _HAS_CXX23 } template diff --git a/tests/std/tests/P0896R4_views_filter/test.cpp b/tests/std/tests/P0896R4_views_filter/test.cpp index d1520701573..787c247c425 100644 --- a/tests/std/tests/P0896R4_views_filter/test.cpp +++ b/tests/std/tests/P0896R4_views_filter/test.cpp @@ -201,6 +201,54 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(!CanEnd); } +#if _HAS_CXX23 + using ranges::const_iterator_t, ranges::const_sentinel_t; + + // Validate view_interface::cbegin + STATIC_ASSERT(CanMemberCBegin); + if (forward_range) { // intentionally not if constexpr + // Ditto "let's make some extra calls because memoization" + const same_as> auto ci = r.cbegin(); + if (!is_empty) { + assert(*ci == *begin(expected)); + } + assert(*r.cbegin() == *begin(expected)); + assert(*r.cbegin() == *begin(expected)); + + if constexpr (copy_constructible) { + auto r2 = r; + const same_as> auto ci2 = r2.cbegin(); + assert(*r2.cbegin() == *ci2); + assert(*r2.cbegin() == *ci2); + if (!is_empty) { + assert(*ci2 == *ci); + } + } + + STATIC_ASSERT(!CanMemberCBegin); + } + + // Validate view_interface::cend + STATIC_ASSERT(CanMemberCEnd); + if (!is_empty) { + if constexpr (common_range) { + same_as> auto ci = r.cend(); + if constexpr (bidirectional_range) { + assert(*prev(ci) == *prev(end(expected))); + } + } else { + [[maybe_unused]] same_as> auto cs = r.cend(); + } + + if constexpr (bidirectional_range && common_range && copy_constructible) { + auto r2 = r; + assert(*prev(r2.cend()) == *prev(end(expected))); + } + + STATIC_ASSERT(!CanMemberCEnd); + } +#endif // _HAS_CXX23 + // Validate view_interface::data STATIC_ASSERT(!CanData); STATIC_ASSERT(!CanData); diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 96d87e350f9..c5280696def 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -215,6 +215,77 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { } } +#if _HAS_CXX23 + using ranges::const_iterator_t, ranges::const_sentinel_t; + + // Validate view_interface::cbegin + static_assert(CanMemberCBegin); + static_assert(CanMemberCBegin + == (forward_range && is_reference_v> + && input_range>) ); + if (forward_range) { // intentionally not if constexpr + const same_as> auto ci = r.cbegin(); + if (!is_empty) { + assert(*ci == *begin(expected)); + } + + if constexpr (copyable) { + auto r2 = r; + const same_as> auto ci2 = r2.cbegin(); + if (!is_empty) { + assert(*ci2 == *ci); + } + } + + static_assert(CanMemberCBegin == CanCBegin); + if constexpr (CanMemberCBegin) { + const same_as> auto ci2 = as_const(r).cbegin(); + if (!is_empty) { + assert(*ci2 == *ci); + } + + if constexpr (copyable) { + const auto r2 = r; + const same_as> auto ci3 = r2.cbegin(); + if (!is_empty) { + assert(*ci3 == *ci); + } + } + } + } + + // Validate view_interface::cend + static_assert(CanMemberCEnd); + static_assert(CanMemberCEnd + == (forward_range && is_reference_v> + && input_range>) ); + const same_as> auto cs = r.end(); + if (!is_empty) { + if constexpr (bidirectional_range && common_range) { + assert(*prev(cs) == *prev(end(expected))); + + if constexpr (copyable) { + auto r2 = r; + assert(*prev(r2.cend()) == *prev(end(expected))); + } + } + + static_assert(CanMemberCEnd == CanCEnd); + if constexpr (CanMemberCEnd) { + const same_as> auto cs2 = as_const(r).cend(); + if constexpr (bidirectional_range && common_range) { + assert(*prev(cs2) == *prev(end(expected))); + + if constexpr (copyable) { + const auto r2 = r; + const same_as> auto cs3 = r2.cend(); + assert(*prev(cs3) == *prev(end(expected))); + } + } + } + } +#endif // _HAS_CXX23 + // Validate view_interface::data static_assert(!CanData); static_assert(!CanData); diff --git a/tests/std/tests/P0896R4_views_reverse/test.cpp b/tests/std/tests/P0896R4_views_reverse/test.cpp index ba624f3d4d0..60ae11572b2 100644 --- a/tests/std/tests/P0896R4_views_reverse/test.cpp +++ b/tests/std/tests/P0896R4_views_reverse/test.cpp @@ -230,6 +230,64 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { } } +#if _HAS_CXX23 + // Validate view_interface::cbegin + static_assert(CanMemberCBegin); + { + // Ditto "let's make some extra calls because reverse_view sometimes caches begin" + const same_as>>> auto ci = r.cbegin(); + if (!is_empty) { + assert(*ci == *begin(expected)); + } + + if constexpr (copyable) { + auto r2 = r; + const same_as>>> auto ci2 = r2.cbegin(); + assert(r2.cbegin() == ci2); + assert(r2.cbegin() == ci2); + if (!is_empty) { + assert(*ci2 == *ci); + } + } + + static_assert(CanMemberCBegin == common_range); + if constexpr (CanMemberCBegin) { + const same_as>>> auto ci3 = as_const(r).cbegin(); + assert(as_const(r).cbegin() == ci3); + assert(as_const(r).cbegin() == ci3); + if (!is_empty) { + assert(*ci3 == *ci); + } + + if constexpr (copyable) { + const auto r2 = r; + const same_as>>> auto ci4 = r2.cbegin(); + assert(r2.cbegin() == ci4); + assert(r2.cbegin() == ci4); + if (!is_empty) { + assert(*ci4 == *ci); + } + } + } + } + + // Validate view_interface::cend + static_assert(CanMemberCEnd); + if (!is_empty) { + assert(*prev(r.cend()) == *prev(end(expected))); + + if constexpr (copyable) { + auto r2 = r; + assert(*prev(r2.cend()) == *prev(end(expected))); + } + + static_assert(CanMemberCEnd == common_range); + if constexpr (CanMemberCEnd) { + assert(*prev(as_const(r).cend()) == *prev(end(expected))); + } + } +#endif // _HAS_CXX23 + // Validate view_interface::data static_assert(!CanData); static_assert(!CanData); diff --git a/tests/std/tests/P0896R4_views_transform/test.cpp b/tests/std/tests/P0896R4_views_transform/test.cpp index 927049c307f..340c07dd3f1 100644 --- a/tests/std/tests/P0896R4_views_transform/test.cpp +++ b/tests/std/tests/P0896R4_views_transform/test.cpp @@ -250,6 +250,54 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { } } +#if _HAS_CXX23 + using ranges::const_iterator_t, ranges::const_sentinel_t; + + // Validate view_interface::cbegin + STATIC_ASSERT(CanMemberCBegin); + STATIC_ASSERT(CanMemberCBegin == (range && const_invocable)); + if (forward_range) { // intentionally not if constexpr + const same_as> auto ci = r.cbegin(); + if (!is_empty) { + assert(*ci == *begin(expected)); + } + + if constexpr (copy_constructible) { + auto r2 = r; + const same_as> auto ci2 = r2.cbegin(); + if (!is_empty) { + assert(*ci2 == *ci); + } + } + + if constexpr (CanMemberCBegin) { + const same_as> auto ci3 = as_const(r).cbegin(); + if (!is_empty) { + assert(*ci3 == *ci); + } + } + } + + // Validate view_interface::cend + STATIC_ASSERT(CanMemberCEnd); + STATIC_ASSERT(CanMemberCEnd == (range && const_invocable)); + if (!is_empty) { + same_as> auto cs = r.cend(); + STATIC_ASSERT(is_same_v, const_iterator_t> == common_range); + if constexpr (bidirectional_range && common_range) { + assert(*prev(cs) == *prev(end(expected))); + } + + if constexpr (CanMemberCEnd) { + same_as> auto cs2 = as_const(r).cend(); + STATIC_ASSERT(is_same_v, const_iterator_t> == common_range); + if constexpr (bidirectional_range && common_range) { + assert(*prev(cs2) == *prev(end(expected))); + } + } + } +#endif // _HAS_CXX23 + // Validate view_interface::data STATIC_ASSERT(!CanData); STATIC_ASSERT(!CanData); diff --git a/tests/std/tests/P2374R4_views_cartesian_product/env.lst b/tests/std/tests/P2374R4_views_cartesian_product/env.lst new file mode 100644 index 00000000000..8ac7033b206 --- /dev/null +++ b/tests/std/tests/P2374R4_views_cartesian_product/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_latest_matrix.lst diff --git a/tests/std/tests/P2374R4_views_cartesian_product/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp new file mode 100644 index 00000000000..1c9f016d171 --- /dev/null +++ b/tests/std/tests/P2374R4_views_cartesian_product/test.cpp @@ -0,0 +1,1032 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; +using ranges::bidirectional_range, ranges::random_access_range, ranges::sized_range, ranges::common_range, + ranges::iterator_t; + +template +using maybe_const = conditional_t; + +// Helper concepts from [range.cartesian.view] +template +concept CartesianProductIsRandomAccess = (random_access_range> && ... + && (random_access_range> + && sized_range>) ); + +template +concept CartesianProductCommonArg = common_range || (sized_range && random_access_range); + +template +concept CartesianProductIsBidirectional = (bidirectional_range> && ... + && (bidirectional_range> + && CartesianProductCommonArg>) ); + +template +concept CartesianProductIsCommon = CartesianProductCommonArg; + +template +concept CartesianProductIsSized = (sized_range && ...); + +template class FirstSent, class First, class... Rest> +concept CartesianIsSizedSentinel = + (sized_sentinel_for>, iterator_t>> && ... + && (sized_range> + && sized_sentinel_for>, iterator_t>>) ); + +template +concept CanViewCartesianProduct = requires(Ranges&&... rs) { views::cartesian_product(forward(rs)...); }; + +template +concept UnsignedIntegerLike = _Integer_like && (!_Signed_integer_like); + +template +constexpr bool is_iter_move_nothrow() { + constexpr bool is_inner_iter_move_nothrow = + (noexcept(ranges::iter_move(declval&>())) + && ... // + && noexcept(ranges::iter_move(declval&>()))); + constexpr bool are_references_nothrow_movable = + conjunction_v>, + is_nothrow_move_constructible>...>; + return is_inner_iter_move_nothrow && are_references_nothrow_movable; +} + +template +constexpr bool is_iter_swap_nothrow() { + return (noexcept(ranges::iter_swap(declval&>(), declval&>())) + && ... // + && noexcept(ranges::iter_swap(declval&>(), declval&>()))); +} + +template +constexpr bool test_one(Expected&& expected_range, First&& first, Rest&&... rest) { + using ranges::cartesian_product_view, ranges::view, ranges::input_range, ranges::forward_range, ranges::range, + ranges::range_value_t, ranges::range_reference_t, ranges::range_rvalue_reference_t, ranges::range_difference_t, + ranges::sentinel_t, ranges::prev, ranges::const_iterator_t, ranges::const_sentinel_t; + using views::all_t; + + using VFirst = all_t; + using R = cartesian_product_view...>; + + constexpr bool is_view = (view> && ... && view>); + constexpr bool all_copy_constructible = (copy_constructible && ... && copy_constructible>); + constexpr bool is_bidirectional = CartesianProductIsBidirectional...>; + constexpr bool is_const_bidirectional = CartesianProductIsBidirectional...>; + constexpr bool is_random_access = CartesianProductIsRandomAccess...>; + constexpr bool is_const_random_access = CartesianProductIsRandomAccess...>; + constexpr bool is_sized = CartesianProductIsSized...>; + constexpr bool is_const_sized = CartesianProductIsSized...>; + constexpr bool is_common = CartesianProductIsCommon...>; + constexpr bool is_const_common = CartesianProductIsCommon...>; + + STATIC_ASSERT(view); + STATIC_ASSERT(input_range); + STATIC_ASSERT(forward_range == forward_range); + STATIC_ASSERT(bidirectional_range == is_bidirectional); + STATIC_ASSERT(random_access_range == is_random_access); + STATIC_ASSERT(!ranges::contiguous_range); + STATIC_ASSERT(sized_range == is_sized); + STATIC_ASSERT(common_range == is_common); + + // Check non-default-initializability + STATIC_ASSERT(is_default_constructible_v + == (is_default_constructible_v && ... && is_default_constructible_v>) ); + + // Check borrowed_range + static_assert(!ranges::borrowed_range); + + // Check range closure object + constexpr auto closure = views::cartesian_product; + + // ... with lvalue argument + STATIC_ASSERT(CanViewCartesianProduct + == (!is_view || (copy_constructible && ... && copy_constructible>) )); + if constexpr (CanViewCartesianProduct) { + constexpr bool is_noexcept = + !is_view + || (is_nothrow_copy_constructible_v && ... && is_nothrow_copy_constructible_v>); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(first, rest...)) == is_noexcept); + } + + // ... with const lvalue argument + STATIC_ASSERT(CanViewCartesianProduct&, const remove_reference_t&...> + == (!is_view || (copy_constructible && ... && copy_constructible>) )); + if constexpr (CanViewCartesianProduct&, const remove_reference_t&...>) { + using RC = + cartesian_product_view&>, all_t&>...>; + constexpr bool is_noexcept = + !is_view + || (is_nothrow_copy_constructible_v && ... && is_nothrow_copy_constructible_v>); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(as_const(first), as_const(rest)...)) == is_noexcept); + } + + // ... with rvalue argument + STATIC_ASSERT(CanViewCartesianProduct, remove_reference_t...> + == (is_view || (movable> && ... && movable>) )); + if constexpr (CanViewCartesianProduct, remove_reference_t...>) { + using RS = cartesian_product_view>, all_t>...>; + constexpr bool is_noexcept = + (is_nothrow_move_constructible_v && ... && is_nothrow_move_constructible_v>); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(move(first), move(rest)...)) == is_noexcept); + } + + // ... with const rvalue argument + STATIC_ASSERT(CanViewCartesianProduct, const remove_reference_t...> + == (is_view && (copy_constructible && ... && copy_constructible>) )); + if constexpr (CanViewCartesianProduct, const remove_reference_t...>) { + constexpr bool is_noexcept = + (is_nothrow_copy_constructible_v && ... && is_nothrow_copy_constructible_v>); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(closure(move(as_const(first)), move(as_const(rest))...)) == is_noexcept); + } + + // Check deduction guide + same_as auto r = cartesian_product_view{forward(first), forward(rest)...}; + + // Check cartesian_product_view::size + STATIC_ASSERT(CanMemberSize == is_sized); + if constexpr (CanMemberSize) { + UnsignedIntegerLike auto s = r.size(); + assert(s == ranges::size(expected_range)); + } + + // Check cartesian_product_view::size (const) + STATIC_ASSERT(CanMemberSize == is_const_sized); + if constexpr (CanMemberSize) { + UnsignedIntegerLike auto s = as_const(r).size(); + assert(s == ranges::size(expected_range)); + } + + const bool is_empty = ranges::empty(expected_range); + + // Check view_interface::empty and operator bool + STATIC_ASSERT(CanMemberEmpty == (forward_range || is_sized)); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); + } + + // Check view_interface::empty and operator bool (const) + STATIC_ASSERT(CanMemberEmpty == (forward_range || is_const_sized)); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(as_const(r).empty() == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + } + + assert(ranges::equal(r, expected_range)); + if (!forward_range) { // intentionally not if constexpr + return true; + } + + // Check cartesian_product_view::begin + STATIC_ASSERT(CanMemberBegin); + { + const same_as> auto i = r.begin(); + if (!is_empty) { + assert(*i == *begin(expected_range)); + } + + if constexpr (all_copy_constructible) { + auto r2 = r; + const same_as> auto i2 = r2.begin(); + if (!is_empty) { + assert(*i2 == *i); + } + } + } + + // Check cartesian_product_view::begin (const) + STATIC_ASSERT(CanMemberBegin == (range && ... && range>) ); + if constexpr (CanMemberBegin) { + const same_as> auto ci = as_const(r).begin(); + if (!is_empty) { + assert(*ci == *begin(expected_range)); + } + + if constexpr (all_copy_constructible) { + const auto r2 = r; + const same_as> auto ci2 = r2.begin(); + if (!is_empty) { + assert(*ci2 == *ci); + } + } + } + + // Check cartesian_product_view::end + STATIC_ASSERT(CanMemberEnd); + { + const same_as> auto s = r.end(); + assert((r.begin() == s) == is_empty); + STATIC_ASSERT(common_range == is_common); + + if constexpr (same_as, default_sentinel_t>) { + STATIC_ASSERT(!is_common); + STATIC_ASSERT(noexcept(r.end())); + } else if constexpr (common_range && is_bidirectional) { + if (!is_empty) { + assert(*prev(s) == *prev(end(expected_range))); + } + + if constexpr (all_copy_constructible) { + auto r2 = r; + if (!is_empty) { + assert(*prev(r2.end()) == *prev(end(expected_range))); + } + } + } + } + + // Check cartesian_product_view::end (const) + STATIC_ASSERT(CanMemberEnd); + if constexpr (CanMemberEnd) { + const same_as> auto cs = as_const(r).end(); + assert((r.begin() == cs) == is_empty); + STATIC_ASSERT(common_range == is_const_common); + + if constexpr (same_as, default_sentinel_t>) { + STATIC_ASSERT(!is_const_common); + STATIC_ASSERT(noexcept(as_const(r).end())); + } else if constexpr (common_range && is_const_bidirectional) { + if (!is_empty) { + assert(*prev(cs) == *prev(end(expected_range))); + } + + if constexpr (all_copy_constructible) { + const auto r2 = r; + if (!is_empty) { + assert(*prev(r2.end()) == *prev(end(expected_range))); + } + } + } + } + + // Check view_interface::cbegin + STATIC_ASSERT(CanMemberCBegin); + STATIC_ASSERT(CanMemberCBegin == (range && ... && range>) ); + { + const same_as> auto i = r.cbegin(); + if (!is_empty) { + assert(*i == *cbegin(expected_range)); + } + + if constexpr (all_copy_constructible) { + auto r2 = r; + const same_as> auto i2 = r2.cbegin(); + if (!is_empty) { + assert(*i2 == *i); + } + } + + if constexpr (CanCBegin) { + const same_as> auto i3 = as_const(r).cbegin(); + if (!is_empty) { + assert(*i3 == *i); + } + } + } + + // Check view_interface::cend + STATIC_ASSERT(CanMemberCEnd); + STATIC_ASSERT(CanMemberCEnd); + if (!is_empty) { + same_as> auto i = r.cend(); + if constexpr (common_range && is_bidirectional) { + assert(*prev(i) == *prev(cend(expected_range))); + } + + if constexpr (CanCEnd) { + same_as> auto i2 = as_const(r).cend(); + if constexpr (common_range && is_const_bidirectional) { + assert(*prev(i2) == *prev(cend(expected_range))); + } + } + } + + if (is_empty) { + return true; + } + + // Check view_interface::data + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + + // Check view_interface::operator[] + STATIC_ASSERT(CanIndex == is_random_access); + if constexpr (CanIndex) { + assert(r[0] == expected_range[0]); + } + + // Check view_interface::operator[] (const) + STATIC_ASSERT(CanIndex == is_const_random_access); + if constexpr (CanIndex) { + assert(as_const(r)[0] == expected_range[0]); + } + + // Check view_interface::front + STATIC_ASSERT(CanMemberFront == forward_range); + if constexpr (CanMemberFront) { + assert(r.front() == *begin(expected_range)); + } + + // Check view_interface::front (const) + STATIC_ASSERT(CanMemberFront == forward_range); + if constexpr (CanMemberFront) { + assert(as_const(r).front() == *begin(expected_range)); + } + + // Check view_interface::back + STATIC_ASSERT(CanMemberBack == (is_bidirectional && is_common)); + if constexpr (CanMemberBack) { + assert(r.back() == *prev(end(expected_range))); + } + + // Check view_interface::back (const) + STATIC_ASSERT(CanMemberBack == (is_const_bidirectional && is_const_common)); + if constexpr (CanMemberBack) { + assert(as_const(r).back() == *prev(end(expected_range))); + } + + { // Check cartesian_product_view::iterator + using I = iterator_t; + STATIC_ASSERT(input_iterator); + + // Check iterator_category + STATIC_ASSERT(same_as); + + // Check iterator_concept + using IterConcept = typename I::iterator_concept; + STATIC_ASSERT(is_random_access == same_as); + STATIC_ASSERT((is_bidirectional && !is_random_access) == same_as); + STATIC_ASSERT((forward_range && !is_bidirectional) == same_as); + + // Check value_type + STATIC_ASSERT(same_as, range_value_t...>>); + + // Check default-initializability + STATIC_ASSERT(default_initializable == default_initializable>); + + auto i = r.begin(); + + { // Check dereference + same_as, range_reference_t...>> decltype(auto) v = *as_const(i); + assert(v == expected_range[0]); + } + + { // Check pre-incrementation + same_as decltype(auto) i2 = ++i; + assert(&i2 == &i); + if (i != r.end()) { + assert(*i == expected_range[1]); + } + i = r.begin(); + } + + if constexpr (forward_range) { // Check post-incrementation + same_as decltype(auto) i2 = i++; + assert(*i2 == expected_range[0]); + if (i != r.end()) { + assert(*i == expected_range[1]); + } + i = r.begin(); + } else { + STATIC_ASSERT(is_void_v); + } + + if constexpr (is_bidirectional) { + { // Check pre-decrementation + i = ranges::next(r.begin()); + + same_as decltype(auto) i2 = --i; + assert(&i2 == &i); + assert(*i2 == expected_range[0]); + } + + { // Check post-decrementation + i = ranges::next(r.begin()); + + same_as decltype(auto) i2 = i--; + if (i2 != r.end()) { + assert(*i2 == expected_range[1]); + } + assert(*i == expected_range[0]); + } + } + + if constexpr (is_random_access) { + const auto half_max_distance = ranges::distance(r) / 2; + + { // Check advancement operators + same_as decltype(auto) i2 = (i += half_max_distance); + assert(&i2 == &i); + if (i != r.end()) { + assert(*i == expected_range[static_cast(half_max_distance)]); + } + + same_as decltype(auto) i3 = (i -= half_max_distance); + assert(&i3 == &i); + assert(*i == expected_range[0]); + } + + { // Check subscript operator + same_as, range_reference_t...>> decltype(auto) v = i[0]; + assert(v == expected_range[0]); + } + + if constexpr (equality_comparable>) { // Check equality comparisons + auto i2 = r.begin(); + same_as auto b1 = i == i2; + assert(b1); + ++i2; + same_as auto b2 = i != i2; + assert(b2); + same_as auto b3 = i2 != default_sentinel; + assert(b3); + ranges::advance(i2, r.end()); + same_as auto b4 = i2 == default_sentinel; + assert(b4); + } + + if constexpr ((random_access_range && ... + && random_access_range>) ) { // Check 3way comparisons + using Cat = common_comparison_category_t>, + compare_three_way_result_t>>...>; + auto i2 = r.begin(); + same_as auto cmp1 = i <=> i2; + assert(cmp1 == Cat::equivalent); + ++i2; + assert((i <=> i2) == Cat::less); + assert((i2 <=> i) == Cat::greater); + + same_as auto b1 = i < i2; + assert(b1); + same_as auto b2 = i2 > i; + assert(b2); + same_as auto b3 = i <= i2; + assert(b3); + same_as auto b4 = i2 >= i; + assert(b4); + } + + { // Check operator+ + same_as auto i2 = i + half_max_distance; + if (i2 != r.end()) { + assert(*i2 == expected_range[static_cast(half_max_distance)]); + } + + same_as auto i3 = half_max_distance + i; + if (i3 != r.end()) { + assert(*i3 == expected_range[static_cast(half_max_distance)]); + } + } + + { // Check operator-(Iter, Diff) + same_as auto i2 = (i + half_max_distance) - half_max_distance; + assert(*i2 == expected_range[0]); + } + } + + if constexpr (CartesianIsSizedSentinel...>) { // Check differencing + _Signed_integer_like auto diff = i - i; + assert(diff == 0); + assert(i - ranges::next(i) == -1); + assert(ranges::next(i) - i == 1); + } + + STATIC_ASSERT(sized_sentinel_for + == CartesianIsSizedSentinel...>); + if constexpr (sized_sentinel_for) { // Check differencing with default_sentinel + const auto expected_size = ranges::ssize(expected_range); + const _Signed_integer_like auto diff1 = i - default_sentinel; + assert(diff1 == -expected_size); + const _Signed_integer_like auto diff2 = default_sentinel - i; + assert(diff2 == expected_size); + } + + { // Check iter_move (hidden friend available via ADL) + same_as, range_rvalue_reference_t>...>> decltype(auto) + rval = iter_move(as_const(i)); + assert(rval == expected_range[0]); + static_assert(noexcept(iter_move(i)) == is_iter_move_nothrow...>()); + } + + if constexpr ((indirectly_swappable> && ... + && indirectly_swappable>>) ) { + // Check iter_swap, other tests are defined in test_iter_swap function + static_assert(is_void_v); + static_assert(noexcept(iter_swap(i, i)) == is_iter_swap_nothrow...>()); + } + } + + // Check cartesian_product_view::iterator + if constexpr (CanMemberBegin) { + using CI = iterator_t; + STATIC_ASSERT(input_iterator); + + // Check iterator_category + STATIC_ASSERT(same_as); + + // Check iterator_concept + using IterConcept = typename CI::iterator_concept; + STATIC_ASSERT(is_const_random_access == same_as); + STATIC_ASSERT( + (is_const_bidirectional && !is_const_random_access) == same_as); + STATIC_ASSERT( + (forward_range && !is_const_bidirectional) == same_as); + + // Check value_type + static_assert( + same_as, range_value_t...>>); + + // Check default-initializability + STATIC_ASSERT(default_initializable == default_initializable>); + + // Check conversion from non-const iterator + if constexpr ((convertible_to, iterator_t> && ... + && convertible_to, iterator_t>) ) { + auto i = r.begin(); + [[maybe_unused]] CI ci{move(i)}; + } + + auto i = r.begin(); + CI ci = as_const(r).begin(); + + { // Check dereference + same_as, range_reference_t...>> decltype(auto) v = + *as_const(ci); + assert(v == expected_range[0]); + } + + { // Check pre-incrementation + same_as decltype(auto) ci2 = ++ci; + assert(&ci2 == &ci); + if (ci != as_const(r).end()) { + assert(*ci == expected_range[1]); + } + ci = as_const(r).begin(); + } + + if constexpr (forward_range) { // Check post-incrementation + same_as decltype(auto) ci2 = ci++; + assert(*ci2 == expected_range[0]); + if (ci != as_const(r).end()) { + assert(*ci == expected_range[1]); + } + ci = as_const(r).begin(); + } else { + STATIC_ASSERT(is_void_v); + } + + if constexpr (is_const_bidirectional) { + { // Check pre-decrementation + ci = ranges::next(r.begin()); + + same_as decltype(auto) ci2 = --ci; + assert(&ci2 == &ci); + assert(*ci2 == expected_range[0]); + } + + { // Check post-decrementation + ci = ranges::next(r.begin()); + + same_as decltype(auto) ci2 = ci--; + if (ci2 != r.end()) { + assert(*ci2 == expected_range[1]); + } + assert(*ci == expected_range[0]); + } + } + + if constexpr (is_const_random_access) { + const auto half_max_distance = ranges::distance(r) / 2; + + { // Check advancement operators + same_as decltype(auto) ci2 = (ci += half_max_distance); + assert(&ci2 == &ci); + if (ci != r.end()) { + assert(*ci == expected_range[static_cast(half_max_distance)]); + } + + same_as decltype(auto) ci3 = (ci -= half_max_distance); + assert(&ci3 == &ci); + assert(*ci == expected_range[0]); + } + + { // Check subscript operator + same_as, range_reference_t...>> decltype(auto) v = + ci[0]; + assert(v == expected_range[0]); + } + + if constexpr (equality_comparable>) { // Check equality comparisons + CI ci2 = as_const(r).begin(); + same_as auto b1 = ci == ci2; + assert(b1); + ++ci2; + same_as auto b2 = ci != ci2; + assert(b2); + same_as auto b3 = ci2 != default_sentinel; + assert(b3); + ranges::advance(ci2, r.end()); + same_as auto b4 = ci2 == default_sentinel; + assert(b4); + } + + if constexpr (equality_comparable_with, + iterator_t>) { // Check equality comparisons (mixed) + CI ci2 = as_const(r).begin(); + same_as auto b1 = i == ci2; + assert(b1); + ++ci2; + same_as auto b2 = i != ci2; + assert(b2); + } + + if constexpr ((random_access_range && ... + && random_access_range>) ) { // Check 3way comparisons + using Cat = common_comparison_category_t>, + compare_three_way_result_t>>...>; + CI ci2 = as_const(r).begin(); + same_as auto cmp1 = ci <=> ci2; + assert(cmp1 == Cat::equivalent); + ++ci2; + assert((ci <=> ci2) == Cat::less); + assert((ci2 <=> ci) == Cat::greater); + + same_as auto b1 = ci < ci2; + assert(b1); + same_as auto b2 = ci2 > ci; + assert(b2); + same_as auto b3 = ci <= ci2; + assert(b3); + same_as auto b4 = ci2 >= ci; + assert(b4); + } + + if constexpr ((random_access_range && ... + && random_access_range>) ) { // Check 3way comparisons (mixed) + using Cat = common_comparison_category_t>, + compare_three_way_result_t>>...>; + CI ci2 = as_const(r).begin(); + same_as auto cmp1 = i <=> ci2; + assert(cmp1 == Cat::equivalent); + ++ci2; + assert((i <=> ci2) == Cat::less); + assert((ci2 <=> i) == Cat::greater); + + same_as auto b1 = i < ci2; + assert(b1); + same_as auto b2 = ci2 > i; + assert(b2); + same_as auto b3 = i <= ci2; + assert(b3); + same_as auto b4 = ci2 >= i; + assert(b4); + } + + { // Check operator+ + same_as auto ci2 = ci + 1; + if (ci2 != r.end()) { + assert(*ci2 == expected_range[1]); + } + + same_as auto ci3 = 1 + ci; + if (ci3 != r.end()) { + assert(*ci3 == expected_range[1]); + } + } + + { // Check operator-(Iter, Diff) + same_as auto ci2 = ranges::next(ci) - 1; + assert(*ci2 == expected_range[0]); + } + } + + if constexpr (CartesianIsSizedSentinel...>) { // Check differencing + _Signed_integer_like auto diff = ci - ci; + assert(diff == 0); + assert(ci - ranges::next(ci) == -1); + assert(ranges::next(ci) - ci == 1); + } + + STATIC_ASSERT(sized_sentinel_for + == CartesianIsSizedSentinel...>); + if constexpr (sized_sentinel_for) { // Check differencing with default_sentinel + const auto expected_size = ranges::ssize(expected_range); + const _Signed_integer_like auto diff1 = ci - default_sentinel; + assert(diff1 == -expected_size); + const _Signed_integer_like auto diff2 = default_sentinel - ci; + assert(diff2 == expected_size); + } + + { // Check iter_move + same_as, + range_rvalue_reference_t>...>> decltype(auto) rval = iter_move(as_const(ci)); + assert(rval == expected_range[0]); + static_assert(noexcept(iter_move(ci)) == is_iter_move_nothrow...>()); + } + + if constexpr ((indirectly_swappable> && ... + && indirectly_swappable>>) ) { + // Check iter_swap, other tests are defined in test_iter_swap function + static_assert(is_void_v); + static_assert(noexcept(iter_swap(ci, ci)) == is_iter_swap_nothrow...>()); + } + } + + return true; +} + +// Check calling views::cartesian_product without arguments +STATIC_ASSERT(same_as); + +template + requires (indirectly_swappable> && ...) +constexpr void test_iter_swap(Rngs&... rngs) { + // This test assumes that 'ranges::size(rng)' is at least 2 for each rng in rngs + auto r = views::cartesian_product(rngs...); + using R = decltype(r); + using Val = ranges::range_value_t; + + { // Check iter_swap for cartesian_product_view::iterator + auto i = r.begin(); + Val first = *i; + auto j = ranges::next(i); + Val second = *j; + + iter_swap(i, j); + assert(*i == second); + assert(*j == first); + + ranges::iter_swap(i, j); + assert(*i == first); + assert(*j == second); + + static_assert(noexcept(iter_swap(i, j)) == is_iter_swap_nothrow()); + } + + // Check iter_swap for cartesian_product_view::iterator + if constexpr (((CanMemberBegin && indirectly_swappable>) &&...)) { + using CVal = ranges::range_value_t; + auto i = as_const(r).begin(); + CVal first = *i; + auto j = ranges::next(i); + CVal second = *j; + + iter_swap(i, j); + assert(*i == second); + assert(*j == first); + + ranges::iter_swap(i, j); + assert(*i == first); + assert(*j == second); + + static_assert(noexcept(iter_swap(i, j)) == is_iter_swap_nothrow()); + } +} + +constexpr tuple some_ranges = { + array{0, 1, 2, 3, 4}, + array{11, 22, 33}, + array{'7'}, + array{"4"sv, "2"sv, "0"sv}, +}; + +// Expected result of views::cartesian_product(get<0>(some_ranges)) +constexpr array, 5> expected_result_0{{{0}, {1}, {2}, {3}, {4}}}; + +// Expected result of views::cartesian_product(get<0>(some_ranges), get<1>(some_ranges)) +constexpr array, 15> expected_result_1{{{0, 11}, {0, 22}, {0, 33}, {1, 11}, {1, 22}, {1, 33}, {2, 11}, + {2, 22}, {2, 33}, {3, 11}, {3, 22}, {3, 33}, {4, 11}, {4, 22}, {4, 33}}}; + +// Expected result of views::cartesian_product(get<0>(some_ranges), ..., get<2>(some_ranges)) +constexpr array, 15> expected_result_2{ + {{0, 11, '7'}, {0, 22, '7'}, {0, 33, '7'}, {1, 11, '7'}, {1, 22, '7'}, {1, 33, '7'}, {2, 11, '7'}, {2, 22, '7'}, + {2, 33, '7'}, {3, 11, '7'}, {3, 22, '7'}, {3, 33, '7'}, {4, 11, '7'}, {4, 22, '7'}, {4, 33, '7'}}}; + +// Expected result of views::cartesian_product(get<0>(some_ranges), ..., get<3>(some_ranges)) +constexpr array, 45> expected_result_3{ + {{0, 11, '7', "4"sv}, {0, 11, '7', "2"sv}, {0, 11, '7', "0"sv}, {0, 22, '7', "4"sv}, {0, 22, '7', "2"sv}, + {0, 22, '7', "0"sv}, {0, 33, '7', "4"sv}, {0, 33, '7', "2"sv}, {0, 33, '7', "0"sv}, {1, 11, '7', "4"sv}, + {1, 11, '7', "2"sv}, {1, 11, '7', "0"sv}, {1, 22, '7', "4"sv}, {1, 22, '7', "2"sv}, {1, 22, '7', "0"sv}, + {1, 33, '7', "4"sv}, {1, 33, '7', "2"sv}, {1, 33, '7', "0"sv}, {2, 11, '7', "4"sv}, {2, 11, '7', "2"sv}, + {2, 11, '7', "0"sv}, {2, 22, '7', "4"sv}, {2, 22, '7', "2"sv}, {2, 22, '7', "0"sv}, {2, 33, '7', "4"sv}, + {2, 33, '7', "2"sv}, {2, 33, '7', "0"sv}, {3, 11, '7', "4"sv}, {3, 11, '7', "2"sv}, {3, 11, '7', "0"sv}, + {3, 22, '7', "4"sv}, {3, 22, '7', "2"sv}, {3, 22, '7', "0"sv}, {3, 33, '7', "4"sv}, {3, 33, '7', "2"sv}, + {3, 33, '7', "0"sv}, {4, 11, '7', "4"sv}, {4, 11, '7', "2"sv}, {4, 11, '7', "0"sv}, {4, 22, '7', "4"sv}, + {4, 22, '7', "2"sv}, {4, 22, '7', "0"sv}, {4, 33, '7', "4"sv}, {4, 33, '7', "2"sv}, {4, 33, '7', "0"sv}}}; + +template +struct test_input_range { + template + using type = test::range; +}; + +template +struct test_range { + template + using type = + test::range}, + IsCommon, test::CanCompare{derived_from || IsCommon == test::Common::yes}, + test::ProxyRef{!derived_from}>; +}; + +struct instantiator { + template + static constexpr void call() { + typename R::template type r0{get<0>(some_ranges)}; + test_one(expected_result_0, r0); + + if constexpr (ranges::forward_range>) { + typename R::template type r1{get<1>(some_ranges)}; + test_one(expected_result_1, r0, r1); + +#if !(defined(__clang__) && defined(_DEBUG) && !defined(_DLL)) // constexpr limit + typename R::template type r2{get<2>(some_ranges)}; + test_one(expected_result_2, r0, r1, r2); +#endif // "Clang /MTd" configuration + + int swap_a1[] = {1, 2, 3}; + typename R::template type swap_r1{swap_a1}; + int swap_a2[] = {9, 8, 7}; + typename R::template type swap_r2{swap_a2}; + test_iter_swap(swap_r1, swap_r2); + } + } +}; + +constexpr void instantiation_test() { + // The cartesian_product_view is sensitive to category, commonality, and size, but oblivious to + // differencing and proxyness. + using test::Common, test::Sized, test::CanDifference; + + // When the base range is an input range, the view is sensitive to differencing + instantiator::call>(); + instantiator::call>(); + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); +} + +template > +using move_only_view = test::range}, + test::ProxyRef{!derived_from}, test::CanView::yes, test::Copyability::move_only>; + +namespace check_recommended_practice_implementation { // MSVC STL specific behavior + using ranges::cartesian_product_view, ranges::empty_view, ranges::single_view, views::all_t, ranges::range_size_t, + ranges::range_difference_t, ranges::ref_view, ranges::owning_view; + using Arr = array; + using Vec = vector; + using Span = span; + + // Computing product for such small array does not require big range_size_t + STATIC_ASSERT(sizeof(range_size_t>>) <= sizeof(size_t)); + STATIC_ASSERT(sizeof(range_size_t, all_t>>) <= sizeof(size_t)); + STATIC_ASSERT(sizeof(range_size_t, all_t, all_t>>) <= sizeof(size_t)); + + // Same thing with range_difference_t + STATIC_ASSERT(sizeof(range_difference_t>>) <= sizeof(ptrdiff_t)); + STATIC_ASSERT(sizeof(range_difference_t, all_t>>) <= sizeof(ptrdiff_t)); + STATIC_ASSERT( + sizeof(range_difference_t, all_t, all_t>>) <= sizeof(ptrdiff_t)); + + // Computing product for such small span does not require big range_size_t + STATIC_ASSERT(sizeof(range_size_t>>) <= sizeof(size_t)); + STATIC_ASSERT(sizeof(range_size_t, all_t>>) <= sizeof(size_t)); + STATIC_ASSERT( + sizeof(range_size_t, all_t, all_t>>) <= sizeof(size_t)); + + // Same thing with range_difference_t + STATIC_ASSERT(sizeof(range_difference_t>>) <= sizeof(ptrdiff_t)); + STATIC_ASSERT(sizeof(range_difference_t, all_t>>) <= sizeof(ptrdiff_t)); + STATIC_ASSERT( + sizeof(range_difference_t, all_t, all_t>>) <= sizeof(ptrdiff_t)); + + // Check 'single_view' and 'empty_view' + STATIC_ASSERT(sizeof(range_size_t, single_view>>) <= sizeof(size_t)); + STATIC_ASSERT( + sizeof(range_difference_t, single_view>>) <= sizeof(ptrdiff_t)); + + // Check 'ref_view<(const) V>' and 'owning_view' + STATIC_ASSERT(sizeof(range_size_t, ref_view, owning_view>>) + <= sizeof(size_t)); + STATIC_ASSERT( + sizeof(range_difference_t, ref_view, owning_view>>) + <= sizeof(ptrdiff_t)); + + // One vector should not use big integer-class type... + STATIC_ASSERT(sizeof(range_size_t>>) <= sizeof(size_t)); + STATIC_ASSERT(sizeof(range_difference_t>>) <= sizeof(ptrdiff_t)); + + // ... but two vectors will + STATIC_ASSERT(sizeof(range_size_t, all_t>>) > sizeof(size_t)); + STATIC_ASSERT(sizeof(range_difference_t, all_t>>) > sizeof(ptrdiff_t)); +} // namespace check_recommended_practice_implementation + +int main() { + // Check views + { // ... copyable + constexpr span s{get<0>(some_ranges)}; + STATIC_ASSERT(test_one(expected_result_0, s)); + test_one(expected_result_0, s); + } + + { // ... modifiable elements (so iterators are indirectly swappable) + auto arr = get<0>(some_ranges); + span s{arr}; + test_one(expected_result_0, s); + } + + { // ... move-only + using test::Common, test::Sized; + test_one(expected_result_3, // + move_only_view{get<0>(some_ranges)}, + move_only_view{get<1>(some_ranges)}, + move_only_view{get<2>(some_ranges)}, + move_only_view{get<3>(some_ranges)}); + test_one(expected_result_3, // + move_only_view{get<0>(some_ranges)}, + move_only_view{get<1>(some_ranges)}, + move_only_view{get<2>(some_ranges)}, + move_only_view{get<3>(some_ranges)}); + test_one(expected_result_2, // + move_only_view{get<0>(some_ranges)}, + move_only_view{get<1>(some_ranges)}, + move_only_view{get<2>(some_ranges)}); + test_one(expected_result_2, // + move_only_view{get<0>(some_ranges)}, + move_only_view{get<1>(some_ranges)}, + move_only_view{get<2>(some_ranges)}); + } + + // Check non-views + { + constexpr auto& r0 = get<0>(some_ranges); + STATIC_ASSERT(test_one(expected_result_0, r0)); + test_one(expected_result_0, r0); + + auto r1 = get<1>(some_ranges) | ranges::to(); + test_one(expected_result_1, r0, r1); + + auto r2 = get<2>(some_ranges) | ranges::to(); + test_one(expected_result_2, r0, r1, r2); + + auto r3 = get<3>(some_ranges) | ranges::to(); + test_one(expected_result_3, r0, r1, r2, r3); + } + +#ifndef _PREFAST_ // TRANSITION, GH-1030 + STATIC_ASSERT((instantiation_test(), true)); +#endif // TRANSITION, GH-1030 + instantiation_test(); +} diff --git a/tests/std/tests/P2374R4_views_cartesian_product_death/env.lst b/tests/std/tests/P2374R4_views_cartesian_product_death/env.lst new file mode 100644 index 00000000000..8ac7033b206 --- /dev/null +++ b/tests/std/tests/P2374R4_views_cartesian_product_death/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_latest_matrix.lst diff --git a/tests/std/tests/P2374R4_views_cartesian_product_death/test.cpp b/tests/std/tests/P2374R4_views_cartesian_product_death/test.cpp new file mode 100644 index 00000000000..65b850caafb --- /dev/null +++ b/tests/std/tests/P2374R4_views_cartesian_product_death/test.cpp @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#define _CONTAINER_DEBUG_LEVEL 1 + +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +constexpr auto much_ints = views::iota(0ull, (numeric_limits::max)()); +constexpr array little_ints = {1, 2, 3, 4}; +void test_view_size() { + auto v = views::cartesian_product(much_ints, much_ints, much_ints); + // Size of cartesian product cannot be represented by _Size_type (N4928 [range.cartesian.view]/10). + (void) v.size(); +} + +void test_view_const_size() { + auto v = views::cartesian_product(much_ints, much_ints, much_ints); + // Size of cartesian product cannot be represented by _Size_type (N4928 [range.cartesian.view]/10). + (void) as_const(v).size(); +} + +void test_iterator_advance_past_end_with_small_offset() { + // This preconditions check works only when all ranges model ranges::sized_range + auto v = views::cartesian_product(little_ints, little_ints, little_ints); + auto i = v.begin(); + // Cannot advance cartesian_product_view iterator past end (N4928 [range.cartesian.iterator]/19). + i += 65; +} + +void test_iterator_advance_past_end_with_big_offset() { + // This preconditions check works only when all ranges model ranges::sized_range + auto v = views::cartesian_product(little_ints, little_ints, little_ints); + auto i = v.begin(); + // Cannot advance cartesian_product_view iterator past end (N4928 [range.cartesian.iterator]/19). + i += 1000; +} + +void test_iterator_advance_before_begin() { + auto v = views::cartesian_product(little_ints, little_ints, little_ints); + auto i = v.end(); + // Cannot advance cartesian_product_view iterator before begin (N4928 [range.cartesian.iterator]/19). + i += -65; +} + +void test_iterator_differencing() { + auto v = views::cartesian_product(much_ints, much_ints, much_ints); + auto i1 = v.begin(); + auto i2 = v.end(); + // Scaled-sum cannot be represented by _Difference_type (N4928 [range.cartesian.iterator]/8). + (void) (i2 - i1); +} + +void test_iterator_and_default_sentinel_differencing() { + auto v = views::cartesian_product(much_ints, much_ints, much_ints); + auto i = v.begin(); + // Scaled-sum cannot be represented by _Difference_type (N4928 [range.cartesian.iterator]/8). + (void) (default_sentinel - i); +} + +int main(int argc, char* argv[]) { + std_testing::death_test_executive exec; + +#if _ITERATOR_DEBUG_LEVEL != 0 + exec.add_death_tests({ + test_view_size, + test_view_const_size, + test_iterator_advance_past_end_with_small_offset, + test_iterator_advance_past_end_with_big_offset, + test_iterator_advance_before_begin, + test_iterator_differencing, + test_iterator_and_default_sentinel_differencing, + }); +#else // ^^^ test everything / test only _CONTAINER_DEBUG_LEVEL cases vvv + exec.add_death_tests({ + test_view_size, + test_view_const_size, + }); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + return exec.run(argc, argv); +} diff --git a/tests/std/tests/P2465R3_standard_library_modules/custom_format.py b/tests/std/tests/P2465R3_standard_library_modules/custom_format.py index 3f5dbd3ef3c..54567eab7db 100644 --- a/tests/std/tests/P2465R3_standard_library_modules/custom_format.py +++ b/tests/std/tests/P2465R3_standard_library_modules/custom_format.py @@ -9,7 +9,7 @@ class CustomTestFormat(STLTestFormat): def getBuildSteps(self, test, litConfig, shared): - outputDir, outputBase = test.getTempPaths() + _, outputBase = test.getTempPaths() stdIxx = os.path.join(litConfig.cxx_modules, 'std.ixx') stdCompatIxx = os.path.join(litConfig.cxx_modules, 'std.compat.ixx') diff --git a/tests/std/tests/VSO_0000000_any_calling_conventions/custom_format.py b/tests/std/tests/VSO_0000000_any_calling_conventions/custom_format.py index 0f08a5e0183..d829adc921f 100644 --- a/tests/std/tests/VSO_0000000_any_calling_conventions/custom_format.py +++ b/tests/std/tests/VSO_0000000_any_calling_conventions/custom_format.py @@ -1,7 +1,6 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -import itertools import os from stl.test.format import STLTestFormat, TestStep diff --git a/tests/std/tests/VSO_0000000_matching_npos_address/custom_format.py b/tests/std/tests/VSO_0000000_matching_npos_address/custom_format.py index fa2efc4372f..910e2111fb6 100644 --- a/tests/std/tests/VSO_0000000_matching_npos_address/custom_format.py +++ b/tests/std/tests/VSO_0000000_matching_npos_address/custom_format.py @@ -12,7 +12,7 @@ def getBuildSteps(self, test, litConfig, shared): exeSource = test.getSourcePath() test2Source = os.path.join(os.path.dirname(exeSource), 'test2.cpp') - outputDir, outputBase = test.getTempPaths() + _, outputBase = test.getTempPaths() if TestType.COMPILE in test.testType: cmd = [test.cxx, '/c', exeSource, test2Source, *test.flags, *test.compileFlags] diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp index 6b607b762a9..22fa2b285ea 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -19,6 +19,8 @@ #include #endif +#include "test_min_max_element_support.hpp" + using namespace std; #pragma warning(disable : 4984) // 'if constexpr' is a C++17 language extension @@ -121,103 +123,6 @@ void test_find(mt19937_64& gen) { } } -template -FwdIt last_known_good_min_element(FwdIt first, FwdIt last) { - FwdIt result = first; - - for (; first != last; ++first) { - if (*first < *result) { - result = first; - } - } - - return result; -} - -template -FwdIt last_known_good_max_element(FwdIt first, FwdIt last) { - FwdIt result = first; - - for (; first != last; ++first) { - if (*result < *first) { - result = first; - } - } - - return result; -} - -template -pair last_known_good_minmax_element(FwdIt first, FwdIt last) { - // find smallest and largest elements - pair found(first, first); - - if (first != last) { - while (++first != last) { // process one or two elements - FwdIt next = first; - if (++next == last) { // process last element - if (*first < *found.first) { - found.first = first; - } else if (!(*first < *found.second)) { - found.second = first; - } - } else { // process next two elements - if (*next < *first) { // test next for new smallest - if (*next < *found.first) { - found.first = next; - } - - if (!(*first < *found.second)) { - found.second = first; - } - } else { // test first for new smallest - if (*first < *found.first) { - found.first = first; - } - - if (!(*next < *found.second)) { - found.second = next; - } - } - first = next; - } - } - } - - return found; -} - -template -void test_case_min_max_element(const vector& input) { - auto expected_min = last_known_good_min_element(input.begin(), input.end()); - auto expected_max = last_known_good_max_element(input.begin(), input.end()); - auto expected_minmax = last_known_good_minmax_element(input.begin(), input.end()); - auto actual_min = min_element(input.begin(), input.end()); - auto actual_max = max_element(input.begin(), input.end()); - auto actual_minmax = minmax_element(input.begin(), input.end()); - assert(expected_min == actual_min); - assert(expected_max == actual_max); - assert(expected_minmax == actual_minmax); -#ifdef __cpp_lib_concepts - using ranges::views::take; - - auto actual_min_range = ranges::min_element(input); - auto actual_max_range = ranges::max_element(input); - auto actual_minmax_range = ranges::minmax_element(input); - auto actual_min_sized_range = ranges::min_element(take(input, static_cast(input.size()))); - auto actual_max_sized_range = ranges::max_element(take(input, static_cast(input.size()))); - auto actual_minmax_sized_range = ranges::minmax_element(take(input, static_cast(input.size()))); - assert(expected_min == actual_min_range); - assert(expected_max == actual_max_range); - assert(expected_minmax.first == actual_minmax_range.min); - assert(expected_minmax.second == actual_minmax_range.max); - assert(expected_min == actual_min_sized_range); - assert(expected_max == actual_max_sized_range); - assert(expected_minmax.first == actual_minmax_sized_range.min); - assert(expected_minmax.second == actual_minmax_sized_range.max); -#endif // __cpp_lib_concepts -} - template void test_min_max_element(mt19937_64& gen) { using Limits = numeric_limits; @@ -504,12 +409,16 @@ int main() { #if defined(_M_IX86) || defined(_M_X64) disable_instructions(__ISA_AVAILABLE_AVX2); test_vector_algorithms(gen); + test_various_containers(); + disable_instructions(__ISA_AVAILABLE_SSE42); test_vector_algorithms(gen); + test_various_containers(); #endif // defined(_M_IX86) || defined(_M_X64) #if defined(_M_IX86) disable_instructions(__ISA_AVAILABLE_SSE2); test_vector_algorithms(gen); + test_various_containers(); #endif // defined(_M_IX86) #endif // _M_CEE_PURE } diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 5ceec795b71..25ba795b2a6 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -1608,6 +1608,20 @@ STATIC_ASSERT(__cpp_lib_ranges_as_rvalue == 202207L); #endif #endif +#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 +#ifndef __cpp_lib_ranges_cartesian_product +#error __cpp_lib_ranges_cartesian_product is not defined +#elif __cpp_lib_ranges_cartesian_product != 202207L +#error __cpp_lib_ranges_cartesian_product is not 202207L +#else +STATIC_ASSERT(__cpp_lib_ranges_cartesian_product == 202207L); +#endif +#else +#ifdef __cpp_lib_ranges_cartesian_product +#error __cpp_lib_ranges_cartesian_product is defined +#endif +#endif + #if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 #ifndef __cpp_lib_ranges_chunk #error __cpp_lib_ranges_chunk is not defined diff --git a/tests/utils/stl/test/file_parsing.py b/tests/utils/stl/test/file_parsing.py index 9a1308b9d5a..5b086cc49af 100644 --- a/tests/utils/stl/test/file_parsing.py +++ b/tests/utils/stl/test/file_parsing.py @@ -1,10 +1,9 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -from collections import namedtuple from dataclasses import dataclass, field from pathlib import Path -from typing import Dict, List, Optional, Set, Tuple, Union +from typing import Dict, List, Optional, Tuple, Union import itertools import os import re @@ -60,7 +59,7 @@ def _parse_env_line(line: str) -> Optional[_TmpEnvEntry]: for env_match in _ENV_VAR_MULTI_ITEM_REGEX.finditer(line): name = env_match.group("name") value = env_match.group("value") - result.env[env_match.group("name")] = env_match.group("value") + result.env[name] = value return result diff --git a/tests/utils/stl/test/format.py b/tests/utils/stl/test/format.py index f2ad078b47f..7de5471eda7 100644 --- a/tests/utils/stl/test/format.py +++ b/tests/utils/stl/test/format.py @@ -10,7 +10,6 @@ from pathlib import Path from typing import Dict, List, Optional import copy -import errno import itertools import os import re @@ -167,7 +166,6 @@ def getBuildSetupSteps(self, test, litConfig, shared): yield from [] def getBuildSteps(self, test, litConfig, shared): - filename = test.path_in_suite[-1] _, tmpBase = test.getTempPaths() shouldFail = TestType.FAIL in test.testType diff --git a/tests/utils/stl/test/tests.py b/tests/utils/stl/test/tests.py index 5d10ae30386..fc0b0e2fafe 100644 --- a/tests/utils/stl/test/tests.py +++ b/tests/utils/stl/test/tests.py @@ -13,7 +13,7 @@ import os import shutil -from lit.Test import Result, SKIPPED, Test, UNRESOLVED, UNSUPPORTED +from lit.Test import Result, SKIPPED, Test, UNSUPPORTED from libcxx.test.dsl import Feature import lit