diff --git a/README.md b/README.md index a776788796c..e79750ea31e 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ issue. The [bug tag][] and [enhancement tag][] are being populated. # Goals -We're implementing the latest C++ Working Draft, currently [N4964][], which will eventually become the next C++ +We're implementing the latest C++ Working Draft, currently [N4971][], which will eventually become the next C++ International Standard. The terms Working Draft (WD) and Working Paper (WP) are interchangeable; we often informally refer to these drafts as "the Standard" while being aware of the difference. (There are other relevant Standards; for example, supporting `/std:c++14` and `/std:c++17` involves understanding how the C++14 and C++17 @@ -530,7 +530,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception [LWG issues]: https://cplusplus.github.io/LWG/lwg-toc.html [LWG tag]: https://github.com/microsoft/STL/issues?q=is%3Aopen+is%3Aissue+label%3ALWG [Microsoft Open Source Code of Conduct]: https://opensource.microsoft.com/codeofconduct/ -[N4964]: https://wg21.link/n4964 +[N4971]: https://wg21.link/n4971 [NOTICE.txt]: NOTICE.txt [Ninja]: https://ninja-build.org [STL-CI-badge]: https://dev.azure.com/vclibs/STL/_apis/build/status%2FSTL-CI?branchName=main "STL-CI" diff --git a/azure-devops/asan-pipeline.yml b/azure-devops/asan-pipeline.yml index 4068735bad8..53ef2c46d79 100644 --- a/azure-devops/asan-pipeline.yml +++ b/azure-devops/asan-pipeline.yml @@ -8,7 +8,6 @@ variables: - name: testSelection value: '-R stlasan' # Restrict to "stlasan" test -pr: none trigger: none schedules: diff --git a/azure-devops/config.yml b/azure-devops/config.yml index 86908112536..7afcfb904b3 100644 --- a/azure-devops/config.yml +++ b/azure-devops/config.yml @@ -5,7 +5,7 @@ variables: - name: poolName - value: 'StlBuild-2023-12-12T1453-Pool' + value: 'StlBuild-2024-01-09T1215-Pool' readonly: true - name: poolDemands value: 'EnableSpotVM -equals true' diff --git a/boost-math b/boost-math index 1a7be5d895d..44af29a78c8 160000 --- a/boost-math +++ b/boost-math @@ -1 +1 @@ -Subproject commit 1a7be5d895d266a870af7a6ed258e5bcf9838277 +Subproject commit 44af29a78c85ee89ce37f7f43d532afd05c3d981 diff --git a/docs/cgmanifest.json b/docs/cgmanifest.json index 9a65588b24e..8ec3fb1df8e 100644 --- a/docs/cgmanifest.json +++ b/docs/cgmanifest.json @@ -6,7 +6,7 @@ "type": "git", "git": { "repositoryUrl": "https://github.com/boostorg/math", - "commitHash": "1a7be5d895d266a870af7a6ed258e5bcf9838277" + "commitHash": "44af29a78c85ee89ce37f7f43d532afd05c3d981" } } }, diff --git a/stl/inc/atomic b/stl/inc/atomic index 93aa9794f72..8161a9981a1 100644 --- a/stl/inc/atomic +++ b/stl/inc/atomic @@ -2155,8 +2155,9 @@ public: constexpr atomic() noexcept(is_nothrow_default_constructible_v<_Ty>) : _Base() {} - atomic(const atomic&) = delete; - atomic& operator=(const atomic&) = delete; + atomic(const atomic&) = delete; + atomic& operator=(const atomic&) = delete; + atomic& operator=(const atomic&) volatile = delete; #if _HAS_CXX17 static constexpr bool is_always_lock_free = _Is_always_lock_free; diff --git a/stl/inc/chrono b/stl/inc/chrono index 34f10386d07..982be0a92d1 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -5477,7 +5477,14 @@ namespace chrono { int _Estimated_width = -1; (void) _Measure_string_prefix(_Stream.view(), _Estimated_width); - return _Write_aligned(_STD move(_FormatCtx.out()), _Estimated_width, _Specs, _Fmt_align::_Left, + + auto _Format_specs = _Specs; + if (_Specs._Dynamic_width_index >= 0) { + _Format_specs._Width = _Get_dynamic_specs<_Width_checker>( + _FormatCtx.arg(static_cast(_Specs._Dynamic_width_index))); + } + + return _Write_aligned(_STD move(_FormatCtx.out()), _Estimated_width, _Format_specs, _Fmt_align::_Left, [&](auto _Out) { return _Fmt_write(_STD move(_Out), _Stream.view()); }); } diff --git a/stl/inc/exception b/stl/inc/exception index fc888003c12..bb34e333cfe 100644 --- a/stl/inc/exception +++ b/stl/inc/exception @@ -68,6 +68,9 @@ _STD_END #else // ^^^ _HAS_EXCEPTIONS / !_HAS_EXCEPTIONS vvv +#pragma push_macro("stdext") +#undef stdext + _STDEXT_BEGIN class exception; _STDEXT_END @@ -203,6 +206,8 @@ _EXPORT_STD using _STDEXT bad_exception; _STD_END +#pragma pop_macro("stdext") + #endif // ^^^ !_HAS_EXCEPTIONS ^^^ extern "C++" _CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL __ExceptionPtrCreate(_Out_ void*) noexcept; diff --git a/stl/inc/execution b/stl/inc/execution index 68b22eed7ed..539108d3575 100644 --- a/stl/inc/execution +++ b/stl/inc/execution @@ -4235,7 +4235,8 @@ struct _Static_partitioned_transform_reduce2 { // transformed reduction task sch auto& _Transform_op = _This->_Transform_op; auto _Chunk = _This->_Basis._Get_chunk(_Key); auto _Next = _Chunk._First; - _Ty _Val{_Reduce_op(_Transform_op(*_Chunk._First), _Transform_op(*++_Next))}; + + _Ty _Val = _Reduce_op(_Transform_op(*_Chunk._First), _Transform_op(*++_Next)); while (++_Next != _Chunk._Last) { _Val = _Reduce_op(_STD move(_Val), _Transform_op(*_Next)); } diff --git a/stl/inc/format b/stl/inc/format index 38e6d6c6b8d..b0b97ca0575 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -1762,10 +1762,13 @@ template class _Specs_checker : public _Handler { private: _Basic_format_arg_type _Arg_type; - // we'll see this if we get a modifier that requires an integer presentation type - // for types that can have either integer or non-integer presentation types (charT or bool) + + // Set for the hash and sign modifiers. bool _Need_arithmetic_presentation_type = false; + // Set for the zero modifier. + bool _Need_arithmetic_or_pointer_presentation_type = false; + public: constexpr explicit _Specs_checker(const _Handler& _Handler_inst, const _Basic_format_arg_type _Arg_type_) : _Handler(_Handler_inst), _Arg_type(_Arg_type_) {} @@ -1776,6 +1779,12 @@ public: } } + constexpr void _Require_numeric_or_pointer_argument() const { + if (!_Is_arithmetic_fmt_type(_Arg_type) && _Arg_type != _Basic_format_arg_type::_Pointer_type) { + _Throw_format_error("Format specifier requires numeric or pointer argument."); + } + } + constexpr void _Check_precision() const { if (_Is_integral_fmt_type(_Arg_type) || _Arg_type == _Basic_format_arg_type::_Pointer_type) { _Throw_format_error("Precision not allowed for this argument type."); @@ -1802,8 +1811,8 @@ public: } constexpr void _On_zero() { - _Need_arithmetic_presentation_type = true; - _Require_numeric_argument(); + _Need_arithmetic_or_pointer_presentation_type = true; + _Require_numeric_or_pointer_argument(); _Handler::_On_zero(); } @@ -1869,6 +1878,7 @@ public: _Cat = _Presentation_type_category::_Floating; break; case 'p': + case 'P': _Cat = _Presentation_type_category::_Pointer; break; default: @@ -1960,7 +1970,18 @@ public: if (_Need_arithmetic_presentation_type && _Cat != _Presentation_type_category::_Integer && _Cat != _Presentation_type_category::_Floating) { - _Throw_format_error("Modifier requires an integer presentation type for bool"); + // N4971 [format.string.std]/5: "The sign option is only valid for arithmetic types + // other than charT and bool or when an integer presentation type is specified." + // N4971 [format.string.std]/7: "The # option [...] is valid for arithmetic types + // other than charT and bool or when an integer presentation type is specified, and not otherwise." + _Throw_format_error("Hash/sign modifier requires an arithmetic presentation type"); + } + + if (_Need_arithmetic_or_pointer_presentation_type && _Cat != _Presentation_type_category::_Integer + && _Cat != _Presentation_type_category::_Floating && _Cat != _Presentation_type_category::_Pointer) { + // N4971 [format.string.std]/8: "The 0 option is valid for arithmetic types + // other than charT and bool, pointer types, or when an integer presentation type is specified." + _Throw_format_error("Zero modifier requires an arithmetic or pointer presentation type"); } _Handler::_On_type(_Type); } @@ -3168,25 +3189,37 @@ _NODISCARD _OutputIt _Fmt_write( template _NODISCARD _OutputIt _Fmt_write( _OutputIt _Out, const void* const _Value, const _Basic_format_specs<_CharT>& _Specs, _Lazy_locale) { - _STL_INTERNAL_CHECK(_Specs._Type == '\0' || _Specs._Type == 'p'); + _STL_INTERNAL_CHECK(_Specs._Type == '\0' || _Specs._Type == 'p' || _Specs._Type == 'P'); _STL_INTERNAL_CHECK(_Specs._Sgn == _Fmt_sign::_None); _STL_INTERNAL_CHECK(!_Specs._Alt); _STL_INTERNAL_CHECK(_Specs._Precision == -1); - _STL_INTERNAL_CHECK(!_Specs._Leading_zero); _STL_INTERNAL_CHECK(!_Specs._Localized); - // Since the bit width of 0 is 0x0, special-case it instead of complicating the math even more. - int _Width = 3; - if (_Value != nullptr) { - // Compute the bit width of the pointer (i.e. how many bits it takes to be represented). - // Add 3 to the bit width so we always round up on the division. - // Divide that by the amount of bits a hexit represents (log2(16) = log2(2^4) = 4). - // Add 2 for the 0x prefix. - _Width = 2 + (_STD bit_width(reinterpret_cast(_Value)) + 3) / 4; + char _Buffer[2 * sizeof(void*)]; // 2 hexits per byte: 8 hexits for 32-bit, 16 hexits for 64-bit + const auto [_End, _Ec] = _STD to_chars(_Buffer, _STD end(_Buffer), reinterpret_cast(_Value), 16); + _STL_INTERNAL_CHECK(_Ec == errc{}); + const int _Width = 2 + static_cast(_End - _Buffer); + + _CharT _Prefix[2] = {_CharT{'0'}, _CharT{'x'}}; + if (_Specs._Type == 'P') { + _Prefix[1] = _CharT{'X'}; + _Buffer_to_uppercase(_Buffer, _End); + } + + const bool _Write_leading_zeroes = _Specs._Leading_zero && _Specs._Alignment == _Fmt_align::_None; + auto _Writer = [&](_OutputIt _Out) { + _Out = _RANGES copy(_Prefix, _Prefix + 2, _STD move(_Out)).out; + if (_Write_leading_zeroes && _Width < _Specs._Width) { + _Out = _RANGES fill_n(_STD move(_Out), _Specs._Width - _Width, _CharT{'0'}); + } + return _STD _Widen_and_copy<_CharT>(_Buffer, _End, _STD move(_Out)); + }; + + if (_Write_leading_zeroes) { + return _Writer(_STD move(_Out)); } - return _Write_aligned(_STD move(_Out), _Width, _Specs, _Fmt_align::_Right, - [=](_OutputIt _Out) { return _Fmt_write<_CharT>(_STD move(_Out), _Value); }); + return _STD _Write_aligned(_STD move(_Out), _Width, _Specs, _Fmt_align::_Right, _Writer); } template @@ -3688,18 +3721,20 @@ _EXPORT_STD using format_args = basic_format_args; _EXPORT_STD using wformat_args = basic_format_args; _EXPORT_STD template -_NODISCARD auto make_format_args(_Args&&... _Vals) { - static_assert((_Formattable_with, _Context> && ...), +_NODISCARD auto make_format_args(_Args&... _Vals) { + // TRANSITION, should cite the new working draft + static_assert((_Formattable_with, _Context> && ...), "Cannot format an argument. To make type T formattable, provide a formatter specialization. " - "See N4950 [format.arg.store]/2 and [formatter.requirements]."); + "See N4964 [format.arg.store]/2 (along with modification in P2905R2) and [formatter.requirements]."); return _Format_arg_store<_Context, _Args...>{_Vals...}; } _EXPORT_STD template -_NODISCARD auto make_wformat_args(_Args&&... _Vals) { - static_assert((_Formattable_with, wformat_context> && ...), +_NODISCARD auto make_wformat_args(_Args&... _Vals) { + // TRANSITION, should cite the new working draft + static_assert((_Formattable_with, wformat_context> && ...), "Cannot format an argument. To make type T formattable, provide a formatter specialization. " - "See N4950 [format.arg.store]/2 and [formatter.requirements]."); + "See N4964 [format.arg.store]/2 (along with modification in P2905R2) and [formatter.requirements]."); return _Format_arg_store{_Vals...}; } diff --git a/stl/inc/istream b/stl/inc/istream index e1eb8648072..bf05c5a2a96 100644 --- a/stl/inc/istream +++ b/stl/inc/istream @@ -145,6 +145,10 @@ public: return this->good(); } +#pragma push_macro("ipfx") +#pragma push_macro("isfx") +#undef ipfx +#undef isfx // TRANSITION, ABI: non-Standard ipfx() is preserved for binary compatibility _DEPRECATE_IO_PFX_SFX bool __CLR_OR_THIS_CALL ipfx(bool _Noskip = false) { // test stream state and skip whitespace as needed @@ -153,6 +157,8 @@ public: // TRANSITION, ABI: non-Standard isfx() is preserved for binary compatibility _DEPRECATE_IO_PFX_SFX void __CLR_OR_THIS_CALL isfx() {} // perform any wrapup +#pragma pop_macro("isfx") +#pragma pop_macro("ipfx") #ifdef _M_CEE_PURE basic_istream& __CLR_OR_THIS_CALL operator>>(basic_istream&(__clrcall* _Pfn)(basic_istream&) ) { diff --git a/stl/inc/iterator b/stl/inc/iterator index 47c3531806d..01d6a22c2d1 100644 --- a/stl/inc/iterator +++ b/stl/inc/iterator @@ -1467,6 +1467,17 @@ struct iterator_traits> : iterator_traits<_Iter> { _STD_END +#pragma push_macro("stdext") +#pragma push_macro("checked_array_iterator") +#pragma push_macro("make_checked_array_iterator") +#pragma push_macro("make_unchecked_array_iterator") +#pragma push_macro("unchecked_array_iterator") +#undef stdext +#undef checked_array_iterator +#undef make_checked_array_iterator +#undef make_unchecked_array_iterator +#undef unchecked_array_iterator + _STDEXT_BEGIN template class _DEPRECATE_STDEXT_ARR_ITERS checked_array_iterator { // wrap a pointer with checking @@ -1857,6 +1868,12 @@ _STL_RESTORE_DEPRECATED_WARNING _STD_END #endif // _HAS_CXX20 +#pragma pop_macro("unchecked_array_iterator") +#pragma pop_macro("make_unchecked_array_iterator") +#pragma pop_macro("make_checked_array_iterator") +#pragma pop_macro("checked_array_iterator") +#pragma pop_macro("stdext") + #pragma pop_macro("new") _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) diff --git a/stl/inc/memory b/stl/inc/memory index d982b01e72f..8b528752403 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -1464,12 +1464,31 @@ struct _Can_call_function_object : false_type {}; template struct _Can_call_function_object<_Fx, _Arg, void_t()(_STD declval<_Arg>()))>> : true_type {}; -template +template struct _SP_convertible : is_convertible<_Yty*, _Ty*>::type {}; + +template +struct _SP_convertible<_Yty, _Uty[], _Void> : false_type {}; +#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-10548086 +template +struct _SP_convertible<_Yty, _Uty[], void_t<_Yty (*)[]>> : is_convertible<_Yty (*)[], _Uty (*)[]>::type {}; +#else // ^^^ no workaround / workaround vvv template -struct _SP_convertible<_Yty, _Uty[]> : is_convertible<_Yty (*)[], _Uty (*)[]>::type {}; +struct _SP_convertible<_Yty, _Uty[], void_t(nullptr))>> + : is_convertible<_Yty (*)[], _Uty (*)[]>::type {}; +#endif // ^^^ workaround ^^^ + +template +struct _SP_convertible<_Yty, _Uty[_Ext], _Void> : false_type {}; +#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-10548086 +template +struct _SP_convertible<_Yty, _Uty[_Ext], void_t<_Yty (*)[_Ext]>> + : is_convertible<_Yty (*)[_Ext], _Uty (*)[_Ext]>::type {}; +#else // ^^^ no workaround / workaround vvv template -struct _SP_convertible<_Yty, _Uty[_Ext]> : is_convertible<_Yty (*)[_Ext], _Uty (*)[_Ext]>::type {}; +struct _SP_convertible<_Yty, _Uty[_Ext], void_t(nullptr))>> + : is_convertible<_Yty (*)[_Ext], _Uty (*)[_Ext]>::type {}; +#endif // ^^^ workaround ^^^ template struct _SP_pointer_compatible : is_convertible<_Yty*, _Ty*>::type { diff --git a/stl/inc/ostream b/stl/inc/ostream index de3b0a0feef..1276eb0dc1a 100644 --- a/stl/inc/ostream +++ b/stl/inc/ostream @@ -136,6 +136,10 @@ public: bool _Ok; // true if stream state okay at construction }; +#pragma push_macro("opfx") +#pragma push_macro("osfx") +#undef opfx +#undef osfx // TRANSITION, ABI: non-Standard opfx() is preserved for binary compatibility _DEPRECATE_IO_PFX_SFX bool __CLR_OR_THIS_CALL opfx() { // test stream state and flush tie stream as needed if (!this->good()) { @@ -155,6 +159,8 @@ public: _DEPRECATE_IO_PFX_SFX void __CLR_OR_THIS_CALL osfx() noexcept { // perform any wrapup _Osfx(); } +#pragma pop_macro("osfx") +#pragma pop_macro("opfx") void __CLR_OR_THIS_CALL _Osfx() noexcept { // perform any wrapup _TRY_BEGIN @@ -1244,11 +1250,9 @@ void _Print_impl(const _Add_newline _Add_nl, ostream& _Ostr, const format_string if constexpr (_Has_format_args) { if constexpr (_STD _Is_ordinary_literal_encoding_utf8()) { - _STD _Vprint_unicode_impl( - _Add_nl, _Ostr, _Fmt.get(), _STD make_format_args(_STD forward<_Types>(_Args)...)); + _STD _Vprint_unicode_impl(_Add_nl, _Ostr, _Fmt.get(), _STD make_format_args(_Args...)); } else { - _STD _Vprint_nonunicode_impl( - _Add_nl, _Ostr, _Fmt.get(), _STD make_format_args(_STD forward<_Types>(_Args)...)); + _STD _Vprint_nonunicode_impl(_Add_nl, _Ostr, _Fmt.get(), _STD make_format_args(_Args...)); } } else { const string _Unescaped_str{_Unescape_braces(_Add_nl, _Fmt.get())}; diff --git a/stl/inc/print b/stl/inc/print index 0e4c090925b..312962f2f6c 100644 --- a/stl/inc/print +++ b/stl/inc/print @@ -101,11 +101,9 @@ void _Print_impl( if constexpr (_Has_format_args) { if constexpr (_STD _Is_ordinary_literal_encoding_utf8()) { - _STD _Vprint_unicode_impl( - _Add_nl, _Stream, _Fmt.get(), _STD make_format_args(_STD forward<_Types>(_Args)...)); + _STD _Vprint_unicode_impl(_Add_nl, _Stream, _Fmt.get(), _STD make_format_args(_Args...)); } else { - _STD _Vprint_nonunicode_impl( - _Add_nl, _Stream, _Fmt.get(), _STD make_format_args(_STD forward<_Types>(_Args)...)); + _STD _Vprint_nonunicode_impl(_Add_nl, _Stream, _Fmt.get(), _STD make_format_args(_Args...)); } } else { const string _Unescaped_str{_Unescape_braces(_Add_nl, _Fmt.get())}; diff --git a/stl/inc/random b/stl/inc/random index fd52ee177e7..372f965c9da 100644 --- a/stl/inc/random +++ b/stl/inc/random @@ -24,6 +24,20 @@ _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new +// TRANSITION, GH-183 +#pragma push_macro("discard_block") +#pragma push_macro("linear_congruential") +#pragma push_macro("mersenne_twister") +#pragma push_macro("subtract_with_carry") +#pragma push_macro("uniform_int") +#pragma push_macro("uniform_real") +#undef discard_block +#undef linear_congruential +#undef mersenne_twister +#undef subtract_with_carry +#undef uniform_int +#undef uniform_real + #ifdef _ALLOW_RANDOM_DISTRIBUTION_CONST_OPERATOR #define _DISTRIBUTION_CONST const #else @@ -5370,6 +5384,14 @@ _STD_END #undef _NRAND #undef _DISTRIBUTION_CONST +// TRANSITION, GH-183 +#pragma pop_macro("uniform_real") +#pragma pop_macro("uniform_int") +#pragma pop_macro("subtract_with_carry") +#pragma pop_macro("mersenne_twister") +#pragma pop_macro("linear_congruential") +#pragma pop_macro("discard_block") + #pragma pop_macro("new") _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) diff --git a/stl/inc/ranges b/stl/inc/ranges index 7d4ffc38d7f..f1274fdcc6c 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -3809,9 +3809,7 @@ namespace ranges { using value_type = range_value_t<_InnerRng<_Const>>; using difference_type = common_type_t, range_difference_t<_InnerRng<_Const>>>; - // clang-format off - _Iterator() requires default_initializable<_OuterIter> = default; - // clang-format on + _Iterator() = default; constexpr _Iterator(_Iterator _It) requires _Const && convertible_to, _OuterIter> @@ -4265,9 +4263,7 @@ namespace ranges { using value_type = common_type_t, iter_value_t<_PatternIter>>; using difference_type = _Common_diff_t<_OuterIter, _InnerIter, _PatternIter>; - // clang-format off - _Iterator() requires default_initializable<_OuterIter> = default; - // clang-format on + _Iterator() = default; constexpr _Iterator(_Iterator _It) requires _Const && convertible_to, _OuterIter> diff --git a/stl/inc/type_traits b/stl/inc/type_traits index 2e67ff7b97e..3071d591977 100644 --- a/stl/inc/type_traits +++ b/stl/inc/type_traits @@ -1746,7 +1746,12 @@ constexpr auto invoke(_Callable&& _Obj, _Ty1&& _Arg1, _Types2&&... _Args2) noexc } else if constexpr (_Invoker1<_Callable, _Ty1>::_Strategy == _Invoker_strategy::_Pmd_object) { return static_cast<_Ty1&&>(_Arg1).*_Obj; } else if constexpr (_Invoker1<_Callable, _Ty1>::_Strategy == _Invoker_strategy::_Pmd_refwrap) { +#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-10543093 return _Arg1.get().*_Obj; +#else // ^^^ no workaround / workaround vvv + auto& _Ref = _Arg1.get(); + return _Ref.*_Obj; +#endif // ^^^ workaround ^^^ } else { _STL_INTERNAL_STATIC_ASSERT(_Invoker1<_Callable, _Ty1>::_Strategy == _Invoker_strategy::_Pmd_pointer); return (*static_cast<_Ty1&&>(_Arg1)).*_Obj; diff --git a/stl/inc/typeindex b/stl/inc/typeindex index 3c302c79e23..3e1effeaf78 100644 --- a/stl/inc/typeindex +++ b/stl/inc/typeindex @@ -45,7 +45,10 @@ public: return strong_ordering::equal; } +#pragma push_macro("raw_name") // TRANSITION, GH-2195 +#undef raw_name return _CSTD strcmp(_Tptr->raw_name() + 1, _Right._Tptr->raw_name() + 1) <=> 0; +#pragma pop_macro("raw_name") } #else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD bool operator!=(const type_index& _Right) const noexcept { diff --git a/stl/inc/typeinfo b/stl/inc/typeinfo index 20c07d5863f..de903e92e17 100644 --- a/stl/inc/typeinfo +++ b/stl/inc/typeinfo @@ -18,7 +18,10 @@ _STL_DISABLE_CLANG_WARNINGS #pragma warning(disable : 4275) // non dll-interface class 'X' used as base for dll-interface class 'Y' +#pragma push_macro("raw_name") // TRANSITION, GH-2195 +#undef raw_name #include +#pragma pop_macro("raw_name") _EXPORT_STD extern "C++" class type_info; // for typeid, MSVC looks for type_info in the global namespace diff --git a/stl/inc/vector b/stl/inc/vector index c703d95aca2..e0010eef8b9 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -169,7 +169,7 @@ public: #if _HAS_CXX20 _NODISCARD constexpr strong_ordering operator<=>(const _Vector_const_iterator& _Right) const noexcept { _Compat(_Right); - return _Unfancy(_Ptr) <=> _Unfancy(_Right._Ptr); + return _STD _Unfancy_maybe_null(_Ptr) <=> _STD _Unfancy_maybe_null(_Right._Ptr); } #else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD bool operator!=(const _Vector_const_iterator& _Right) const noexcept { @@ -214,11 +214,11 @@ public: using _Prevent_inheriting_unwrap = _Vector_const_iterator; _NODISCARD _CONSTEXPR20 const value_type* _Unwrapped() const noexcept { - return _Unfancy(_Ptr); + return _STD _Unfancy_maybe_null(_Ptr); } _CONSTEXPR20 void _Seek_to(const value_type* _It) noexcept { - _Ptr = _Refancy<_Tptr>(const_cast(_It)); + _Ptr = _STD _Refancy_maybe_null<_Tptr>(const_cast(_It)); } _Tptr _Ptr; // pointer to element in vector @@ -342,7 +342,7 @@ public: using _Prevent_inheriting_unwrap = _Vector_iterator; _NODISCARD _CONSTEXPR20 value_type* _Unwrapped() const noexcept { - return _Unfancy(this->_Ptr); + return _STD _Unfancy_maybe_null(this->_Ptr); } }; @@ -425,17 +425,6 @@ public: pointer _Myend; // pointer to end of array }; -template -constexpr auto _Unfancy_maybe_null(_Ptrty _Ptr) noexcept { - // converts from a (potentially null) fancy pointer to a plain pointer - return _Ptr ? _STD addressof(*_Ptr) : nullptr; -} - -template -constexpr _Ty* _Unfancy_maybe_null(_Ty* _Ptr) noexcept { // do nothing for plain pointers - return _Ptr; -} - _EXPORT_STD template > class vector { // varying size array of values private: @@ -2496,15 +2485,12 @@ protected: template class _Vb_const_iterator : public _Vb_iter_base<_Alvbase_wrapped> { -private: - using _Mybase = _Vb_iter_base<_Alvbase_wrapped>; - public: - using _Mycont = typename _Mybase::_Mycont; - using _Difference_type = typename _Mybase::_Difference_type; - using _Size_type = typename _Mybase::_Size_type; - using _Reft = _Vb_reference<_Alvbase_wrapped>; - using const_reference = bool; + using _Mybase = _Vb_iter_base<_Alvbase_wrapped>; + using _Mycont = typename _Mybase::_Mycont; + using _Size_type = typename _Mybase::_Size_type; + using _Reft = _Vb_reference<_Alvbase_wrapped>; + using const_reference = bool; using iterator_category = random_access_iterator_tag; using value_type = bool; @@ -2521,7 +2507,7 @@ public: #if _ITERATOR_DEBUG_LEVEL != 0 const auto _Cont = static_cast(this->_Getcont()); _STL_VERIFY(_Cont, "cannot dereference value-initialized vector iterator"); - _STL_VERIFY(this->_Total_off(_Cont) < static_cast<_Difference_type>(_Cont->_Mysize), + _STL_VERIFY(this->_Total_off(_Cont) < static_cast(_Cont->_Mysize), "vector iterator not dereferenceable"); #endif // _ITERATOR_DEBUG_LEVEL != 0 @@ -2559,7 +2545,7 @@ public: if (_Off < 0) { _STL_VERIFY(-_Start_offset <= _Off, "cannot seek vector iterator before begin"); } else if (0 < _Off) { - _STL_VERIFY(_Off <= static_cast<_Difference_type>(_Cont->_Mysize - _Start_offset), + _STL_VERIFY(_Off <= static_cast(_Cont->_Mysize - _Start_offset), "cannot seek vector iterator after end"); } } @@ -2682,7 +2668,7 @@ public: #if _ITERATOR_DEBUG_LEVEL != 0 const auto _Cont = static_cast(this->_Getcont()); _STL_VERIFY(_Cont, "cannot increment value-initialized vector iterator"); - _STL_VERIFY(this->_Total_off(_Cont) < static_cast<_Difference_type>(_Cont->_Mysize), + _STL_VERIFY(this->_Total_off(_Cont) < static_cast(_Cont->_Mysize), "cannot increment vector end iterator"); #endif // _ITERATOR_DEBUG_LEVEL != 0 @@ -2698,9 +2684,8 @@ public: template class _Vb_iterator : public _Vb_const_iterator<_Alvbase_wrapped> { public: - using _Mybase = _Vb_const_iterator<_Alvbase_wrapped>; - using _Mycont = typename _Mybase::_Mycont; - using _Difference_type = typename _Mybase::_Difference_type; + using _Mybase = _Vb_const_iterator<_Alvbase_wrapped>; + using _Mycont = typename _Mybase::_Mycont; using _Reft = _Vb_reference<_Alvbase_wrapped>; using const_reference = bool; @@ -2717,7 +2702,7 @@ public: #if _ITERATOR_DEBUG_LEVEL != 0 const auto _Cont = static_cast(this->_Getcont()); _STL_VERIFY(_Cont, "cannot dereference value-initialized vector iterator"); - _STL_VERIFY(this->_Total_off(_Cont) < static_cast<_Difference_type>(_Cont->_Mysize), + _STL_VERIFY(this->_Total_off(_Cont) < static_cast(_Cont->_Mysize), "vector iterator not dereferenceable"); #endif // _ITERATOR_DEBUG_LEVEL != 0 @@ -3512,7 +3497,7 @@ public: _Iterator_base12** _Pnext = &this->_Myproxy->_Myfirstiter; while (*_Pnext) { // test offset from beginning of vector - const auto& _Pnextiter = static_cast(**_Pnext); + const auto& _Pnextiter = static_cast(**_Pnext); const auto _Temp = *_Pnext; if (!_Pnextiter._Myptr) { // orphan the iterator _Temp->_Myproxy = nullptr; diff --git a/stl/inc/xcharconv_ryu.h b/stl/inc/xcharconv_ryu.h index 0e97cb1231b..d2f8db8dd62 100644 --- a/stl/inc/xcharconv_ryu.h +++ b/stl/inc/xcharconv_ryu.h @@ -193,28 +193,19 @@ _NODISCARD __forceinline uint64_t __ryu_umul128(const uint64_t __a, const uint64 #endif // ^^^ intrinsics unavailable ^^^ _NODISCARD inline uint64_t __ryu_shiftright128(const uint64_t __lo, const uint64_t __hi, const uint32_t __dist) { -#if defined(_M_X64) && !defined(_M_ARM64EC) - // For the __shiftright128 intrinsic, the shift value is always - // modulo 64. - // In the current implementation of the double-precision version - // of Ryu, the shift value is always < 64. - // (The shift value is in the range [49, 58].) - // Check this here in case a future change requires larger shift - // values. In this case this function needs to be adjusted. + // In the current implementation, the shift value is always < 64. + // If larger shift values are ever required, this function will need to be adjusted. _STL_INTERNAL_CHECK(__dist < 64); + +#if defined(_M_X64) && !defined(_M_ARM64EC) return __shiftright128(__lo, __hi, static_cast(__dist)); -#else // ^^^ defined(_M_X64) && !defined(_M_ARM64EC) / !defined(_M_X64) || defined(_M_ARM64EC) vvv - // We don't need to handle the case __dist >= 64 here (see above). - _STL_INTERNAL_CHECK(__dist < 64); -#if defined(_WIN64) || defined(_M_HYBRID_X86_ARM64) - _STL_INTERNAL_CHECK(__dist > 0); +#else // ^^^ __shiftright128 intrinsic available / __shiftright128 intrinsic unavailable vvv + if (__dist == 0) { + return __lo; + } + return (__hi << (64 - __dist)) | (__lo >> __dist); -#else // ^^^ 64-bit or _M_HYBRID_X86_ARM64 / 32-bit vvv - // Avoid a 64-bit shift by taking advantage of the range of shift values. - _STL_INTERNAL_CHECK(__dist >= 32); - return (__hi << (64 - __dist)) | (static_cast(__lo >> 32) >> (__dist - 32)); -#endif // ^^^ 32-bit ^^^ -#endif // defined(_M_X64) && !defined(_M_ARM64EC) +#endif // ^^^ __shiftright128 intrinsic unavailable ^^^ } diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 3235bd5702f..8e027db3e7e 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -36,6 +36,17 @@ _NODISCARD constexpr _Ty* _Unfancy(_Ty* _Ptr) noexcept { // do nothing for plain return _Ptr; } +template +constexpr auto _Unfancy_maybe_null(_Ptrty _Ptr) noexcept { + // converts from a (potentially null) fancy pointer to a plain pointer + return _Ptr ? _STD addressof(*_Ptr) : nullptr; +} + +template +constexpr _Ty* _Unfancy_maybe_null(_Ty* _Ptr) noexcept { // do nothing for plain pointers + return _Ptr; +} + template struct _NODISCARD _Tidy_guard { // class with destructor that calls _Tidy _Ty* _Target; @@ -300,6 +311,16 @@ _CONSTEXPR20 _Pointer _Refancy(_Pointer _Ptr) noexcept { return _Ptr; } +template , int> = 0> +_CONSTEXPR20 _Pointer _Refancy_maybe_null(typename pointer_traits<_Pointer>::element_type* _Ptr) noexcept { + return _Ptr == nullptr ? _Pointer() : pointer_traits<_Pointer>::pointer_to(*_Ptr); +} + +template , int> = 0> +_CONSTEXPR20 _Pointer _Refancy_maybe_null(_Pointer _Ptr) noexcept { + return _Ptr; +} + template _CONSTEXPR20 void _Destroy_range(_NoThrowFwdIt _First, _NoThrowSentinel _Last) noexcept; diff --git a/stl/inc/xstring b/stl/inc/xstring index 6bc2b3d35c3..034315462ac 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -1984,7 +1984,7 @@ public: #if _HAS_CXX20 _NODISCARD constexpr strong_ordering operator<=>(const _String_const_iterator& _Right) const noexcept { _Compat(_Right); - return _Unfancy(_Ptr) <=> _Unfancy(_Right._Ptr); + return _STD _Unfancy_maybe_null(_Ptr) <=> _STD _Unfancy_maybe_null(_Right._Ptr); } #else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD bool operator!=(const _String_const_iterator& _Right) const noexcept { @@ -2030,11 +2030,11 @@ public: using _Prevent_inheriting_unwrap = _String_const_iterator; _NODISCARD _CONSTEXPR20 const value_type* _Unwrapped() const noexcept { - return _Unfancy(_Ptr); + return _STD _Unfancy_maybe_null(_Ptr); } _CONSTEXPR20 void _Seek_to(const value_type* _It) noexcept { - _Ptr = _Refancy(const_cast(_It)); + _Ptr = _STD _Refancy_maybe_null(const_cast(_It)); } pointer _Ptr; // pointer to element in string @@ -2154,7 +2154,7 @@ public: using _Prevent_inheriting_unwrap = _String_iterator; _NODISCARD _CONSTEXPR20 value_type* _Unwrapped() const noexcept { - return const_cast(_Unfancy(this->_Ptr)); + return const_cast(_STD _Unfancy_maybe_null(this->_Ptr)); } }; diff --git a/stl/inc/xthreads.h b/stl/inc/xthreads.h index 8eddb38e0f3..984bd75087a 100644 --- a/stl/inc/xthreads.h +++ b/stl/inc/xthreads.h @@ -33,19 +33,19 @@ struct _Stl_critical_section { }; struct _Mtx_internal_imp_t { -#ifdef _CRT_WINDOWS +#if defined(_CRT_WINDOWS) || defined(UNDOCKED_WINDOWS_UCRT) #ifdef _WIN64 static constexpr size_t _Critical_section_size = 16; #else // ^^^ defined(_WIN64) / !defined(_WIN64) vvv static constexpr size_t _Critical_section_size = 8; #endif // ^^^ !defined(_WIN64) ^^^ -#else // ^^^ defined(_CRT_WINDOWS) / !defined(_CRT_WINDOWS) vvv +#else // ^^^ Windows private STL / public STL vvv #ifdef _WIN64 static constexpr size_t _Critical_section_size = 64; #else // ^^^ defined(_WIN64) / !defined(_WIN64) vvv static constexpr size_t _Critical_section_size = 36; #endif // ^^^ !defined(_WIN64) ^^^ -#endif // ^^^ !defined(_CRT_WINDOWS) ^^^ +#endif // ^^^ public STL ^^^ static constexpr size_t _Critical_section_align = alignof(void*); diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index aa907237966..9d15a3139cb 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -296,6 +296,7 @@ // P2432R1 Fix istream_view // P2465R3 Standard Library Modules std And std.compat // P2508R1 basic_format_string, format_string, wformat_string +// P2510R3 Formatting Pointers // P2520R0 move_iterator Should Be A Random-Access Iterator // P2538R1 ADL-Proof projected // P2572R1 std::format Fill Character Allowances @@ -307,6 +308,7 @@ // P2711R1 Making Multi-Param Constructors Of Views explicit // P2736R2 Referencing The Unicode Standard // P2770R0 Stashing Stashing Iterators For Proper Flattening +// P2905R2 Runtime Format Strings // P2909R4 Fix Formatting Of Code Units As Integers // _HAS_CXX20 indirectly controls: @@ -882,7 +884,7 @@ #define _CPPLIB_VER 650 #define _MSVC_STL_VERSION 143 -#define _MSVC_STL_UPDATE 202312L +#define _MSVC_STL_UPDATE 202401L #ifndef _ALLOW_COMPILER_AND_STL_VERSION_MISMATCH #if defined(__CUDACC__) && defined(__CUDACC_VER_MAJOR__) @@ -1751,7 +1753,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #define __cpp_lib_erase_if 202002L #ifdef __cpp_lib_concepts -#define __cpp_lib_format 202207L +#define __cpp_lib_format 202304L #define __cpp_lib_format_uchar 202311L #define __cpp_lib_freestanding_ranges 202306L #endif // defined(__cpp_lib_concepts) @@ -1981,7 +1983,9 @@ compiler option, or define _ALLOW_RTCc_IN_STL to suppress this error. #define _CHRONO ::std::chrono:: #define _RANGES ::std::ranges:: -// We use the stdext (standard extension) namespace to contain extensions that are not part of the current standard +// We use the stdext (standard extension) namespace to contain non-standard extensions +#pragma push_macro("stdext") +#undef stdext #define _STDEXT_BEGIN \ _EXTERN_CXX_WORKAROUND \ namespace stdext { @@ -1990,6 +1994,7 @@ compiler option, or define _ALLOW_RTCc_IN_STL to suppress this error. _END_EXTERN_CXX_WORKAROUND #define _STDEXT ::stdext:: +#pragma pop_macro("stdext") #define _CSTD :: diff --git a/stl/src/atomic_wait.cpp b/stl/src/atomic_wait.cpp index 24344533021..123e8dcf57b 100644 --- a/stl/src/atomic_wait.cpp +++ b/stl/src/atomic_wait.cpp @@ -119,7 +119,8 @@ namespace { void _Force_wait_functions_srwlock_only() noexcept { auto _Local = _Wait_functions._Api_level.load(_STD memory_order_acquire); if (_Local <= __std_atomic_api_level::__detecting) { - while (!_Wait_functions._Api_level.compare_exchange_weak(_Local, __std_atomic_api_level::__has_srwlock)) { + while (!_Wait_functions._Api_level.compare_exchange_weak( + _Local, __std_atomic_api_level::__has_srwlock, _STD memory_order_acq_rel)) { if (_Local > __std_atomic_api_level::__detecting) { return; } @@ -128,7 +129,8 @@ namespace { } [[nodiscard]] __std_atomic_api_level _Init_wait_functions(__std_atomic_api_level _Level) { - while (!_Wait_functions._Api_level.compare_exchange_weak(_Level, __std_atomic_api_level::__detecting)) { + while (!_Wait_functions._Api_level.compare_exchange_weak( + _Level, __std_atomic_api_level::__detecting, _STD memory_order_acq_rel)) { if (_Level > __std_atomic_api_level::__detecting) { return _Level; } diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index aa7119f94a3..66a43575fd1 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -53,7 +53,8 @@ std/utilities/memory/default.allocator/allocator.ctor.pass.cpp FAIL # LWG-3197 "std::prev should not require BidirectionalIterator" (New) std/iterators/iterator.primitives/iterator.operations/prev.pass.cpp FAIL -# Itanium ABI assumptions that current_exception and rethrow_exception don't copy the exception object +# Itanium ABI assumptions that current_exception and rethrow_exception don't copy the exception object. +# The SKIPPED tests contain `XFAIL: msvc`, which is not well understood by the test harness. std/language.support/support.exception/except.nested/rethrow_if_nested.pass.cpp SKIPPED std/language.support/support.exception/propagation/current_exception.pass.cpp SKIPPED std/language.support/support.exception/propagation/make_exception_ptr.pass.cpp FAIL @@ -120,9 +121,12 @@ std/utilities/memory/allocator.traits/allocate_at_least.pass.cpp FAIL # libc++ has not implemented P2937R0: "Freestanding Library: Remove strtok" std/language.support/support.limits/support.limits.general/cstring.version.compile.pass.cpp FAIL -# Various bogosity (LLVM-D141004) +# Various bogosity (LLVM-D141004), warning C6011: Dereferencing NULL pointer +# Note: The :1 (ASAN) configuration doesn't run static analysis. std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/construct_pair_const_lvalue_pair.pass.cpp:0 FAIL std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/construct_pair_values.pass.cpp:0 FAIL + +# Various bogosity (LLVM-D141004) std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/resource.pass.cpp FAIL std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/select_on_container_copy_construction.pass.cpp FAIL std/utilities/utility/mem.res/mem.res.pool/mem.res.pool.ctor/ctor_does_not_allocate.pass.cpp FAIL @@ -204,6 +208,12 @@ std/language.support/support.dynamic/new.delete/new.delete.single/new.size_nothr # ASAN runtime intercepts `strtol` and breaks LWG-2009 (VSO-1875597) std/strings/string.conversions/stol.pass.cpp:1 FAIL +# Not analyzed. SKIPPED because this is x86-specific. +# ERROR: AddressSanitizer: allocator is out of memory trying to allocate NNNN bytes +std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_strong.pass.cpp:1 SKIPPED +std/atomics/atomics.types.generic/atomics.types.float/compare_exchange_weak.pass.cpp:1 SKIPPED +std/input.output/syncstream/osyncstream/thread/several_threads.pass.cpp:1 SKIPPED + # *** MISSING STL FEATURES *** # Missing mbrtoc8 and c8rtomb @@ -284,12 +294,6 @@ std/language.support/support.limits/support.limits.general/charconv.version.comp std/utilities/charconv/charconv.syn/from_chars_result.operator_bool.pass.cpp FAIL std/utilities/charconv/charconv.syn/to_chars_result.operator_bool.pass.cpp FAIL -# P2510R3 Formatting Pointers -std/utilities/format/format.functions/format.locale.pass.cpp FAIL -std/utilities/format/format.functions/format.pass.cpp FAIL -std/utilities/format/format.functions/vformat.locale.pass.cpp FAIL -std/utilities/format/format.functions/vformat.pass.cpp FAIL - # P2587R3 Redefining to_string To Use to_chars std/language.support/support.limits/support.limits.general/string.version.compile.pass.cpp FAIL @@ -308,10 +312,6 @@ std/language.support/support.limits/support.limits.general/utility.version.compi std/language.support/support.limits/support.limits.general/expected.version.compile.pass.cpp FAIL std/language.support/support.limits/support.limits.general/mdspan.version.compile.pass.cpp FAIL -# P2905R2 Runtime Format Strings -std/utilities/format/format.arguments/format.arg.store/make_format_args.pass.cpp FAIL -std/utilities/format/format.arguments/format.arg.store/make_wformat_args.pass.cpp FAIL - # P2918R2 Runtime Format Strings II std/utilities/format/format.fmt.string/ctor.runtime-format-string.pass.cpp FAIL std/utilities/format/format.functions/format.locale.runtime_format.pass.cpp FAIL @@ -320,9 +320,6 @@ std/utilities/format/format.syn/runtime_format_string.pass.cpp FAIL # *** MISSING COMPILER FEATURES *** -# MSVC doesn't properly support [[no_unique_address]] -std/algorithms/algorithms.results/no_unique_address.compile.pass.cpp SKIPPED - # P1169R4 static operator() std/thread/futures/futures.task/futures.task.members/ctad.static.compile.pass.cpp:0 FAIL std/thread/futures/futures.task/futures.task.members/ctad.static.compile.pass.cpp:1 FAIL @@ -350,9 +347,6 @@ std/thread/futures/futures.promise/set_value_at_thread_exit_const.pass.cpp FAIL std/thread/futures/futures.promise/set_value_at_thread_exit_void.pass.cpp FAIL std/thread/futures/futures.task/futures.task.members/make_ready_at_thread_exit.pass.cpp FAIL -# libc++ speculatively implements LWG-3633 "Atomics are copy constructible and copy assignable from volatile atomics" -std/atomics/atomics.types.generic/copy_semantics_traits.pass.cpp FAIL - # *** C1XX COMPILER BUGS *** # DevCom-409222 VSO-752709 "Constructing rvalue reference from non-reference-related lvalue reference" @@ -361,6 +355,7 @@ std/utilities/meta/meta.unary/meta.unary.prop/is_constructible.pass.cpp:0 FAIL std/utilities/meta/meta.unary/meta.unary.prop/is_constructible.pass.cpp:1 FAIL # VSO-1271673 "static analyzer doesn't know about short-circuiting" +# Note: The :1 (ASAN) configuration doesn't run static analysis. std/algorithms/alg.sorting/alg.sort/partial.sort/partial_sort.pass.cpp:0 FAIL std/algorithms/alg.sorting/alg.sort/partial.sort/partial_sort_comp.pass.cpp:0 FAIL @@ -390,6 +385,7 @@ std/algorithms/robust_re_difference_type.compile.pass.cpp:1 FAIL # DevCom-1638563 VSO-1457836: icky static analysis false positive # Resolved wontfix, need to report again. +# Note: The :1 (ASAN) configuration doesn't run static analysis. std/language.support/support.coroutines/end.to.end/go.pass.cpp:0 FAIL # DevCom-1638496 VSO-1462745: C1XX doesn't properly reject int <=> unsigned @@ -531,7 +527,7 @@ std/ranges/range.factories/range.repeat.view/iterator/minus.pass.cpp:1 FAIL # *** VCRUNTIME BUGS *** # DevCom-10373274 VSO-1824997 "vcruntime nothrow array operator new falls back on the wrong function" -# This passes for ASAN, surprisingly. +# This passes for the :1 (ASAN) configuration, surprisingly. std/language.support/support.dynamic/new.delete/new.delete.array/new.size_nothrow.replace.indirect.pass.cpp:0 FAIL std/language.support/support.dynamic/new.delete/new.delete.array/new.size_nothrow.replace.indirect.pass.cpp:2 FAIL @@ -869,10 +865,6 @@ std/containers/sequences/deque/deque.modifiers/insert_iter_iter.pass.cpp SKIPPED # Not analyzed. Failing after LLVM-D75622. std/re/re.const/re.matchflag/match_prev_avail.pass.cpp FAIL -# Not analyzed. Possibly C1XX constexpr bug. -std/utilities/function.objects/func.invoke/invoke_constexpr.pass.cpp:0 FAIL -std/utilities/function.objects/func.invoke/invoke_constexpr.pass.cpp:1 FAIL - # Not analyzed. Failing for "[a[.ch.]z]". std/re/re.alg/re.alg.match/awk.locale.pass.cpp FAIL std/re/re.alg/re.alg.match/basic.locale.pass.cpp FAIL @@ -924,16 +916,23 @@ std/numerics/rand/rand.dist/rand.dist.pois/rand.dist.pois.poisson/eval.pass.cpp std/language.support/support.limits/limits/numeric.limits.members/tinyness_before.pass.cpp SKIPPED # Not analyzed. the test fails on x64 but passes on x86 +# warning C4267: 'initializing': conversion from 'size_t' to 'int', possible loss of data +# warning C4244: 'initializing': conversion from 'unsigned __int64' to 'int', possible loss of data std/containers/associative/map/map.access/iterator.pass.cpp:0 SKIPPED std/containers/associative/map/map.access/iterator.pass.cpp:1 SKIPPED + +# Not analyzed. the test fails on x64 but passes on x86 +# warning C4244: 'return': conversion from '__int64' to 'CustomIt::difference_type', possible loss of data std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.-/sentinel.pass.cpp:0 SKIPPED std/iterators/predef.iterators/move.iterators/move.iter.ops/move.iter.op.-/sentinel.pass.cpp:1 SKIPPED + +# Not analyzed. the test fails on x64 but passes on x86 +# warning C4267: 'initializing': conversion from 'size_t' to 'int', possible loss of data std/algorithms/alg.modifying.operations/alg.random.shuffle/ranges_shuffle.pass.cpp:0 SKIPPED std/algorithms/alg.modifying.operations/alg.random.shuffle/ranges_shuffle.pass.cpp:1 SKIPPED # Not analyzed. the test fails on x86 but passes on x64 -std/algorithms/alg.sorting/alg.set.operations/includes/ranges_includes.pass.cpp:0 SKIPPED -std/algorithms/alg.sorting/alg.set.operations/includes/ranges_includes.pass.cpp:1 SKIPPED +# warning C4389: '==': signed/unsigned mismatch std/algorithms/alg.modifying.operations/alg.unique/ranges_unique.pass.cpp:0 SKIPPED std/algorithms/alg.modifying.operations/alg.unique/ranges_unique.pass.cpp:1 SKIPPED @@ -989,10 +988,6 @@ std/containers/sequences/vector/vector.cons/construct_size_value.pass.cpp:1 FAIL std/containers/sequences/vector/vector.cons/construct_size_value_alloc.pass.cpp:0 FAIL std/containers/sequences/vector/vector.cons/construct_size_value_alloc.pass.cpp:1 FAIL -# Not analyzed. Possible STL bug, _Vb_reference derives from _Vb_iter_base and is adopted by the container, but _Orphan_range_unlocked assumes every child is a const_iterator. -# note: failure was caused by cast of object of dynamic type 'Ref' to type 'const std::_Vb_const_iterator>>' -std/containers/sequences/vector.bool/emplace_back.pass.cpp FAIL - # Not analyzed. Looks like a test bug, assuming that hash> is constexpr. std/containers/sequences/vector.bool/enabled_hash.pass.cpp FAIL std/containers/sequences/vector.bool/vector_bool.pass.cpp FAIL @@ -1002,14 +997,6 @@ std/containers/sequences/vector.bool/vector_bool.pass.cpp FAIL std/containers/sequences/vector/iterators.pass.cpp:0 FAIL std/containers/sequences/vector/iterators.pass.cpp:1 FAIL -# Not analyzed. -# error C4854: binding dereferenced null pointer to reference has undefined behavior -# note: while evaluating constexpr function 'std::_Refancy' -std/containers/sequences/vector/vector.erasure/erase.pass.cpp:0 FAIL -std/containers/sequences/vector/vector.erasure/erase.pass.cpp:1 FAIL -std/containers/sequences/vector/vector.erasure/erase_if.pass.cpp:0 FAIL -std/containers/sequences/vector/vector.erasure/erase_if.pass.cpp:1 FAIL - # Not analyzed. Inspecting shift operators for quoted(). std/input.output/iostream.format/quoted.manip/quoted_traits.compile.pass.cpp FAIL @@ -1136,6 +1123,7 @@ std/utilities/format/format.formatter/format.formatter.spec/formatter.unsigned_i # Not analyzed. Apparent false positives from static analysis where it thinks that array indexing is out of bounds. # warning C28020: The expression '0<=_Param_(1)&&_Param_(1)<=1-1' is not true at this call. +# Note: The :1 (ASAN) configuration doesn't run static analysis. std/containers/views/mdspan/extents/ctor_default.pass.cpp:0 FAIL std/containers/views/mdspan/extents/ctor_from_array.pass.cpp:0 FAIL std/containers/views/mdspan/extents/ctor_from_integral.pass.cpp:0 FAIL @@ -1155,6 +1143,7 @@ std/containers/views/mdspan/mdspan/ctor.dh_span.pass.cpp:0 FAIL # Not analyzed. Apparent false positives from static analysis where it thinks `new (std::nothrow)` could return null, despite an assert(). # warning C28182: Dereferencing NULL pointer. +# Note: The :1 (ASAN) configuration doesn't run static analysis. std/language.support/support.dynamic/new.delete/new.delete.array/new.size_align_nothrow.pass.cpp:0 FAIL std/language.support/support.dynamic/new.delete/new.delete.array/new.size_nothrow.pass.cpp:0 FAIL @@ -1182,11 +1171,17 @@ std/time/time.syn/formatter.sys_time.pass.cpp:2 FAIL # Not analyzed. constexpr evaluation fails with "vector iterators incompatible". std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp:0 FAIL +std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.pass.cpp:1 FAIL std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.segmented.pass.cpp:0 FAIL +std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_backward.segmented.pass.cpp:1 FAIL std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.segmented.pass.cpp:0 FAIL +std/algorithms/alg.modifying.operations/alg.copy/ranges.copy_n.segmented.pass.cpp:1 FAIL std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp:0 FAIL +std/algorithms/alg.modifying.operations/alg.copy/ranges.copy.segmented.pass.cpp:1 FAIL std/algorithms/alg.modifying.operations/alg.move/ranges.move_backward.segmented.pass.cpp:0 FAIL +std/algorithms/alg.modifying.operations/alg.move/ranges.move_backward.segmented.pass.cpp:1 FAIL std/algorithms/alg.modifying.operations/alg.move/ranges.move.segmented.pass.cpp:0 FAIL +std/algorithms/alg.modifying.operations/alg.move/ranges.move.segmented.pass.cpp:1 FAIL # Not analyzed. constexpr evaluation fails with note: subobject '_Val' is not initialized std/ranges/range.adaptors/range.join/end.pass.cpp:2 FAIL @@ -1195,27 +1190,33 @@ std/ranges/range.adaptors/range.join/range.join.sentinel/eq.pass.cpp:2 FAIL # Not analyzed. constexpr evaluation fails with note: failure was caused by out of range index MEOW; allowed range is 0 <= index < 2 std/ranges/range.adaptors/range.join/adaptor.pass.cpp:0 FAIL +std/ranges/range.adaptors/range.join/adaptor.pass.cpp:1 FAIL std/ranges/range.adaptors/range.join/range.join.iterator/increment.pass.cpp:0 FAIL +std/ranges/range.adaptors/range.join/range.join.iterator/increment.pass.cpp:1 FAIL # Not analyzed. constexpr evaluation fails in assert(*--iter == i). std/ranges/range.adaptors/range.join/range.join.iterator/decrement.pass.cpp:0 FAIL +std/ranges/range.adaptors/range.join/range.join.iterator/decrement.pass.cpp:1 FAIL # Not analyzed. constexpr evaluation fails in assert(*iter++ == i). std/ranges/range.adaptors/range.join/range.join.iterator/star.pass.cpp:0 FAIL +std/ranges/range.adaptors/range.join/range.join.iterator/star.pass.cpp:1 FAIL # Not analyzed. constexpr evaluation fails in ranges::swap. std/ranges/range.adaptors/range.join/range.join.iterator/iter.swap.pass.cpp:0 FAIL - -# Not analyzed. Fails static_assert(std::is_default_constructible_v). -std/ranges/range.adaptors/range.join/range.join.iterator/ctor.default.pass.cpp FAIL +std/ranges/range.adaptors/range.join/range.join.iterator/iter.swap.pass.cpp:1 FAIL # Not analyzed. constexpr evaluation fails in assert(element_moved == 1). std/ranges/range.adaptors/range.lazy.split/ctor.range.pass.cpp:0 FAIL +std/ranges/range.adaptors/range.lazy.split/ctor.range.pass.cpp:1 FAIL std/ranges/range.adaptors/range.split/ctor.range.pass.cpp:0 FAIL +std/ranges/range.adaptors/range.split/ctor.range.pass.cpp:1 FAIL # Not analyzed. constexpr evaluation fails in assert(view_moved == 1). std/ranges/range.adaptors/range.lazy.split/ctor.view.pass.cpp:0 FAIL +std/ranges/range.adaptors/range.lazy.split/ctor.view.pass.cpp:1 FAIL std/ranges/range.adaptors/range.split/ctor.view.pass.cpp:0 FAIL +std/ranges/range.adaptors/range.split/ctor.view.pass.cpp:1 FAIL # Not analyzed. Testing split_view with an empty pattern, might be an MSVC constexpr bug. std/ranges/range.adaptors/range.split/begin.pass.cpp:0 FAIL @@ -1227,16 +1228,6 @@ std/ranges/range.adaptors/range.split/general.pass.cpp:1 FAIL std/strings/char.traits/char.traits.specializations/char.traits.specializations.char/compare.pass.cpp:0 FAIL std/strings/char.traits/char.traits.specializations/char.traits.specializations.char/compare.pass.cpp:1 FAIL -# Not analyzed. static_assert failed: '!std::is_assignable_v&, const std::atomic&>' -# The Standard depicts `atomic& operator=(const atomic&) volatile = delete;` which we seem to be missing. -std/atomics/atomics.types.generic/atomics.types.float/copy.compile.pass.cpp FAIL - -# Not analyzed. -# MSVC error C2087: 'abstract declarator': missing subscript -# Clang error: array has incomplete element type 'int[]' -std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.const/pointer.pass.cpp FAIL -std/utilities/memory/util.smartptr/util.smartptr.shared/util.smartptr.shared.mod/reset_pointer.pass.cpp FAIL - # Not analyzed. SKIPPED because this is x86-specific, fatal error C1060: compiler is out of heap space std/algorithms/alg.modifying.operations/alg.transform/ranges.transform.binary.iterator.pass.cpp:0 SKIPPED std/algorithms/alg.modifying.operations/alg.transform/ranges.transform.binary.iterator.pass.cpp:1 SKIPPED @@ -1244,6 +1235,8 @@ std/algorithms/alg.modifying.operations/alg.transform/ranges.transform.binary.ra std/algorithms/alg.modifying.operations/alg.transform/ranges.transform.binary.range.pass.cpp:1 SKIPPED std/algorithms/alg.sorting/alg.merge/ranges_merge.pass.cpp:0 SKIPPED std/algorithms/alg.sorting/alg.merge/ranges_merge.pass.cpp:1 SKIPPED +std/algorithms/alg.sorting/alg.set.operations/includes/ranges_includes.pass.cpp:0 SKIPPED +std/algorithms/alg.sorting/alg.set.operations/includes/ranges_includes.pass.cpp:1 SKIPPED std/algorithms/alg.sorting/alg.set.operations/set.difference/ranges_set_difference.pass.cpp:0 SKIPPED std/algorithms/alg.sorting/alg.set.operations/set.difference/ranges_set_difference.pass.cpp:1 SKIPPED std/algorithms/alg.sorting/alg.set.operations/set.intersection/ranges_set_intersection.pass.cpp:0 SKIPPED @@ -1390,6 +1383,10 @@ std/utilities/meta/meta.unary/dependent_return_type.compile.pass.cpp:9 SKIPPED # This test is marked as `XFAIL: msvc`, but the MSVC-internal test harness doesn't yet parse XFAIL. std/utilities/function.objects/func.wrap/func.wrap.func/noncopyable_return_type.pass.cpp:9 SKIPPED +# Similarly, this test is marked as `XFAIL: msvc` +# MSVC doesn't properly support [[no_unique_address]] +std/algorithms/algorithms.results/no_unique_address.compile.pass.cpp:9 SKIPPED + # Similarly, this test is marked as `XFAIL: msvc, target={{.+}}-windows-gnu` # because it calls an `fmemopen` function that doesn't exist on Windows. std/input.output/iostream.format/print.fun/no_file_description.pass.cpp:9 SKIPPED diff --git a/tests/std/include/test_format_support.hpp b/tests/std/include/test_format_support.hpp index c8b2a218f45..60ee59410c3 100644 --- a/tests/std/include/test_format_support.hpp +++ b/tests/std/include/test_format_support.hpp @@ -161,9 +161,9 @@ struct VFormatFn { template [[nodiscard]] auto operator()(const std::basic_string_view str, Args&&... args) const { if constexpr (std::same_as) { - return std::vformat(str, std::make_format_args(std::forward(args)...)); + return std::vformat(str, std::make_format_args(args...)); } else { - return std::vformat(str, std::make_wformat_args(std::forward(args)...)); + return std::vformat(str, std::make_wformat_args(args...)); } } }; diff --git a/tests/std/test.lst b/tests/std/test.lst index 9320b475151..319d3d6669f 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -234,6 +234,8 @@ tests\GH_003840_tellg_when_reading_lf_file_in_text_mode tests\GH_003867_output_nan tests\GH_004023_mdspan_fwd_prod_overflow tests\GH_004040_container_nonmember_functions +tests\GH_004201_chrono_formatter +tests\GH_004275_seeking_fancy_iterators tests\LWG2381_num_get_floating_point tests\LWG2597_complex_branch_cut tests\LWG3018_shared_ptr_function @@ -642,6 +644,7 @@ tests\P2474R2_views_repeat tests\P2474R2_views_repeat_death tests\P2494R2_move_only_range_adaptors tests\P2505R5_monadic_functions_for_std_expected +tests\P2510R3_text_formatting_pointers tests\P2517R1_apply_conditional_noexcept tests\P2538R1_adl_proof_std_projected tests\P2609R3_relaxing_ranges_just_a_smidge diff --git a/tests/std/tests/GH_002206_unreserved_names/test.compile.pass.cpp b/tests/std/tests/GH_002206_unreserved_names/test.compile.pass.cpp index c57279e236c..8ba419dbd06 100644 --- a/tests/std/tests/GH_002206_unreserved_names/test.compile.pass.cpp +++ b/tests/std/tests/GH_002206_unreserved_names/test.compile.pass.cpp @@ -7,6 +7,27 @@ #define xtime delete #define xtime_get delete +// Test workaround for extensions of non-reserved names that can't be removed at this moment. +#define raw_name 1001 + +#define ipfx 1002 +#define isfx 1003 + +#define opfx 1004 +#define osfx 1005 + +#define checked_array_iterator 1006 +#define make_checked_array_iterator 1007 +#define make_unchecked_array_iterator 1008 +#define unchecked_array_iterator 1009 + +#define discard_block 1010 +#define linear_congruential 1011 +#define mersenne_twister 1012 +#define subtract_with_carry 1013 +#define uniform_int 1014 +#define uniform_real 1015 + // Also test GH-2645: : Conformance issue on [[msvc::known_semantics]] #define msvc 1 #define known_semantics 2 @@ -40,3 +61,63 @@ #if empty_bases != 6 #error bad macro expansion #endif // empty_bases != 6 + +#if raw_name != 1001 +#error bad macro expansion +#endif // raw_name != 1001 + +#if ipfx != 1002 +#error bad macro expansion +#endif // ipfx != 1002 + +#if isfx != 1003 +#error bad macro expansion +#endif // isfx != 1003 + +#if opfx != 1004 +#error bad macro expansion +#endif // opfx != 1004 + +#if osfx != 1005 +#error bad macro expansion +#endif // osfx != 1005 + +#if checked_array_iterator != 1006 +#error bad macro expansion +#endif // checked_array_iterator != 1006 + +#if make_checked_array_iterator != 1007 +#error bad macro expansion +#endif // make_checked_array_iterator != 1007 + +#if make_unchecked_array_iterator != 1008 +#error bad macro expansion +#endif // make_unchecked_array_iterator != 1008 + +#if unchecked_array_iterator != 1009 +#error bad macro expansion +#endif // unchecked_array_iterator != 1009 + +#if discard_block != 1010 +#error bad macro expansion +#endif // discard_block != 1010 + +#if linear_congruential != 1011 +#error bad macro expansion +#endif // linear_congruential != 1011 + +#if mersenne_twister != 1012 +#error bad macro expansion +#endif // mersenne_twister != 1012 + +#if subtract_with_carry != 1013 +#error bad macro expansion +#endif // subtract_with_carry != 1013 + +#if uniform_int != 1014 +#error bad macro expansion +#endif // uniform_int != 1014 + +#if uniform_real != 1015 +#error bad macro expansion +#endif // uniform_real != 1015 diff --git a/tests/std/tests/GH_004201_chrono_formatter/env.lst b/tests/std/tests/GH_004201_chrono_formatter/env.lst new file mode 100644 index 00000000000..d6d824b5879 --- /dev/null +++ b/tests/std/tests/GH_004201_chrono_formatter/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_20_matrix.lst diff --git a/tests/std/tests/GH_004201_chrono_formatter/test.cpp b/tests/std/tests/GH_004201_chrono_formatter/test.cpp new file mode 100644 index 00000000000..2b3e1096214 --- /dev/null +++ b/tests/std/tests/GH_004201_chrono_formatter/test.cpp @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +using namespace std::literals::chrono_literals; + +int main() { + assert(std::format("[{:20%T}]", 314159s) == "[87:15:59 ]"); + + // std::formatter specializations for types used to ignore dynamically provided width + assert(std::format("[{:{}%T}]", 314159s, 20) == "[87:15:59 ]"); +} diff --git a/tests/std/tests/GH_004275_seeking_fancy_iterators/env.lst b/tests/std/tests/GH_004275_seeking_fancy_iterators/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_004275_seeking_fancy_iterators/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_004275_seeking_fancy_iterators/test.cpp b/tests/std/tests/GH_004275_seeking_fancy_iterators/test.cpp new file mode 100644 index 00000000000..5dbd840c2c5 --- /dev/null +++ b/tests/std/tests/GH_004275_seeking_fancy_iterators/test.cpp @@ -0,0 +1,455 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// some portions of this file are derived from libc++'s test files: +// * support/min_allocator.h + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +template > +class min_pointer; +template +class min_pointer; +template +class min_pointer; +template +class min_pointer; +template +class min_allocator; + +template +class min_pointer { + const void* ptr_; + +public: + min_pointer() = default; + min_pointer(nullptr_t) noexcept : ptr_(nullptr) {} + template + min_pointer(min_pointer p) noexcept : ptr_(p.ptr_) {} + + explicit operator bool() const { + return ptr_ != nullptr; + } + + friend bool operator==(min_pointer x, min_pointer y) { + return x.ptr_ == y.ptr_; + } + friend bool operator!=(min_pointer x, min_pointer y) { + return !(x == y); + } + template + friend class min_pointer; +}; + +template +class min_pointer { + void* ptr_; + +public: + min_pointer() = default; + constexpr min_pointer(nullptr_t) noexcept : ptr_(nullptr) {} + template >> + constexpr min_pointer(min_pointer p) noexcept : ptr_(p.ptr_) {} + + constexpr explicit operator bool() const { + return ptr_ != nullptr; + } + + friend constexpr bool operator==(min_pointer x, min_pointer y) { + return x.ptr_ == y.ptr_; + } + friend constexpr bool operator!=(min_pointer x, min_pointer y) { + return !(x == y); + } + template + friend class min_pointer; +}; + +template +class min_pointer { + T* ptr_; + + constexpr explicit min_pointer(T* p) noexcept : ptr_(p) {} + +public: + min_pointer() = default; + constexpr min_pointer(nullptr_t) noexcept : ptr_(nullptr) {} + constexpr explicit min_pointer(min_pointer p) noexcept : ptr_(static_cast(p.ptr_)) {} + + constexpr explicit operator bool() const { + return ptr_ != nullptr; + } + + using difference_type = ptrdiff_t; + using reference = T&; + using pointer = T*; + using value_type = T; + using iterator_category = random_access_iterator_tag; + + constexpr reference operator*() const { + if (ptr_ == nullptr) { + abort(); + } + return *ptr_; + } + constexpr pointer operator->() const { + return ptr_; + } + + constexpr min_pointer& operator++() { + ++ptr_; + return *this; + } + constexpr min_pointer operator++(int) { + min_pointer tmp(*this); + ++ptr_; + return tmp; + } + + constexpr min_pointer& operator--() { + --ptr_; + return *this; + } + constexpr min_pointer operator--(int) { + min_pointer tmp(*this); + --ptr_; + return tmp; + } + + constexpr min_pointer& operator+=(difference_type n) { + ptr_ += n; + return *this; + } + constexpr min_pointer& operator-=(difference_type n) { + ptr_ -= n; + return *this; + } + + constexpr min_pointer operator+(difference_type n) const { + min_pointer tmp(*this); + tmp += n; + return tmp; + } + + friend constexpr min_pointer operator+(difference_type n, min_pointer x) { + return x + n; + } + + constexpr min_pointer operator-(difference_type n) const { + min_pointer tmp(*this); + tmp -= n; + return tmp; + } + + friend constexpr difference_type operator-(min_pointer x, min_pointer y) { + return x.ptr_ - y.ptr_; + } + + constexpr reference operator[](difference_type n) const { + return ptr_[n]; + } + + friend constexpr bool operator<(min_pointer x, min_pointer y) { + return x.ptr_ < y.ptr_; + } + friend constexpr bool operator>(min_pointer x, min_pointer y) { + return y < x; + } + friend constexpr bool operator<=(min_pointer x, min_pointer y) { + return !(y < x); + } + friend constexpr bool operator>=(min_pointer x, min_pointer y) { + return !(x < y); + } + + static constexpr min_pointer pointer_to(T& t) { + return min_pointer(addressof(t)); + } + + friend constexpr bool operator==(min_pointer x, min_pointer y) { + return x.ptr_ == y.ptr_; + } + friend constexpr bool operator!=(min_pointer x, min_pointer y) { + return !(x == y); + } + template + friend class min_pointer; + template + friend class min_allocator; +}; + +template +class min_pointer { + const T* ptr_; + + constexpr explicit min_pointer(const T* p) : ptr_(p) {} + +public: + min_pointer() = default; + constexpr min_pointer(nullptr_t) : ptr_(nullptr) {} + constexpr min_pointer(min_pointer p) : ptr_(p.ptr_) {} + constexpr explicit min_pointer(min_pointer p) : ptr_(static_cast(p.ptr_)) {} + + constexpr explicit operator bool() const { + return ptr_ != nullptr; + } + + using difference_type = ptrdiff_t; + using reference = const T&; + using pointer = const T*; + using value_type = T; + using iterator_category = random_access_iterator_tag; + + constexpr reference operator*() const { + if (ptr_ == nullptr) { + abort(); + } + return *ptr_; + } + constexpr pointer operator->() const { + return ptr_; + } + + constexpr min_pointer& operator++() { + ++ptr_; + return *this; + } + constexpr min_pointer operator++(int) { + min_pointer tmp(*this); + ++ptr_; + return tmp; + } + + constexpr min_pointer& operator--() { + --ptr_; + return *this; + } + constexpr min_pointer operator--(int) { + min_pointer tmp(*this); + --ptr_; + return tmp; + } + + constexpr min_pointer& operator+=(difference_type n) { + ptr_ += n; + return *this; + } + constexpr min_pointer& operator-=(difference_type n) { + ptr_ -= n; + return *this; + } + + constexpr min_pointer operator+(difference_type n) const { + min_pointer tmp(*this); + tmp += n; + return tmp; + } + + friend constexpr min_pointer operator+(difference_type n, min_pointer x) { + return x + n; + } + + constexpr min_pointer operator-(difference_type n) const { + min_pointer tmp(*this); + tmp -= n; + return tmp; + } + + friend constexpr difference_type operator-(min_pointer x, min_pointer y) { + return x.ptr_ - y.ptr_; + } + + constexpr reference operator[](difference_type n) const { + return ptr_[n]; + } + + friend constexpr bool operator<(min_pointer x, min_pointer y) { + return x.ptr_ < y.ptr_; + } + friend constexpr bool operator>(min_pointer x, min_pointer y) { + return y < x; + } + friend constexpr bool operator<=(min_pointer x, min_pointer y) { + return !(y < x); + } + friend constexpr bool operator>=(min_pointer x, min_pointer y) { + return !(x < y); + } + + static constexpr min_pointer pointer_to(const T& t) { + return min_pointer(addressof(t)); + } + + friend constexpr bool operator==(min_pointer x, min_pointer y) { + return x.ptr_ == y.ptr_; + } + friend constexpr bool operator!=(min_pointer x, min_pointer y) { + return x.ptr_ != y.ptr_; + } + friend constexpr bool operator==(min_pointer x, nullptr_t) { + return x.ptr_ == nullptr; + } + friend constexpr bool operator!=(min_pointer x, nullptr_t) { + return x.ptr_ != nullptr; + } + friend constexpr bool operator==(nullptr_t, min_pointer x) { + return x.ptr_ == nullptr; + } + friend constexpr bool operator!=(nullptr_t, min_pointer x) { + return x.ptr_ != nullptr; + } + template + friend class min_pointer; +}; + +#if _HAS_CXX20 +#define CONSTEXPR20 constexpr +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +#define CONSTEXPR20 inline +#endif // ^^^ !_HAS_CXX20 ^^^ + +template +class min_allocator { +public: + using value_type = T; + using pointer = min_pointer; + + min_allocator() = default; + template + constexpr min_allocator(min_allocator) noexcept {} + + CONSTEXPR20 pointer allocate(ptrdiff_t n) { + return pointer(allocator().allocate(n)); + } + + CONSTEXPR20 void deallocate(pointer p, ptrdiff_t n) { + allocator().deallocate(p.ptr_, n); + } + + friend constexpr bool operator==(min_allocator, min_allocator) { + return true; + } + friend constexpr bool operator!=(min_allocator x, min_allocator y) { + return !(x == y); + } +}; + +CONSTEXPR20 bool test_seeking_vector_iterators() { + using VIt = vector>::iterator; + assert(find(VIt{}, VIt{}, 0) == VIt{}); + + using VCIt = vector>::const_iterator; + assert(find(VCIt{}, VCIt{}, 0) == VCIt{}); + + return true; +} + +CONSTEXPR20 bool test_seeking_string_iterators() { + using SIt = basic_string, min_allocator>::iterator; + assert(find(SIt{}, SIt{}, '\0') == SIt{}); + + using SCIt = basic_string, min_allocator>::const_iterator; + assert(find(SCIt{}, SCIt{}, '\0') == SCIt{}); + + return true; +} + +CONSTEXPR20 bool test_vector_iterators_ordering() { + using VIt = vector>::iterator; + using VCIt = vector>::const_iterator; + + assert(!(VIt{} < VIt{})); + assert(!(VIt{} > VIt{})); + assert(VIt{} <= VIt{}); + assert(VIt{} >= VIt{}); + + assert(!(VCIt{} < VCIt{})); + assert(!(VCIt{} > VCIt{})); + assert(VCIt{} <= VCIt{}); + assert(VCIt{} >= VCIt{}); + + assert(!(VIt{} < VCIt{})); + assert(!(VIt{} > VCIt{})); + assert(VIt{} <= VCIt{}); + assert(VIt{} >= VCIt{}); + + assert(!(VCIt{} < VIt{})); + assert(!(VCIt{} > VIt{})); + assert(VCIt{} <= VIt{}); + assert(VCIt{} >= VIt{}); + +#if _HAS_CXX20 + assert(VIt{} <=> VIt{} == strong_ordering::equal); + assert(VCIt{} <=> VCIt{} == strong_ordering::equal); + assert(VIt{} <=> VCIt{} == strong_ordering::equal); + assert(VCIt{} <=> VIt{} == strong_ordering::equal); +#endif // _HAS_CXX20 + + return true; +} + +CONSTEXPR20 bool test_string_iterators_ordering() { + using SIt = basic_string, min_allocator>::iterator; + using SCIt = basic_string, min_allocator>::const_iterator; + + assert(!(SIt{} < SIt{})); + assert(!(SIt{} > SIt{})); + assert(SIt{} <= SIt{}); + assert(SIt{} >= SIt{}); + + assert(!(SCIt{} < SCIt{})); + assert(!(SCIt{} > SCIt{})); + assert(SCIt{} <= SCIt{}); + assert(SCIt{} >= SCIt{}); + + assert(!(SIt{} < SCIt{})); + assert(!(SIt{} > SCIt{})); + assert(SIt{} <= SCIt{}); + assert(SIt{} >= SCIt{}); + + assert(!(SCIt{} < SIt{})); + assert(!(SCIt{} > SIt{})); + assert(SCIt{} <= SIt{}); + assert(SCIt{} >= SIt{}); + +#if _HAS_CXX20 + assert(SIt{} <=> SIt{} == strong_ordering::equal); + assert(SCIt{} <=> SCIt{} == strong_ordering::equal); + assert(SIt{} <=> SCIt{} == strong_ordering::equal); + assert(SCIt{} <=> SIt{} == strong_ordering::equal); +#endif // _HAS_CXX20 + + return true; +} + +#if _HAS_CXX20 +static_assert(test_seeking_vector_iterators()); +static_assert(test_seeking_string_iterators()); +static_assert(test_vector_iterators_ordering()); +static_assert(test_string_iterators_ordering()); +#endif // _HAS_CXX20 + +int main() { + test_seeking_vector_iterators(); + test_seeking_string_iterators(); + test_vector_iterators_ordering(); + test_string_iterators_ordering(); +} diff --git a/tests/std/tests/P0024R2_parallel_algorithms_transform_reduce/test.cpp b/tests/std/tests/P0024R2_parallel_algorithms_transform_reduce/test.cpp index 91418380195..c1a8a7dccfd 100644 --- a/tests/std/tests/P0024R2_parallel_algorithms_transform_reduce/test.cpp +++ b/tests/std/tests/P0024R2_parallel_algorithms_transform_reduce/test.cpp @@ -1,7 +1,9 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#pragma warning(disable : 4242 4244 4365) // test_case_incorrect_special_case_reasoning tests narrowing on purpose +// test_case_incorrect_special_case_reasoning and test_case_narrowing_conversion test narrowing on purpose +#pragma warning(disable : 4242 4244 4267 4365) + #include #include #include @@ -144,6 +146,25 @@ void test_case_incorrect_special_case_reasoning() { assert(transform_reduce(begin(c), end(c), d, 0, multiplies<>{}, plus{}) == 0); } +void test_case_narrowing_conversion() { + size_t a[] = {1, 2, 3}; + auto return_itself = [](size_t x) { return x; }; + // Initializing a smaller type (int here) with a larger type (size_t here). + // According to N4971 [transform.reduce]/7, + // this narrowing conversion should compile without error. + assert(transform_reduce(begin(a), end(a), 0, plus{}, return_itself) == 6); + assert(transform_reduce(seq, begin(a), end(a), 0, plus{}, return_itself) == 6); + assert(transform_reduce(par, begin(a), end(a), 0, plus{}, return_itself) == 6); + + size_t b[] = {1, 2, 3}; + // Initializing a smaller type (int here) with a larger type (size_t here). + // According to N4971 [transform.reduce]/3, + // this narrowing conversion should compile without error. + assert(transform_reduce(begin(a), end(a), b, 0, plus{}, plus{}) == 12); + assert(transform_reduce(seq, begin(a), end(a), b, 0, plus{}, plus{}) == 12); + assert(transform_reduce(par, begin(a), end(a), b, 0, plus{}, plus{}) == 12); +} + int main() { mt19937 gen(1729); parallel_test_case(test_case_transform_reduce_binary, gen); @@ -153,4 +174,5 @@ int main() { parallel_test_case([](const size_t testSize) { test_case_move_only(seq, testSize); }); parallel_test_case([](const size_t testSize) { test_case_move_only(par, testSize); }); test_case_incorrect_special_case_reasoning(); + test_case_narrowing_conversion(); } diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp index 2901002420e..6b441e01abb 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_formatting/test.cpp @@ -213,11 +213,11 @@ bool test_parse_chrono_format_specs() { } template -auto make_testing_format_args(Args&&... vals) { +auto make_testing_format_args(Args&&... vals) { // references to temporaries are risky, see P2905R2; we'll be careful if constexpr (is_same_v) { - return make_wformat_args(forward(vals)...); + return make_wformat_args(vals...); } else { - return make_format_args(forward(vals)...); + return make_format_args(vals...); } } diff --git a/tests/std/tests/P0645R10_text_formatting_args/test.cpp b/tests/std/tests/P0645R10_text_formatting_args/test.cpp index 2e06a21ea91..1af863a77a2 100644 --- a/tests/std/tests/P0645R10_text_formatting_args/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_args/test.cpp @@ -248,7 +248,8 @@ void test_visit_monostate() { template void test_lwg3810() { - [[maybe_unused]] auto args_store = make_format_args(1, 2, 3); + int args[]{1, 2, 3}; + [[maybe_unused]] auto args_store = make_format_args(args[0], args[1], args[2]); static_assert(same_as>); } @@ -264,6 +265,54 @@ void test_lvalue_only_visitation() { visit_format_arg(lvalue_only_visitor{}, basic_format_arg{}); } +namespace detail { + constexpr bool permissive() { + return false; + } + + template + struct DependentBase { + static constexpr bool permissive() { + return true; + } + }; + + template + struct Derived : DependentBase { + static constexpr bool test() { + return permissive(); + } + }; +} // namespace detail +constexpr bool is_permissive = detail::Derived::test(); + +template +concept CanMakeFormatArgs = requires(Args&&... args) { make_format_args(static_cast(args)...); }; + +// P2905R2 Runtime format strings (make make_(w)format_args only take lvalue references) +template +void test_lvalue_reference_parameters() { + using char_type = Context::char_type; + + static_assert(CanMakeFormatArgs&, basic_string_view&>); + static_assert( + CanMakeFormatArgs&, const basic_string_view&>); + + static_assert(CanMakeFormatArgs, const basic_string_view>); + + static_assert(!CanMakeFormatArgs); + static_assert(!CanMakeFormatArgs); + static_assert(!CanMakeFormatArgs); + static_assert(!CanMakeFormatArgs); + static_assert(!CanMakeFormatArgs); + static_assert(!CanMakeFormatArgs); + static_assert(CanMakeFormatArgs> == is_permissive); + static_assert(CanMakeFormatArgs> == is_permissive); +} + int main() { test_basic_format_arg(); test_basic_format_arg(); @@ -277,4 +326,7 @@ int main() { test_lvalue_only_visitation(); test_lvalue_only_visitation(); + + test_lvalue_reference_parameters(); + test_lvalue_reference_parameters(); } diff --git a/tests/std/tests/P0645R10_text_formatting_custom_formatting/test.cpp b/tests/std/tests/P0645R10_text_formatting_custom_formatting/test.cpp index 43f2e48214f..5966daf3593 100644 --- a/tests/std/tests/P0645R10_text_formatting_custom_formatting/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_custom_formatting/test.cpp @@ -15,11 +15,11 @@ using namespace std; // copied from the text_formatting_formatting test case template -auto make_testing_format_args(Args&&... vals) { +auto make_testing_format_args(Args&&... vals) { // references to temporaries are risky, see P2905R2; we'll be careful if constexpr (is_same_v) { - return make_wformat_args(forward(vals)...); + return make_wformat_args(vals...); } else { - return make_format_args(forward(vals)...); + return make_format_args(vals...); } } diff --git a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp index 4fa15eefa34..7dea640f249 100644 --- a/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_formatting/test.cpp @@ -63,11 +63,11 @@ template > using alternative_basic_string = basic_string, Alloc>; template -auto make_testing_format_args(Args&&... vals) { +auto make_testing_format_args(Args&&... vals) { // references to temporaries are risky, see P2905R2; we'll be careful if constexpr (is_same_v) { - return make_wformat_args(forward(vals)...); + return make_wformat_args(vals...); } else { - return make_format_args(forward(vals)...); + return make_format_args(vals...); } } @@ -991,7 +991,7 @@ void test_pointer_specs() { throw_helper(STR("{:#}"), nullptr); // Leading zero - throw_helper(STR("{:0}"), nullptr); + assert(format(STR("{:05}"), nullptr) == STR("0x000")); // Width assert(format(STR("{:5}"), nullptr) == STR(" 0x0")); @@ -1350,10 +1350,10 @@ void libfmt_formatter_test_zero_flag() { assert(format(STR("{0:05}"), 42ull) == STR("00042")); assert(format(STR("{0:07}"), -42.0) == STR("-000042")); assert(format(STR("{0:07}"), -42.0l) == STR("-000042")); + assert(format(STR("{0:05}"), reinterpret_cast(0x42)) == STR("0x042")); throw_helper(STR("{0:0"), 'c'); throw_helper(STR("{0:05}"), 'c'); throw_helper(STR("{0:05}"), STR("abc")); - throw_helper(STR("{0:05}"), reinterpret_cast(0x42)); } template diff --git a/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp b/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp index db01cedbefa..c11d20e7519 100644 --- a/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp +++ b/tests/std/tests/P0645R10_text_formatting_utf8/test.cpp @@ -23,7 +23,8 @@ void test_multibyte_format_strings() { { try { // Bad fill character encoding: missing lead byte before \x9f - (void) vformat("{:\x9f\x8f\x88<10}"sv, make_format_args(42)); + int arg = 42; + (void) vformat("{:\x9f\x8f\x88<10}"sv, make_format_args(arg)); assert(false); } catch (const format_error&) { } diff --git a/tests/std/tests/P0896R4_views_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp index 25c2647b76d..422d1fe4512 100644 --- a/tests/std/tests/P0896R4_views_join/test.cpp +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -177,6 +177,16 @@ constexpr bool test_one(Outer&& rng, Expected&& expected) { } } + // Also validate that join_view iterators are default-constructible + { + STATIC_ASSERT(is_default_constructible_v>); + [[maybe_unused]] iterator_t i; + if constexpr (CanMemberBegin) { + STATIC_ASSERT(is_default_constructible_v>); + [[maybe_unused]] iterator_t ci; + } + } + // Validate join_view::end static_assert(CanMemberEnd); static_assert(CanMemberEnd diff --git a/tests/std/tests/P2286R8_text_formatting_escaping/test.cpp b/tests/std/tests/P2286R8_text_formatting_escaping/test.cpp index c735ff84dac..19ffb640920 100644 --- a/tests/std/tests/P2286R8_text_formatting_escaping/test.cpp +++ b/tests/std/tests/P2286R8_text_formatting_escaping/test.cpp @@ -22,11 +22,11 @@ template #define STR(Literal) (choose_literal(Literal, L##Literal)) template -auto make_testing_format_args(Args&&... vals) { +auto make_testing_format_args(Args&&... vals) { // references to temporaries are risky, see P2905R2; we'll be careful if constexpr (is_same_v) { - return make_wformat_args(forward(vals)...); + return make_wformat_args(vals...); } else { - return make_format_args(forward(vals)...); + return make_format_args(vals...); } } diff --git a/tests/std/tests/P2441R2_views_join_with/test.cpp b/tests/std/tests/P2441R2_views_join_with/test.cpp index f2deb380045..994f6d9b554 100644 --- a/tests/std/tests/P2441R2_views_join_with/test.cpp +++ b/tests/std/tests/P2441R2_views_join_with/test.cpp @@ -219,6 +219,16 @@ constexpr void test_one(Outer&& rng, Delimiter&& delimiter, Expected&& expected) } } + // Also validate that join_with_view iterators are default-constructible + { + STATIC_ASSERT(is_default_constructible_v>); + [[maybe_unused]] iterator_t i; + if constexpr (CanMemberBegin) { + STATIC_ASSERT(is_default_constructible_v>); + [[maybe_unused]] iterator_t ci; + } + } + // Validate join_with_view::end static_assert(CanMemberEnd); static_assert(CanMemberEnd diff --git a/tests/std/tests/P2510R3_text_formatting_pointers/env.lst b/tests/std/tests/P2510R3_text_formatting_pointers/env.lst new file mode 100644 index 00000000000..d6d824b5879 --- /dev/null +++ b/tests/std/tests/P2510R3_text_formatting_pointers/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_20_matrix.lst diff --git a/tests/std/tests/P2510R3_text_formatting_pointers/test.cpp b/tests/std/tests/P2510R3_text_formatting_pointers/test.cpp new file mode 100644 index 00000000000..e7d37a270c6 --- /dev/null +++ b/tests/std/tests/P2510R3_text_formatting_pointers/test.cpp @@ -0,0 +1,301 @@ +// 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 +#include + +#include + +#define STR(Str) TYPED_LITERAL(CharT, Str) + +using namespace std; + +template +basic_string pointer_to_string(const void* ptr) { + constexpr size_t hexits = 2 * sizeof(void*); + char buffer[hexits]; + auto [end, ec] = to_chars(buffer, buffer + hexits, reinterpret_cast(ptr), 16); + assert(ec == errc{}); + basic_string out; + ranges::transform(buffer, end, back_inserter(out), [](char c) { return static_cast(c); }); + return out; +} + +template +basic_string string_to_uppercase(const basic_string& str) { + basic_string out; + ranges::transform(str, back_inserter(out), [](auto c) { + if constexpr (same_as) { + return static_cast(toupper(static_cast(c))); + } else if constexpr (same_as) { + return static_cast(towupper(static_cast(c))); + } + }); + return out; +} + +template + requires indirect_binary_predicate, const T*> +constexpr bool all_equal_to(R&& r, const T& val) { + return ranges::all_of(r, [&val](const auto& elem) { return val == elem; }); +} + +static_assert(all_equal_to(array{1, 1, 1, 1}, 1)); +static_assert(!all_equal_to(array{1, 1, 1, 1}, 2)); +static_assert(!all_equal_to(array{1, 1, 1, 2}, 1)); + +template class Fmt> +void check_pointer_formatter() { + Fmt fmt; + const int variable = 0; + const void* const ptr = &variable; + const auto rep = pointer_to_string(ptr); + const auto lowercase_rep = STR("0x") + rep; + const auto uppercase_rep = STR("0X") + string_to_uppercase(rep); + + { // type == '\0' + assert(fmt(STR("{}"), ptr) == lowercase_rep); + assert(fmt(STR("{:}"), ptr) == lowercase_rep); + assert(fmt(STR("{0}"), ptr) == lowercase_rep); + assert(fmt(STR("{0:}"), ptr) == lowercase_rep); + } + + { // align only + assert(fmt(STR("{:<}"), ptr) == lowercase_rep); + assert(fmt(STR("{:^}"), ptr) == lowercase_rep); + assert(fmt(STR("{:>}"), ptr) == lowercase_rep); + } + + { // fill-and-align only + assert(fmt(STR("{:_<}"), ptr) == lowercase_rep); + assert(fmt(STR("{:-^}"), ptr) == lowercase_rep); + assert(fmt(STR("{:=>}"), ptr) == lowercase_rep); + } + + { // type == 'p' || type == 'P' + assert(fmt(STR("{:p}"), ptr) == lowercase_rep); + assert(fmt(STR("{:P}"), ptr) == uppercase_rep); + } + + { // leading zero and type + assert(fmt(STR("{:0}"), ptr) == lowercase_rep); + assert(fmt(STR("{:0p}"), ptr) == lowercase_rep); + assert(fmt(STR("{:0P}"), ptr) == uppercase_rep); + } + + { // width only + const int pad = static_cast(25 - lowercase_rep.size()); + const auto str = fmt(STR("{:25}"), ptr); + assert(all_equal_to(views::take(str, pad), STR(' '))); + assert(ranges::equal(views::drop(str, pad), lowercase_rep)); + } + + { // width && align + const auto str = fmt(STR("{:<31}"), ptr); + assert(ranges::equal(views::take(str, static_cast(lowercase_rep.size())), lowercase_rep)); + assert(all_equal_to(views::drop(str, static_cast(lowercase_rep.size())), ' ')); + } + + { // width && fill-and-align (2) + const int w = 22; + const int pad = static_cast(w - lowercase_rep.size()); + const auto str = fmt(STR("{:*^{}}"), ptr, w); + basic_string_view v{str}; + + const int left_pad = pad / 2; + assert(all_equal_to(views::take(v, left_pad), STR('*'))); + v.remove_prefix(static_cast(left_pad)); + assert(v.starts_with(lowercase_rep)); + v.remove_prefix(lowercase_rep.size()); + assert(all_equal_to(v, STR('*'))); + } + + { // leading zero && width && type == 'p' + const int w = 35; + const auto str = fmt(STR("{:0{}p}"), ptr, w); + basic_string_view v{str}; + assert(v.starts_with(STR("0x"))); + v.remove_prefix(2); + const int zero_count = w - static_cast(lowercase_rep.size()); + assert(all_equal_to(views::take(v, zero_count), STR('0'))); + v.remove_prefix(static_cast(zero_count)); + assert(ranges::equal(v, views::drop(lowercase_rep, 2))); + } + + { // leading zero && width && type == 'P' + const int w = 39; + const auto str = fmt(STR("{1:0{0}P}"), w, ptr); + basic_string_view v{str}; + assert(v.starts_with(STR("0X"))); + v.remove_prefix(2); + const int zero_count = w - static_cast(uppercase_rep.size()); + assert(all_equal_to(views::take(v, zero_count), STR('0'))); + v.remove_prefix(static_cast(zero_count)); + assert(ranges::equal(v, views::drop(uppercase_rep, 2))); + } + + { // align && leading zero (should have no effect) && width && type == 'p' + const int w = 42; + const int pad = static_cast(w - lowercase_rep.size()); + const auto str = fmt(STR("{:^0{}p}"), ptr, w); + const int left_pad = pad / 2; + basic_string_view v{str}; + assert(all_equal_to(views::take(v, left_pad), STR(' '))); + v.remove_prefix(static_cast(left_pad)); + assert(v.starts_with(lowercase_rep)); + v.remove_prefix(lowercase_rep.size()); + assert(all_equal_to(v, STR(' '))); + } + + { // fill-and-align && leading zero (should have no effect) && width && type == 'P' + const int w = 44; + const int pad = static_cast(w - uppercase_rep.size()); + const auto str = fmt(STR("{1:#>0{0}P}"), w, ptr); + basic_string_view v{str}; + assert(all_equal_to(views::take(v, pad), STR('#'))); + v.remove_prefix(static_cast(pad)); + assert(ranges::equal(v, uppercase_rep)); + } +} + +template class Fmt> +void check_nullptr_t_formatter() { + Fmt fmt; + + { // type == '\0' + assert(fmt(STR("{}"), nullptr) == STR("0x0")); + assert(fmt(STR("{:}"), nullptr) == STR("0x0")); + assert(fmt(STR("{0}"), nullptr) == STR("0x0")); + assert(fmt(STR("{0:}"), nullptr) == STR("0x0")); + } + + { // align only + assert(fmt(STR("{:<}"), nullptr) == STR("0x0")); + assert(fmt(STR("{:^}"), nullptr) == STR("0x0")); + assert(fmt(STR("{:>}"), nullptr) == STR("0x0")); + } + + { // fill-and-align only + assert(fmt(STR("{:_<}"), nullptr) == STR("0x0")); + assert(fmt(STR("{:-^}"), nullptr) == STR("0x0")); + assert(fmt(STR("{:=>}"), nullptr) == STR("0x0")); + } + + { // type == 'p' || type == 'P' + assert(fmt(STR("{:p}"), nullptr) == STR("0x0")); + assert(fmt(STR("{:P}"), nullptr) == STR("0X0")); + } + + { // leading zero and type + assert(fmt(STR("{:0}"), nullptr) == STR("0x0")); + assert(fmt(STR("{:0p}"), nullptr) == STR("0x0")); + assert(fmt(STR("{:0P}"), nullptr) == STR("0X0")); + } + + { // width only + assert(fmt(STR("{:5}"), nullptr) == STR(" 0x0")); + assert(fmt(STR("{:7}"), nullptr) == STR(" 0x0")); + assert(fmt(STR("{:11}"), nullptr) == STR(" 0x0")); + } + + { // width && align + assert(fmt(STR("{:<5}"), nullptr) == STR("0x0 ")); + assert(fmt(STR("{:^7}"), nullptr) == STR(" 0x0 ")); + assert(fmt(STR("{:>11}"), nullptr) == STR(" 0x0")); + } + + { // width && fill-and-align + assert(fmt(STR("{:#<5}"), nullptr) == STR("0x0##")); + assert(fmt(STR("{:*^7}"), nullptr) == STR("**0x0**")); + assert(fmt(STR("{:=>11}"), nullptr) == STR("========0x0")); + } + + { // leading zero && width && type == '\0' + assert(fmt(STR("{:05}"), nullptr) == STR("0x000")); + assert(fmt(STR("{:07}"), nullptr) == STR("0x00000")); + assert(fmt(STR("{:011}"), nullptr) == STR("0x000000000")); + } + + { // leading zero && width && type == 'p' + assert(fmt(STR("{:05p}"), nullptr) == STR("0x000")); + assert(fmt(STR("{:07p}"), nullptr) == STR("0x00000")); + assert(fmt(STR("{:011p}"), nullptr) == STR("0x000000000")); + } + + { // leading zero && width && type == 'P' + assert(fmt(STR("{:05P}"), nullptr) == STR("0X000")); + assert(fmt(STR("{:07P}"), nullptr) == STR("0X00000")); + assert(fmt(STR("{:011P}"), nullptr) == STR("0X000000000")); + } + + { // align && leading zero (should have no effect) && width && type in ('\0', 'p', 'P') + assert(fmt(STR("{:<05}"), nullptr) == STR("0x0 ")); + assert(fmt(STR("{:^07p}"), nullptr) == STR(" 0x0 ")); + assert(fmt(STR("{:>011P}"), nullptr) == STR(" 0X0")); + } + + { // fill-and-align && leading zero (should have no effect) && width && type in ('\0', 'p', 'P') + assert(fmt(STR("{:!<05}"), nullptr) == STR("0x0!!")); + assert(fmt(STR("{:@^07p}"), nullptr) == STR("@@0x0@@")); + assert(fmt(STR("{:#>011P}"), nullptr) == STR("########0X0")); + } +} + +template +void check_invalid_format_spec() { + ExpectFormatError fmt; + PtrType ptr = nullptr; + + { // damaged fields + fmt(STR("{"), ptr); + fmt(STR("{}}"), ptr); + fmt(STR("{{}"), ptr); + fmt(STR("{:}}"), ptr); + } + + { // sign || '#' || precision || 'L' + fmt(STR("{:+}"), ptr); + fmt(STR("{:-}"), ptr); + fmt(STR("{: }"), ptr); + fmt(STR("{:#}"), ptr); + fmt(STR("{:.2}"), ptr); + fmt(STR("{:L}"), ptr); + } + + { // mixed invalid specs + fmt(STR("{:+#}"), ptr); + fmt(STR("{:-.3}"), ptr); + fmt(STR("{: #}"), ptr); + fmt(STR("{:#.4}"), ptr); + fmt(STR("{:.3o}"), ptr); + fmt(STR("{:X}"), ptr); + fmt(STR("{:invalid for sure}"), ptr); + } +} + +template +void test() { + check_pointer_formatter(); + check_pointer_formatter(); + check_nullptr_t_formatter(); + check_nullptr_t_formatter(); + check_invalid_format_spec(); + check_invalid_format_spec(); +} + +int main() { + test(); + test(); +} 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 4c6ac476bee..81b7159170e 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 @@ -362,7 +362,7 @@ STATIC_ASSERT(__cpp_lib_flat_set == 202207L); #endif #ifdef __cpp_lib_concepts -STATIC_ASSERT(__cpp_lib_format == 202207L); +STATIC_ASSERT(__cpp_lib_format == 202304L); #elif defined(__cpp_lib_format) #error __cpp_lib_format is defined #endif