diff --git a/.gitattributes b/.gitattributes index dc85e5fa21f..938b5267f7a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -7,6 +7,10 @@ # Ensure GitHub detects our C++ code as C++ code. /stl/inc/** linguist-language=C++ /stl/src/** linguist-language=C++ +*.h linguist-language=C++ + +# Ensure GitHub detects lit.cfg and lit.local.cfg as Python instead of HAProxy. +*.cfg linguist-language=Python # Ensure GitHub detects our Perl legacy test harness code as Perl code instead of Raku. *.pl linguist-language=Perl diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b166a3d5a2..d5b7eba7e0d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ project(msvc_standard_libraries LANGUAGES CXX) find_package(Boost REQUIRED) -set(VCLIBS_MIN_BOOST_VERSION 1.74.0) +set(VCLIBS_MIN_BOOST_VERSION 1.75.0) if("${Boost_VERSION}" VERSION_LESS "${VCLIBS_MIN_BOOST_VERSION}") message(FATAL_ERROR "Detected Boost version is too old (older than ${VCLIBS_MIN_BOOST_VERSION}).") endif() diff --git a/README.md b/README.md index 8cc98b808cf..f81f691f560 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,7 @@ Just try to follow these rules, so we can spend more time fixing bugs and implem The STL uses boost-math headers to provide P0226R1 Mathematical Special Functions. We recommend using [vcpkg][] to acquire this dependency. -1. Install Visual Studio 2019 16.9 Preview 4 or later. +1. Install Visual Studio 2019 16.10 Preview 1 or later. * We recommend selecting "C++ CMake tools for Windows" in the VS Installer. This will ensure that you're using supported versions of CMake and Ninja. * Otherwise, install [CMake][] 3.19 or later, and [Ninja][] 1.10.2 or later. @@ -158,7 +158,7 @@ acquire this dependency. # How To Build With A Native Tools Command Prompt -1. Install Visual Studio 2019 16.9 Preview 4 or later. +1. Install Visual Studio 2019 16.10 Preview 1 or later. * We recommend selecting "C++ CMake tools for Windows" in the VS Installer. This will ensure that you're using supported versions of CMake and Ninja. * Otherwise, install [CMake][] 3.19 or later, and [Ninja][] 1.10.2 or later. @@ -234,7 +234,7 @@ C:\Users\username\Desktop>dumpbin /IMPORTS .\example.exe | findstr msvcp # How To Run The Tests With A Native Tools Command Prompt 1. Follow either [How To Build With A Native Tools Command Prompt][] or [How To Build With The Visual Studio IDE][]. -2. Acquire [Python][] 3.9.1 or newer and have it on the `PATH` (or run it directly using its absolute or relative path). +2. Acquire [Python][] 3.9.2 or newer and have it on the `PATH` (or run it directly using its absolute or relative path). 3. Have LLVM's `bin` directory on the `PATH` (so `clang-cl.exe` is available). * We recommend selecting "C++ Clang tools for Windows" in the VS Installer. This will automatically add LLVM to the `PATH` of the x86 and x64 Native Tools Command Prompts, and will ensure that you're using a supported version. @@ -354,7 +354,7 @@ those features first the tests will begin passing unexpectedly for us and return this it is necessary to add a `PASS` entry to the `expected_results.txt` of the testsuite in question. The `UNSUPPORTED` result code means that the requirements for a test are not met and so it will not be run. Currently -all tests which use the `/BE` or `/clr:pure` options are unsupported. +all tests which use the `/clr` or `/clr:pure` options are unsupported. Also, the `/BE` option is unsupported for x64. The `SKIPPED` result code indicates that a given test was explicitly skipped by adding a `SKIPPED` entry to the `expected_results.txt`. A test may be skipped for a number of reasons, which include, but are not limited to: diff --git a/azure-devops/create-vmss.ps1 b/azure-devops/create-vmss.ps1 index 15126f5ab46..64fa971fdc8 100644 --- a/azure-devops/create-vmss.ps1 +++ b/azure-devops/create-vmss.ps1 @@ -20,9 +20,9 @@ $ErrorActionPreference = 'Stop' # https://aka.ms/azps-changewarnings $Env:SuppressAzurePowerShellBreakingChangeWarnings = 'true' -$Location = 'northeurope' +$Location = 'westus2' $Prefix = 'StlBuild-' + (Get-Date -Format 'yyyy-MM-dd') -$VMSize = 'Standard_D32as_v4' +$VMSize = 'Standard_D32ds_v4' $ProtoVMName = 'PROTOTYPE' $LiveVMPrefix = 'BUILD' $WindowsServerSku = '2019-Datacenter' diff --git a/azure-devops/provision-image.ps1 b/azure-devops/provision-image.ps1 index 8468b850ad6..66988bfd7ac 100644 --- a/azure-devops/provision-image.ps1 +++ b/azure-devops/provision-image.ps1 @@ -91,7 +91,7 @@ if ([string]::IsNullOrEmpty($AdminUserPassword)) { $PsExecPath = Join-Path $ExtractedPsToolsPath 'PsExec64.exe' # https://github.com/PowerShell/PowerShell/releases/latest - $PowerShellZipUrl = 'https://github.com/PowerShell/PowerShell/releases/download/v7.1.1/PowerShell-7.1.1-win-x64.zip' + $PowerShellZipUrl = 'https://github.com/PowerShell/PowerShell/releases/download/v7.1.2/PowerShell-7.1.2-win-x64.zip' Write-Host "Downloading: $PowerShellZipUrl" $ExtractedPowerShellPath = DownloadAndExtractZip -Url $PowerShellZipUrl $PwshPath = Join-Path $ExtractedPowerShellPath 'pwsh.exe' @@ -139,7 +139,7 @@ $Workloads = @( $ReleaseInPath = 'Preview' $Sku = 'Enterprise' $VisualStudioBootstrapperUrl = 'https://aka.ms/vs/16/pre/vs_enterprise.exe' -$PythonUrl = 'https://www.python.org/ftp/python/3.9.1/python-3.9.1-amd64.exe' +$PythonUrl = 'https://www.python.org/ftp/python/3.9.2/python-3.9.2-amd64.exe' # https://docs.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk $WindowsDriverKitUrl = 'https://go.microsoft.com/fwlink/?linkid=2128854' diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 262f731d872..8e8832a925f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -8,7 +8,7 @@ variables: buildOutputLocation: 'D:\build' vcpkgLocation: '$(Build.SourcesDirectory)/vcpkg' -pool: 'StlBuild-2021-02-17' +pool: 'StlBuild-2021-03-02' stages: - stage: Code_Format diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index 298f1439d03..ba137d5fefb 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -177,6 +177,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/semaphore ${CMAKE_CURRENT_LIST_DIR}/inc/set ${CMAKE_CURRENT_LIST_DIR}/inc/shared_mutex + ${CMAKE_CURRENT_LIST_DIR}/inc/source_location ${CMAKE_CURRENT_LIST_DIR}/inc/span ${CMAKE_CURRENT_LIST_DIR}/inc/sstream ${CMAKE_CURRENT_LIST_DIR}/inc/stack diff --git a/stl/inc/__msvc_all_public_headers.hpp b/stl/inc/__msvc_all_public_headers.hpp index 65b45f62770..9889036b41b 100644 --- a/stl/inc/__msvc_all_public_headers.hpp +++ b/stl/inc/__msvc_all_public_headers.hpp @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include diff --git a/stl/inc/algorithm b/stl/inc/algorithm index be003e41cf8..8f13788c196 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -538,7 +538,7 @@ namespace ranges { template _Se, class _Pj = identity, indirect_binary_predicate, projected<_It, _Pj>> _Pr = ranges::equal_to> - _NODISCARD constexpr _It operator()(_It _First, const _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + _NODISCARD constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { _Adl_verify_range(_First, _Last); auto _UResult = _Adjacent_find_unchecked( diff --git a/stl/inc/array b/stl/inc/array index d51bc8f54b9..6f8e8b722cb 100644 --- a/stl/inc/array +++ b/stl/inc/array @@ -111,6 +111,11 @@ public: return _Ptr == _Right._Ptr; } +#if _HAS_CXX20 + _NODISCARD constexpr strong_ordering operator<=>(const _Array_const_iterator& _Right) const noexcept { + return _Ptr <=> _Right._Ptr; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD _CONSTEXPR17 bool operator!=(const _Array_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -130,6 +135,7 @@ public: _NODISCARD _CONSTEXPR17 bool operator>=(const _Array_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 using _Prevent_inheriting_unwrap = _Array_const_iterator; @@ -235,6 +241,12 @@ private: return _Idx == _Right._Idx; } +#if _HAS_CXX20 + _NODISCARD constexpr strong_ordering operator<=>(const _Array_const_iterator& _Right) const noexcept { + _Compat(_Right); + return _Idx <=> _Right._Idx; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD _CONSTEXPR17 bool operator!=(const _Array_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -255,6 +267,7 @@ private: _NODISCARD _CONSTEXPR17 bool operator>=(const _Array_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 _CONSTEXPR17 void _Compat(const _Array_const_iterator& _Right) const noexcept { // test for compatible iterator pair _STL_VERIFY(_Ptr == _Right._Ptr, "array iterators incompatible"); @@ -775,17 +788,41 @@ _CONSTEXPR20 void swap(array<_Ty, _Size>& _Left, array<_Ty, _Size>& _Right) noex template _NODISCARD _CONSTEXPR20 bool operator==(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { +#ifdef __EDG__ // TRANSITION, VSO-1161663 return _STD equal(_Left.begin(), _Left.end(), _Right.begin()); +#else // ^^^ workaround / no workaround vvv + return _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); +#endif // ^^^ no workaround ^^^ } +#if !_HAS_CXX20 template -_NODISCARD _CONSTEXPR20 bool operator!=(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { +_NODISCARD bool operator!=(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD constexpr _Synth_three_way_result<_Ty> operator<=>( + const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { +#ifdef __EDG__ // TRANSITION, VSO-1161663 + return _STD lexicographical_compare_three_way( + _Left.begin(), _Left.end(), _Right.begin(), _Right.end(), _Synth_three_way{}); +#else // ^^^ workaround / no workaround vvv + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end(), + _Right._Unchecked_begin(), _Right._Unchecked_end(), _Synth_three_way{}); +#endif // ^^^ no workaround ^^^ +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template _NODISCARD _CONSTEXPR20 bool operator<(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { +#ifdef __EDG__ // TRANSITION, VSO-1161663 return _STD lexicographical_compare(_Left.begin(), _Left.end(), _Right.begin(), _Right.end()); +#else // ^^^ workaround / no workaround vvv + return _STD lexicographical_compare( + _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()); +#endif // ^^^ no workaround ^^^ } template @@ -802,6 +839,7 @@ template _NODISCARD _CONSTEXPR20 bool operator>=(const array<_Ty, _Size>& _Left, const array<_Ty, _Size>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ #if _HAS_CXX20 // FUNCTION TEMPLATE to_array diff --git a/stl/inc/bitset b/stl/inc/bitset index 8d21a2676ce..ff75bdd526b 100644 --- a/stl/inc/bitset +++ b/stl/inc/bitset @@ -370,9 +370,11 @@ public: return _CSTD memcmp(&_Array[0], &_Right._Array[0], sizeof(_Array)) == 0; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const bitset& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 _NODISCARD bool test(size_t _Pos) const { if (_Bits <= _Pos) { diff --git a/stl/inc/charconv b/stl/inc/charconv index 9ab5d01ec81..bdb31a1b857 100644 --- a/stl/inc/charconv +++ b/stl/inc/charconv @@ -209,6 +209,9 @@ to_chars_result to_chars(char* _First, char* _Last, bool _Value, int _Base = 10) struct from_chars_result { const char* ptr; errc ec; +#if _HAS_CXX20 + _NODISCARD friend bool operator==(const from_chars_result&, const from_chars_result&) = default; +#endif // _HAS_CXX20 }; // FUNCTION from_chars (STRING TO INTEGER) diff --git a/stl/inc/chrono b/stl/inc/chrono index c664419e172..69d0f848fcf 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -1360,15 +1360,16 @@ namespace chrono { return *this; } - inline constexpr day _Last_day_table[] = { - day{31}, day{28}, day{31}, day{30}, day{31}, day{30}, day{31}, day{31}, day{30}, day{31}, day{30}, day{31}}; + // To prevent UB by going out of bounds, four extra days with an invalid day are added. + inline constexpr day _Last_day_table[] = {day{31}, day{28}, day{31}, day{30}, day{31}, day{30}, day{31}, day{31}, + day{30}, day{31}, day{30}, day{31}, day{255}, day{255}, day{255}, day{255}}; _NODISCARD constexpr day _Last_day(const year& _Year, const month& _Month) { if (_Month == month{2} && _Year.is_leap()) { return day{29}; } - return _Last_day_table[static_cast(_Month) - 1]; + return _Last_day_table[(static_cast(_Month) - 1) & 0xF]; } class year_month_day_last; @@ -1420,22 +1421,142 @@ namespace chrono { _CHRONO month _Month; _CHRONO day _Day; + // _Civil_from_days and _Days_from_civil perform conversions between the dates in the (proleptic) Gregorian + // calendar and the continuous count of days since 1970-01-01. + + // To simplify the handling of leap days (February 29th), the algorithm below uses a modified calendar + // internally, in which each year begins on March 1st, while January and February belong to the previous year. + // We denote the modified year and month number as _Yp and _Mp. We also define modified centuries that begin on + // each modified year whose _Yp is a multiple of 100. + + // _Mp | Month | Day of Year + // --- | --------- | ----------- + // 0 | March | [ 0, 30] + // 1 | April | [ 31, 60] + // 2 | May | [ 61, 91] + // 3 | June | [ 92, 121] + // 4 | July | [122, 152] + // 5 | August | [153, 183] + // 6 | September | [184, 213] + // 7 | October | [214, 244] + // 8 | November | [245, 274] + // 9 | December | [275, 305] + // 10 | January | [306, 336] + // 11 | February | [337, 365] on leap years, [337, 364] on regular years + + // _Yp | First Day | Last Day (inclusive) | Leap Year? + // --- | ----------- | -------------------- | ---------- + // -4 | -0004-03-01 | -0003-02-28 | No + // -3 | -0003-03-01 | -0002-02-28 | No + // -2 | -0002-03-01 | -0001-02-28 | No + // -1 | -0001-03-01 | 0000-02-29 | Yes + // 0 | 0000-03-01 | 0001-02-28 | No + // 1 | 0001-03-01 | 0002-02-28 | No + // 2 | 0002-03-01 | 0003-02-28 | No + // 3 | 0003-03-01 | 0004-02-29 | Yes + + // _Century | First Day | Last Day (inclusive) | Long Century? + // -------- | ----------- | -------------------- | ------------- + // -4 | -0400-03-01 | -0300-02-28 | No + // -3 | -0300-03-01 | -0200-02-28 | No + // -2 | -0200-03-01 | -0100-02-28 | No + // -1 | -0100-03-01 | 0000-02-29 | Yes + // 0 | 0000-03-01 | 0100-02-28 | No + // 1 | 0100-03-01 | 0200-02-28 | No + // 2 | 0200-03-01 | 0300-02-28 | No + // 3 | 0300-03-01 | 0400-02-29 | Yes + + // The structure of the modified calendar: + // 1 ) It has a period of 4 centuries. + // 2 ) Each calendar period (146097 days) contains 3 regular centuries followed by a long century (36525 days). + // 3 ) Each regular century (36524 days) contains 24 regular 4-year spans followed by a short 4-year span. + // 3') Each long century (36525 days) contains 25 regular 4-year spans. + // 4 ) Each regular 4-year span (1461 days) contains 3 regular years followed by a leap year. + // 4') Each short 4-year span (1460 days) contains 4 regular years. + + // Formula 1: Compute _Day_of_year of the first day of month _Mp + // + // _Day_of_year = (979 * _Mp + 19) >> 5 + // + // A more well-known formula is 30 * _Mp + floor((3 * _Mp + 2) / 5) or floor((153 * _Mp + 2) / 5), which is used + // in Howard Hinnant's paper. + // + // The formula above returns the same result for all _Mp in [0, 11]. + // Note that 979 / 2^5 = 30.59375 ~= 30.6 = 153 / 5. + + // Formula 1': Compute _Mp from _Day_of_year + // + // _Mp = (535 * _Day_of_year + 333) >> 14 + // + // Howard Hinnant's paper uses floor((5 * _Day_of_year + 2) / 153), the inverse of floor((153 * _Mp + 2) / 5) or + // ceil((153 * _Mp - 2) / 5). + // + // The formula above returns the same result for all _Day_of_year in [0, 365]. + // Note that 2^14 / 535 = 30.624... ~= 30.6 = 153 / 5. + + // Formula 2: Compute _Zx of the first day of year _Yp, where _Zx is the continuous count of days since + // 0000-03-01. + // + // _Zx = ((1461 * _Yp) >> 2) - _Century + (_Century >> 2) + // + // Start with multiplying by the number of days in regular years (365), add one day for the leap year in each + // 4-year span, subtract one day for the short 4-year span in each century, and finally add one day for the long + // century in each calendar period. This gives us 365 * _Yp + floor(_Yp / 4) - _Century + floor(_Century / 4). + + // Formula 2-1: Compute _Day_of_century of the first day of year _Year_of_century + // + // _Day_of_century = (1461 * _Year_of_century) >> 2 + // + // Start with multiplying by the number of days in regular years (365), add one day for the leap year in each + // 4-year span. This gives us 365 * _Year_of_century + floor(_Year_of_century / 4) + // == floor(1461 * _Year_of_century / 4). + + // Formula 2-1': Compute _Year_of_century from _Day_of_century + // + // _Year_of_century = (91867 * (_Day_of_century + 1)) >> 25 + // + // The inverse of floor(1461 * _Year_of_century / 4) or ceil((1461 * _Year_of_century - 3) / 4) is + // floor((4 * _Day_of_century + 3) / 1461). + // + // The formula above returns the same result for all _Day_of_century in [0, 36524]. + // Note that 2^25 / 91867 = 365.2501... ~= 365.25 = 1461 / 4. + + // Formula 2-2: Compute _Zx of the first day of century _Century, where _Zx is the continuous count of days + // since 0000-03-01. + // + // _Zx = (146097 * _Century) >> 2 + // + // Start with multiplying by the number of days in regular centuries (36524), add one day for the long century + // in each calendar period. This gives us 36524 * _Century + floor(_Century / 4) = floor(146097 * _Century / 4). + + // Formula 2-2': Compute _Century from _Zx, where _Zx is the continuous count of days since 0000-03-01. + // + // _Century = floor((4 * _Zx + 3) / 146097) + // + // This is the inverse of floor(146097 * _Year_of_century / 4) or ceil((146097 * _Year_of_century - 3) / 4) + // courtesy of Howard Hinnant // https://howardhinnant.github.io/date_algorithms.html#civil_from_days _NODISCARD static constexpr year_month_day _Civil_from_days(int _Tp) noexcept { static_assert(numeric_limits::digits >= 32); static_assert(numeric_limits::digits >= 26); - const int _Zx = _Tp + 719468; // Shift epoch to 0000-03-01 + const int _Zx = _Tp + 719468; // Shift epoch to 0000-03-01 + // Formula 2-2' const int _Century = (_Zx >= 0 ? 4 * _Zx + 3 : 4 * _Zx - 146093) / 146097; + // Formula 2-2 const unsigned int _Day_of_century = static_cast(_Zx - ((146097 * _Century) >> 2)); // [0, 36524] + // Formula 2-1' const unsigned int _Year_of_century = (91867 * (_Day_of_century + 1)) >> 25; // [0, 99] - const int _Year = static_cast(_Year_of_century) + _Century * 100; // Where March is the first month + const int _Yp = static_cast(_Year_of_century) + _Century * 100; // Where March is the first month + // Formula 2-1 const unsigned int _Day_of_year = _Day_of_century - ((1461 * _Year_of_century) >> 2); // [0, 365] - const unsigned int _Mp = (535 * _Day_of_year + 333) >> 14; // [0, 11] - const unsigned int _Day = _Day_of_year - ((979 * _Mp + 19) >> 5) + 1; // [1, 31] - const unsigned int _Month = _Mp + (_Mp < 10 ? 3 : static_cast(-9)); // [1, 12] - return year_month_day{_CHRONO year{_Year + (_Month <= 2)}, _CHRONO month{_Month}, _CHRONO day{_Day}}; + // Formula 1' + const unsigned int _Mp = (535 * _Day_of_year + 333) >> 14; // [0, 11] + // Formula 1 + const unsigned int _Day = _Day_of_year - ((979 * _Mp + 19) >> 5) + 1; // [1, 31] + const unsigned int _Month = _Mp + (_Mp < 10 ? 3 : static_cast(-9)); // [1, 12] + return year_month_day{_CHRONO year{_Yp + (_Month <= 2)}, _CHRONO month{_Month}, _CHRONO day{_Day}}; } // courtesy of Howard Hinnant // https://howardhinnant.github.io/date_algorithms.html#days_from_civil @@ -1443,11 +1564,13 @@ namespace chrono { static_assert(numeric_limits::digits >= 18); static_assert(numeric_limits::digits >= 26); const unsigned int _Mo = static_cast(_Month); // [1, 12] - const int _Ye = static_cast(_Year) - (_Mo <= 2); - const int _Century = (_Ye >= 0 ? _Ye : _Ye - 99) / 100; + const int _Yp = static_cast(_Year) - (_Mo <= 2); + const int _Century = (_Yp >= 0 ? _Yp : _Yp - 99) / 100; const unsigned int _Mp = _Mo + (_Mo > 2 ? static_cast(-3) : 9); // [0, 11] + // Formula 1 const int _Day_of_year = static_cast(((979 * _Mp + 19) >> 5) + static_cast(_Day)) - 1; - return days{((_Ye * 1461) >> 2) - _Century + (_Century >> 2) + _Day_of_year - 719468}; + // Formula 2 + return days{((1461 * _Yp) >> 2) - _Century + (_Century >> 2) + _Day_of_year - 719468}; } }; diff --git a/stl/inc/cmath b/stl/inc/cmath index 46eecf2faff..358fc459b04 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -902,50 +902,50 @@ _STD_END #if _HAS_CXX17 _EXTERN_C -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_assoc_laguerre(unsigned int, unsigned int, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_assoc_laguerref(unsigned int, unsigned int, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_assoc_legendre(unsigned int, unsigned int, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_assoc_legendref(unsigned int, unsigned int, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_beta(double, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_betaf(float, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_comp_ellint_1(double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_comp_ellint_1f(float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_comp_ellint_2(double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_comp_ellint_2f(float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_comp_ellint_3(double, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_comp_ellint_3f(float, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_bessel_i(double, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_bessel_if(float, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_bessel_j(double, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_bessel_jf(float, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_bessel_k(double, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_bessel_kf(float, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_neumann(double, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_neumannf(float, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_ellint_1(double, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_ellint_1f(float, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_ellint_2(double, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_ellint_2f(float, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_ellint_3(double, double, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_ellint_3f(float, float, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_expint(double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_expintf(float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_hermite(unsigned int, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_hermitef(unsigned int, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_laguerre(unsigned int, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_laguerref(unsigned int, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_legendre(unsigned int, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_legendref(unsigned int, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_riemann_zeta(double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_riemann_zetaf(float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_sph_bessel(unsigned int, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_sph_besself(unsigned int, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_sph_legendre(unsigned int, unsigned int, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_sph_legendref(unsigned int, unsigned int, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_sph_neumann(unsigned int, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_sph_neumannf(unsigned int, float) noexcept; -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_hypot3(double, double, double) noexcept; -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_hypot3f(float, float, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_assoc_laguerre(unsigned int, unsigned int, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_assoc_laguerref(unsigned int, unsigned int, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_assoc_legendre(unsigned int, unsigned int, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_assoc_legendref(unsigned int, unsigned int, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_beta(double, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_betaf(float, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_comp_ellint_1(double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_comp_ellint_1f(float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_comp_ellint_2(double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_comp_ellint_2f(float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_comp_ellint_3(double, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_comp_ellint_3f(float, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_cyl_bessel_i(double, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_cyl_bessel_if(float, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_cyl_bessel_j(double, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_cyl_bessel_jf(float, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_cyl_bessel_k(double, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_cyl_bessel_kf(float, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_cyl_neumann(double, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_cyl_neumannf(float, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_ellint_1(double, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_ellint_1f(float, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_ellint_2(double, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_ellint_2f(float, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_ellint_3(double, double, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_ellint_3f(float, float, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_expint(double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_expintf(float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_hermite(unsigned int, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_hermitef(unsigned int, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_laguerre(unsigned int, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_laguerref(unsigned int, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_legendre(unsigned int, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_legendref(unsigned int, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_riemann_zeta(double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_riemann_zetaf(float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_sph_bessel(unsigned int, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_sph_besself(unsigned int, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_sph_legendre(unsigned int, unsigned int, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_sph_legendref(unsigned int, unsigned int, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_sph_neumann(unsigned int, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_sph_neumannf(unsigned int, float) noexcept; +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_hypot3(double, double, double) noexcept; +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_hypot3f(float, float, float) noexcept; _END_EXTERN_C _STD_BEGIN diff --git a/stl/inc/compare b/stl/inc/compare index 46bdfd0371f..ab47a879606 100644 --- a/stl/inc/compare +++ b/stl/inc/compare @@ -342,6 +342,35 @@ struct compare_three_way { }; // clang-format on +// STRUCT _Synth_three_way +struct _Synth_three_way { + // clang-format off + template + _NODISCARD constexpr auto operator()(const _Ty1& _Left, const _Ty2& _Right) const + requires requires { + { _Left < _Right } -> _Boolean_testable; + { _Right < _Left } -> _Boolean_testable; + } + // clang-format on + { + if constexpr (three_way_comparable_with<_Ty1, _Ty2>) { + return _Left <=> _Right; + } else { + if (_Left < _Right) { + return weak_ordering::less; + } else if (_Right < _Left) { + return weak_ordering::greater; + } else { + return weak_ordering::equivalent; + } + } + } +}; + +// ALIAS TEMPLATE _Synth_three_way_result +template +using _Synth_three_way_result = decltype(_Synth_three_way{}(_STD declval<_Ty1&>(), _STD declval<_Ty2&>())); + // Note: The following CPOs are passing arguments as lvalues; see GH-1374. // CUSTOMIZATION POINT OBJECT strong_order diff --git a/stl/inc/complex b/stl/inc/complex index e0aff1ed4ee..5e935cf52d0 100644 --- a/stl/inc/complex +++ b/stl/inc/complex @@ -1514,12 +1514,15 @@ _NODISCARD constexpr bool operator==(const complex<_Ty>& _Left, const _Ty& _Righ return _Left.real() == _Right && _Left.imag() == 0; } +#if !_HAS_CXX20 template _NODISCARD constexpr bool operator==(const _Ty& _Left, const complex<_Ty>& _Right) { return _Left == _Right.real() && 0 == _Right.imag(); } +#endif // !_HAS_CXX20 // FUNCTION TEMPLATE operator!= +#if !_HAS_CXX20 template _NODISCARD constexpr bool operator!=(const complex<_Ty>& _Left, const complex<_Ty>& _Right) { return !(_Left == _Right); @@ -1534,6 +1537,7 @@ template _NODISCARD constexpr bool operator!=(const _Ty& _Left, const complex<_Ty>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // FUNCTION TEMPLATE imag template diff --git a/stl/inc/condition_variable b/stl/inc/condition_variable index 42d02aa8db7..3c17c8227a3 100644 --- a/stl/inc/condition_variable +++ b/stl/inc/condition_variable @@ -92,12 +92,18 @@ public: template cv_status wait_until(_Lock& _Lck, const chrono::time_point<_Clock, _Duration>& _Abs_time) { // wait until time point +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 return wait_for(_Lck, _Abs_time - _Clock::now()); } template bool wait_until(_Lock& _Lck, const chrono::time_point<_Clock, _Duration>& _Abs_time, _Predicate _Pred) { // wait for signal with timeout and check predicate +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 while (!_Pred()) { if (wait_until(_Lck, _Abs_time) == cv_status::timeout) { return _Pred(); @@ -193,6 +199,7 @@ public: template bool wait_until( _Lock& _Lck, stop_token _Stoken, const chrono::time_point<_Clock, _Duration>& _Abs_time, _Predicate _Pred) { + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); stop_callback<_Cv_any_notify_all> _Cb{_Stoken, this}; for (;;) { if (_Pred()) { diff --git a/stl/inc/deque b/stl/inc/deque index cb0f3258fd3..9018290d9c6 100644 --- a/stl/inc/deque +++ b/stl/inc/deque @@ -107,6 +107,11 @@ public: return _Myoff == _Right._Myoff; } +#if _HAS_CXX20 + _NODISCARD strong_ordering operator<=>(const _Deque_unchecked_const_iterator& _Right) const noexcept { + return _Myoff <=> _Right._Myoff; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD bool operator!=(const _Deque_unchecked_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -126,6 +131,7 @@ public: _NODISCARD bool operator>=(const _Deque_unchecked_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 const _Container_base12* _Getcont() const noexcept { // get container pointer return _Mycont; @@ -345,6 +351,12 @@ public: return this->_Myoff == _Right._Myoff; } +#if _HAS_CXX20 + _NODISCARD strong_ordering operator<=>(const _Deque_const_iterator& _Right) const noexcept { + _Compat(_Right); + return this->_Myoff <=> _Right._Myoff; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD bool operator!=(const _Deque_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -365,6 +377,7 @@ public: _NODISCARD bool operator>=(const _Deque_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 void _Compat(const _Deque_const_iterator& _Right) const noexcept { // test for compatible iterator pair #if _ITERATOR_DEBUG_LEVEL == 0 @@ -1582,11 +1595,20 @@ _NODISCARD bool operator==(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Al && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Ty> operator<=>(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end(), + _Right._Unchecked_begin(), _Right._Unchecked_end(), _Synth_three_way{}); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template _NODISCARD bool operator<(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Alloc>& _Right) { return _STD lexicographical_compare( @@ -1607,6 +1629,7 @@ template _NODISCARD bool operator>=(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ #if _HAS_CXX20 template diff --git a/stl/inc/filesystem b/stl/inc/filesystem index c0444a29bd9..05341d65c4c 100644 --- a/stl/inc/filesystem +++ b/stl/inc/filesystem @@ -26,6 +26,10 @@ #include #include +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 + #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) @@ -1399,6 +1403,11 @@ namespace filesystem { return _Left.compare(_Right) == 0; } +#if _HAS_CXX20 + _NODISCARD friend strong_ordering operator<=>(const path& _Left, const path& _Right) noexcept { + return _Left.compare(_Right) <=> 0; + } +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD friend bool operator!=(const path& _Left, const path& _Right) noexcept { return _Left.compare(_Right) != 0; } @@ -1418,6 +1427,7 @@ namespace filesystem { _NODISCARD friend bool operator>=(const path& _Left, const path& _Right) noexcept { return _Left.compare(_Right) >= 0; } +#endif // !_HAS_CXX20 _NODISCARD friend path operator/(const path& _Left, const path& _Right) { // append a pair of paths together return path(_Left) /= _Right; @@ -1593,9 +1603,11 @@ namespace filesystem { return _Lhs._Position == _Rhs._Position; } +#if !_HAS_CXX20 _NODISCARD friend bool operator!=(const _Path_iterator& _Lhs, const _Path_iterator& _Rhs) { return _Lhs._Position != _Rhs._Position; } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL != 0 friend void _Verify_range(const _Path_iterator& _Lhs, const _Path_iterator& _Rhs) { @@ -1967,6 +1979,12 @@ namespace filesystem { return _Myperms; } +#if _HAS_CXX20 + _NODISCARD friend bool operator==(const file_status& _Lhs, const file_status& _Rhs) noexcept { + return _Lhs._Myftype == _Rhs._Myftype && _Lhs._Myperms == _Rhs._Myperms; + } +#endif // _HAS_CXX20 + void _Refresh(const __std_win_error _Error, const __std_fs_stats& _Stats) noexcept { if (_Error == __std_win_error::_Success) { const auto _Attrs = _Stats._Attributes; @@ -2445,18 +2463,23 @@ namespace filesystem { return _Result._Status; } - _NODISCARD bool operator<(const directory_entry& _Rhs) const noexcept { - return _Path < _Rhs._Path; - } - _NODISCARD bool operator==(const directory_entry& _Rhs) const noexcept { return _Path == _Rhs._Path; } +#if _HAS_CXX20 + _NODISCARD strong_ordering operator<=>(const directory_entry& _Rhs) const noexcept { + return _Path <=> _Rhs._Path; + } +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD bool operator!=(const directory_entry& _Rhs) const noexcept { return _Path != _Rhs._Path; } + _NODISCARD bool operator<(const directory_entry& _Rhs) const noexcept { + return _Path < _Rhs._Path; + } + _NODISCARD bool operator<=(const directory_entry& _Rhs) const noexcept { return _Path <= _Rhs._Path; } @@ -2468,6 +2491,7 @@ namespace filesystem { _NODISCARD bool operator>=(const directory_entry& _Rhs) const noexcept { return _Path >= _Rhs._Path; } +#endif // !_HAS_CXX20 // [fs.dir.entry.io], inserter template @@ -2706,9 +2730,11 @@ namespace filesystem { return _Impl == _Rhs._Impl; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const directory_iterator& _Rhs) const noexcept /* strengthened */ { return _Impl != _Rhs._Impl; } +#endif // !_HAS_CXX20 _Directory_entry_proxy operator++(int) { _Directory_entry_proxy _Proxy(**this); @@ -2955,9 +2981,11 @@ namespace filesystem { return _Impl == _Rhs._Impl; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const recursive_directory_iterator& _Rhs) const noexcept { return _Impl != _Rhs._Impl; } +#endif // !_HAS_CXX20 _Directory_entry_proxy operator++(int) { _Directory_entry_proxy _Proxy(**this); @@ -3649,6 +3677,10 @@ namespace filesystem { uintmax_t capacity; uintmax_t free; uintmax_t available; + +#if _HAS_CXX20 + _NODISCARD friend constexpr bool operator==(const space_info&, const space_info&) noexcept = default; +#endif // _HAS_CXX20 }; _NODISCARD inline space_info space(const path& _Target) { diff --git a/stl/inc/forward_list b/stl/inc/forward_list index 986f9e3a984..6505741e507 100644 --- a/stl/inc/forward_list +++ b/stl/inc/forward_list @@ -63,17 +63,21 @@ public: return _Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _Flist_unchecked_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 _NODISCARD bool operator==(_Default_sentinel) const noexcept { return _Ptr == nullptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(_Default_sentinel) const noexcept { return _Ptr != nullptr; } +#endif // !_HAS_CXX20 _Nodeptr _Ptr; // pointer to node }; @@ -161,9 +165,11 @@ public: return this->_Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _Flist_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL == 2 friend void _Verify_range(const _Flist_const_iterator& _First, const _Flist_const_iterator& _Last) noexcept { @@ -814,6 +820,10 @@ public: return {}; } + _Unchecked_const_iterator _Unchecked_end_iter() const noexcept { + return _Unchecked_const_iterator(nullptr, nullptr); + } + iterator _Make_iter(_Nodeptr _Where) const noexcept { return iterator(_Where, _STD addressof(_Mypair._Myval2)); } @@ -1518,17 +1528,29 @@ void swap(forward_list<_Ty, _Alloc>& _Left, forward_list<_Ty, _Alloc>& _Right) n template _NODISCARD bool operator==(const forward_list<_Ty, _Alloc>& _Left, const forward_list<_Ty, _Alloc>& _Right) { - return _STD equal(_Left.begin(), _Left.end(), _Right.begin(), _Right.end()); + return _STD equal( + _Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin(), _Right._Unchecked_end_iter()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const forward_list<_Ty, _Alloc>& _Left, const forward_list<_Ty, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Ty> operator<=>( + const forward_list<_Ty, _Alloc>& _Left, const forward_list<_Ty, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), + _Right._Unchecked_begin(), _Right._Unchecked_end_iter(), _Synth_three_way{}); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template _NODISCARD bool operator<(const forward_list<_Ty, _Alloc>& _Left, const forward_list<_Ty, _Alloc>& _Right) { - return _STD lexicographical_compare(_Left.begin(), _Left.end(), _Right.begin(), _Right.end()); + return _STD lexicographical_compare( + _Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin(), _Right._Unchecked_end_iter()); } template @@ -1545,6 +1567,7 @@ template _NODISCARD bool operator>=(const forward_list<_Ty, _Alloc>& _Left, const forward_list<_Ty, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ #if _HAS_CXX20 template diff --git a/stl/inc/functional b/stl/inc/functional index 4a46439d871..a8aee539270 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -1281,6 +1281,7 @@ _NODISCARD bool operator==(const function<_Fty>& _Other, nullptr_t) noexcept { return !_Other; } +#if !_HAS_CXX20 template _NODISCARD bool operator==(nullptr_t, const function<_Fty>& _Other) noexcept { return !_Other; @@ -1295,6 +1296,7 @@ template _NODISCARD bool operator!=(nullptr_t, const function<_Fty>& _Other) noexcept { return static_cast(_Other); } +#endif // !_HAS_CXX20 // PLACEHOLDERS template diff --git a/stl/inc/future b/stl/inc/future index d38072b6692..3036d53dd0b 100644 --- a/stl/inc/future +++ b/stl/inc/future @@ -770,6 +770,9 @@ public: template future_status wait_until(const chrono::time_point<_Clock, _Dur>& _Abs_time) const { // wait until time point +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 if (!valid()) { _Throw_future_error(make_error_code(future_errc::no_state)); } diff --git a/stl/inc/header-units.json b/stl/inc/header-units.json index 66cb2996698..d33b9abc629 100644 --- a/stl/inc/header-units.json +++ b/stl/inc/header-units.json @@ -87,6 +87,7 @@ "semaphore", "set", "shared_mutex", + "source_location", "span", "sstream", "stack", diff --git a/stl/inc/iterator b/stl/inc/iterator index ca74d58917c..6fd2233e372 100644 --- a/stl/inc/iterator +++ b/stl/inc/iterator @@ -302,11 +302,13 @@ _NODISCARD bool operator==(const istream_iterator<_Ty, _Elem, _Traits, _Diff>& _ return _Left._Equal(_Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const istream_iterator<_Ty, _Elem, _Traits, _Diff>& _Left, const istream_iterator<_Ty, _Elem, _Traits, _Diff>& _Right) noexcept /* strengthened */ { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // CLASS TEMPLATE ostream_iterator template > @@ -488,11 +490,13 @@ _NODISCARD bool operator==( return _Left.equal(_Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=( const istreambuf_iterator<_Elem, _Traits>& _Left, const istreambuf_iterator<_Elem, _Traits>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // CLASS TEMPLATE ostreambuf_iterator template @@ -1492,6 +1496,13 @@ public: return _Myindex == _Right._Myindex; } +#if _HAS_CXX20 + _NODISCARD constexpr _STD strong_ordering operator<=>(const checked_array_iterator& _Right) const noexcept { + _STL_VERIFY(_Myarray == _Right._Myarray && _Mysize == _Right._Mysize, + "cannot compare incompatible checked_array_iterators"); + return _Myindex <=> _Right._Myindex; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD constexpr bool operator!=(const checked_array_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -1513,6 +1524,7 @@ public: _NODISCARD constexpr bool operator>=(const checked_array_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 friend constexpr void _Verify_range( const checked_array_iterator& _First, const checked_array_iterator& _Last) noexcept { @@ -1646,6 +1658,11 @@ public: return _Myptr == _Right._Myptr; } +#if _HAS_CXX20 + _NODISCARD constexpr _STD strong_ordering operator<=>(const unchecked_array_iterator& _Right) const noexcept { + return _Myptr <=> _Right._Myptr; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD constexpr bool operator!=(const unchecked_array_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -1665,6 +1682,7 @@ public: _NODISCARD constexpr bool operator>=(const unchecked_array_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL != 0 friend constexpr void _Verify_range( diff --git a/stl/inc/list b/stl/inc/list index dca51000ff7..58f93ff9bb6 100644 --- a/stl/inc/list +++ b/stl/inc/list @@ -74,9 +74,11 @@ public: return _Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _List_unchecked_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 _Nodeptr _Ptr; // pointer to node }; @@ -199,9 +201,11 @@ public: return this->_Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _List_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL == 2 friend void _Verify_range(const _List_const_iterator& _First, const _List_const_iterator& _Last) noexcept { @@ -1803,17 +1807,28 @@ void swap(list<_Ty, _Alloc>& _Left, list<_Ty, _Alloc>& _Right) noexcept /* stren template _NODISCARD bool operator==(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Alloc>& _Right) { - return _Left.size() == _Right.size() && _STD equal(_Left.begin(), _Left.end(), _Right.begin()); + return _Left.size() == _Right.size() + && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Ty> operator<=>(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end(), + _Right._Unchecked_begin(), _Right._Unchecked_end(), _Synth_three_way{}); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template _NODISCARD bool operator<(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Alloc>& _Right) { - return _STD lexicographical_compare(_Left.begin(), _Left.end(), _Right.begin(), _Right.end()); + return _STD lexicographical_compare( + _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()); } template @@ -1830,6 +1845,7 @@ template _NODISCARD bool operator>=(const list<_Ty, _Alloc>& _Left, const list<_Ty, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ #if _HAS_CXX20 template diff --git a/stl/inc/map b/stl/inc/map index 078249a414a..1c66bbcd478 100644 --- a/stl/inc/map +++ b/stl/inc/map @@ -369,11 +369,21 @@ _NODISCARD bool operator==(const map<_Kty, _Ty, _Pr, _Alloc>& _Left, const map<_ && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const map<_Kty, _Ty, _Pr, _Alloc>& _Left, const map<_Kty, _Ty, _Pr, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result> operator<=>( + const map<_Kty, _Ty, _Pr, _Alloc>& _Left, const map<_Kty, _Ty, _Pr, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), + _Right._Unchecked_begin(), _Right._Unchecked_end_iter(), _Synth_three_way{}); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template _NODISCARD bool operator<(const map<_Kty, _Ty, _Pr, _Alloc>& _Left, const map<_Kty, _Ty, _Pr, _Alloc>& _Right) { return _STD lexicographical_compare( @@ -394,6 +404,7 @@ template _NODISCARD bool operator>=(const map<_Kty, _Ty, _Pr, _Alloc>& _Left, const map<_Kty, _Ty, _Pr, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ template void swap(map<_Kty, _Ty, _Pr, _Alloc>& _Left, map<_Kty, _Ty, _Pr, _Alloc>& _Right) noexcept( @@ -557,12 +568,22 @@ _NODISCARD bool operator==( && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=( const multimap<_Kty, _Ty, _Pr, _Alloc>& _Left, const multimap<_Kty, _Ty, _Pr, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result> operator<=>( + const multimap<_Kty, _Ty, _Pr, _Alloc>& _Left, const multimap<_Kty, _Ty, _Pr, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), + _Right._Unchecked_begin(), _Right._Unchecked_end_iter(), _Synth_three_way{}); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template _NODISCARD bool operator<( const multimap<_Kty, _Ty, _Pr, _Alloc>& _Left, const multimap<_Kty, _Ty, _Pr, _Alloc>& _Right) { @@ -587,6 +608,7 @@ _NODISCARD bool operator>=( const multimap<_Kty, _Ty, _Pr, _Alloc>& _Left, const multimap<_Kty, _Ty, _Pr, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ template void swap(multimap<_Kty, _Ty, _Pr, _Alloc>& _Left, multimap<_Kty, _Ty, _Pr, _Alloc>& _Right) noexcept( diff --git a/stl/inc/memory b/stl/inc/memory index ae479ece7bb..4fe7830d5df 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -78,7 +78,7 @@ namespace ranges { _STL_INTERNAL_STATIC_ASSERT(_No_throw_sentinel_for<_OSe, _Out>); _STL_INTERNAL_STATIC_ASSERT(constructible_from, iter_reference_t<_It>>); - if constexpr (is_same_v<_Se, _It> && _Ptr_copy_cat<_It, _Out>::_Really_trivial) { + if constexpr (is_same_v<_Se, _It> && is_same_v<_OSe, _Out> && _Ptr_copy_cat<_It, _Out>::_Really_trivial) { return _Copy_memcpy_common(_IFirst, _ILast, _OFirst, _OLast); } else { _Uninitialized_backout _Backout{_STD move(_OFirst)}; @@ -334,7 +334,7 @@ namespace ranges { private: template - _NODISCARD static _It _Uninitialized_fill_unchecked(_It _OFirst, const _Se _OLast, const _Ty& _Val) { + _NODISCARD static _It _Uninitialized_fill_unchecked(_It _OFirst, _Se _OLast, const _Ty& _Val) { _STL_INTERNAL_STATIC_ASSERT(_No_throw_forward_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(_No_throw_sentinel_for<_Se, _It>); _STL_INTERNAL_STATIC_ASSERT(constructible_from, const _Ty&>); @@ -792,16 +792,13 @@ namespace ranges { private: template - _NODISCARD static _It _Uninitialized_value_construct_unchecked(_It _OFirst, const _Se _OLast) { + _NODISCARD static _It _Uninitialized_value_construct_unchecked(_It _OFirst, _Se _OLast) { _STL_INTERNAL_STATIC_ASSERT(_No_throw_forward_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(_No_throw_sentinel_for<_Se, _It>); _STL_INTERNAL_STATIC_ASSERT(default_initializable>); if constexpr (_Use_memset_value_construct_v<_It>) { - const auto _OFinal = _RANGES next(_OFirst, _STD move(_OLast)); - const auto _Count = static_cast(_OFinal - _OFirst); - _CSTD memset(_OFirst, 0, _Count); - return _OFinal; + return _Zero_range(_OFirst, _RANGES next(_OFirst, _STD move(_OLast))); } else { _Uninitialized_backout _Backout{_STD move(_OFirst)}; @@ -850,8 +847,7 @@ namespace ranges { auto _UFirst = _Get_unwrapped_n(_STD move(_First), _Count); if constexpr (_Use_memset_value_construct_v<_It>) { - _CSTD memset(_UFirst, 0, static_cast(_Count)); - _Seek_wrapped(_First, _UFirst + _Count); + _Seek_wrapped(_First, _Zero_range(_UFirst, _UFirst + _Count)); } else { _Uninitialized_backout _Backout{_STD move(_UFirst)}; @@ -1795,6 +1791,12 @@ _NODISCARD bool operator==(const shared_ptr<_Ty1>& _Left, const shared_ptr<_Ty2> return _Left.get() == _Right.get(); } +#if _HAS_CXX20 +template +_NODISCARD strong_ordering operator<=>(const shared_ptr<_Ty1>& _Left, const shared_ptr<_Ty2>& _Right) noexcept { + return _Left.get() <=> _Right.get(); +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD bool operator!=(const shared_ptr<_Ty1>& _Left, const shared_ptr<_Ty2>& _Right) noexcept { return _Left.get() != _Right.get(); @@ -1819,12 +1821,19 @@ template _NODISCARD bool operator<=(const shared_ptr<_Ty1>& _Left, const shared_ptr<_Ty2>& _Right) noexcept { return _Left.get() <= _Right.get(); } +#endif // ^^^ !_HAS_CXX20 ^^^ template _NODISCARD bool operator==(const shared_ptr<_Ty>& _Left, nullptr_t) noexcept { return _Left.get() == nullptr; } +#if _HAS_CXX20 +template +_NODISCARD strong_ordering operator<=>(const shared_ptr<_Ty>& _Left, nullptr_t) noexcept { + return _Left.get() <=> static_cast::element_type*>(nullptr); +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD bool operator==(nullptr_t, const shared_ptr<_Ty>& _Right) noexcept { return nullptr == _Right.get(); @@ -1879,6 +1888,7 @@ template _NODISCARD bool operator<=(nullptr_t, const shared_ptr<_Ty>& _Right) noexcept { return static_cast::element_type*>(nullptr) <= _Right.get(); } +#endif // ^^^ !_HAS_CXX20 ^^^ template basic_ostream<_Elem, _Traits>& operator<<(basic_ostream<_Elem, _Traits>& _Out, const shared_ptr<_Ty>& _Px) { @@ -2070,20 +2080,26 @@ template _NODISCARD _Refc* _Allocate_flexible_array(const size_t _Count) { const size_t _Bytes = _Calculate_bytes_for_flexible_array<_Refc, _Check_overflow::_Yes>(_Count); constexpr size_t _Align = alignof(_Refc); - if constexpr (_Align <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) { - return static_cast<_Refc*>(::operator new(_Bytes)); - } else { +#ifdef __cpp_aligned_new + if constexpr (_Align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) { return static_cast<_Refc*>(::operator new (_Bytes, align_val_t{_Align})); + } else +#endif // __cpp_aligned_new + { + return static_cast<_Refc*>(::operator new(_Bytes)); } } template void _Deallocate_flexible_array(_Refc* const _Ptr) noexcept { constexpr size_t _Align = alignof(_Refc); - if constexpr (_Align <= __STDCPP_DEFAULT_NEW_ALIGNMENT__) { - ::operator delete(static_cast(_Ptr)); - } else { +#ifdef __cpp_aligned_new + if constexpr (_Align > __STDCPP_DEFAULT_NEW_ALIGNMENT__) { ::operator delete (static_cast(_Ptr), align_val_t{_Align}); + } else +#endif // __cpp_aligned_new + { + ::operator delete(static_cast(_Ptr)); } } @@ -3419,10 +3435,12 @@ _NODISCARD bool operator==(const unique_ptr<_Ty1, _Dx1>& _Left, const unique_ptr return _Left.get() == _Right.get(); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const unique_ptr<_Ty1, _Dx1>& _Left, const unique_ptr<_Ty2, _Dx2>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 template _NODISCARD bool operator<(const unique_ptr<_Ty1, _Dx1>& _Left, const unique_ptr<_Ty2, _Dx2>& _Right) { @@ -3447,11 +3465,25 @@ _NODISCARD bool operator<=(const unique_ptr<_Ty1, _Dx1>& _Left, const unique_ptr return !(_Right < _Left); } +#ifdef __cpp_lib_concepts +// clang-format off +template + requires three_way_comparable_with::pointer, + typename unique_ptr<_Ty2, _Dx2>::pointer> +_NODISCARD compare_three_way_result_t::pointer, + typename unique_ptr<_Ty2, _Dx2>::pointer> + operator<=>(const unique_ptr<_Ty1, _Dx1>& _Left, const unique_ptr<_Ty2, _Dx2>& _Right) { + // clang-format on + return _Left.get() <=> _Right.get(); +} +#endif // __cpp_lib_concepts + template _NODISCARD bool operator==(const unique_ptr<_Ty, _Dx>& _Left, nullptr_t) noexcept { return !_Left; } +#if !_HAS_CXX20 template _NODISCARD bool operator==(nullptr_t, const unique_ptr<_Ty, _Dx>& _Right) noexcept { return !_Right; @@ -3466,6 +3498,7 @@ template _NODISCARD bool operator!=(nullptr_t _Left, const unique_ptr<_Ty, _Dx>& _Right) noexcept { return !(_Left == _Right); } +#endif // !_HAS_CXX20 template _NODISCARD bool operator<(const unique_ptr<_Ty, _Dx>& _Left, nullptr_t _Right) { @@ -3509,6 +3542,17 @@ _NODISCARD bool operator<=(nullptr_t _Left, const unique_ptr<_Ty, _Dx>& _Right) return !(_Right < _Left); } +#ifdef __cpp_lib_concepts +// clang-format off +template + requires three_way_comparable::pointer> +_NODISCARD compare_three_way_result_t::pointer> operator<=>( + const unique_ptr<_Ty, _Dx>& _Left, nullptr_t) { + // clang-format on + return _Left.get() <=> static_cast::pointer>(nullptr); +} +#endif // __cpp_lib_concepts + template struct _Can_stream_unique_ptr : false_type {}; template diff --git a/stl/inc/memory_resource b/stl/inc/memory_resource index a97b4c99049..618c74aae25 100644 --- a/stl/inc/memory_resource +++ b/stl/inc/memory_resource @@ -44,7 +44,7 @@ namespace pmr { #endif // __cpp_aligned_new } - extern "C" _CRT_SATELLITE_1 _NODISCARD memory_resource* __cdecl null_memory_resource() noexcept; + extern "C" _NODISCARD _CRT_SATELLITE_1 memory_resource* __cdecl null_memory_resource() noexcept; // FUNCTION new_delete_resource class _Identity_equal_resource : public memory_resource { diff --git a/stl/inc/mutex b/stl/inc/mutex index cf58b283725..aafefe48bcd 100644 --- a/stl/inc/mutex +++ b/stl/inc/mutex @@ -153,7 +153,12 @@ public: template _NODISCARD_CTOR unique_lock(_Mutex& _Mtx, const chrono::time_point<_Clock, _Duration>& _Abs_time) - : _Pmtx(_STD addressof(_Mtx)), _Owns(_Pmtx->try_lock_until(_Abs_time)) {} // construct and lock with timeout + : _Pmtx(_STD addressof(_Mtx)), _Owns(_Pmtx->try_lock_until(_Abs_time)) { + // construct and lock with timeout +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 + } _NODISCARD_CTOR unique_lock(_Mutex& _Mtx, const xtime* _Abs_time) : _Pmtx(_STD addressof(_Mtx)), _Owns(false) { // try to lock until _Abs_time @@ -209,6 +214,9 @@ public: template _NODISCARD bool try_lock_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) { +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 _Validate(); _Owns = _Pmtx->try_lock_until(_Abs_time); return _Owns; @@ -635,6 +643,9 @@ public: template cv_status wait_until(unique_lock& _Lck, const chrono::time_point<_Clock, _Duration>& _Abs_time) { // wait until time point +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 for (;;) { const auto _Now = _Clock::now(); if (_Abs_time <= _Now) { @@ -654,6 +665,9 @@ public: bool wait_until( unique_lock& _Lck, const chrono::time_point<_Clock, _Duration>& _Abs_time, _Predicate _Pred) { // wait for signal with timeout and check predicate +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 return _Wait_until1(_Lck, _Abs_time, _Pred); } @@ -792,8 +806,11 @@ public: } template - _NODISCARD bool try_lock_until( - const chrono::time_point<_Clock, _Duration>& _Abs_time) { // try to lock the mutex with timeout + _NODISCARD bool try_lock_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) { + // try to lock the mutex with timeout +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 return _Try_lock_until(_Abs_time); } @@ -903,8 +920,11 @@ public: } template - _NODISCARD bool try_lock_until( - const chrono::time_point<_Clock, _Duration>& _Abs_time) { // try to lock the mutex with timeout + _NODISCARD bool try_lock_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) { + // try to lock the mutex with timeout +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 return _Try_lock_until(_Abs_time); } diff --git a/stl/inc/optional b/stl/inc/optional index 04e36c0c10b..81d2639a164 100644 --- a/stl/inc/optional +++ b/stl/inc/optional @@ -11,6 +11,9 @@ #if !_HAS_CXX17 #pragma message("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 @@ -480,11 +483,30 @@ _NODISCARD constexpr bool operator>=(const optional<_Ty1>& _Left, const optional return !_Right.has_value() || (_Left.has_value() && *_Left >= *_Right); } +#ifdef __cpp_lib_concepts +template _Ty2> +_NODISCARD constexpr compare_three_way_result_t<_Ty1, _Ty2> operator<=>( + const optional<_Ty1>& _Left, const optional<_Ty2>& _Right) { + if (_Left && _Right) { + return *_Left <=> *_Right; + } + + return _Left.has_value() <=> _Right.has_value(); +} +#endif // __cpp_lib_concepts + // COMPARISONS WITH nullopt [optional.nullops] template _NODISCARD constexpr bool operator==(const optional<_Ty>& _Left, nullopt_t) noexcept { return !_Left.has_value(); } + +#if _HAS_CXX20 +template +_NODISCARD constexpr strong_ordering operator<=>(const optional<_Ty>& _Left, nullopt_t) noexcept { + return _Left.has_value() <=> false; +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD constexpr bool operator==(nullopt_t, const optional<_Ty>& _Right) noexcept { return !_Right.has_value(); @@ -534,6 +556,7 @@ template _NODISCARD constexpr bool operator>=(nullopt_t, const optional<_Ty>& _Right) noexcept { return !_Right.has_value(); } +#endif // !_HAS_CXX20 // COMPARISONS WITH T [optional.comp_with_t] template @@ -567,6 +590,7 @@ template = _NODISCARD constexpr bool operator==(const optional<_Ty1>& _Left, const _Ty2& _Right) { return _Left ? *_Left == _Right : false; } + template = 0> _NODISCARD constexpr bool operator==(const _Ty1& _Left, const optional<_Ty2>& _Right) { return _Right ? _Left == *_Right : false; @@ -617,6 +641,22 @@ _NODISCARD constexpr bool operator>=(const _Ty1& _Left, const optional<_Ty2>& _R return _Right ? _Left >= *_Right : true; } +#ifdef __cpp_lib_concepts +// clang-format off +template + requires (!_Is_specialization_v<_Ty2, optional>) // TRANSITION, GH-1674 + && three_way_comparable_with<_Ty1, _Ty2> +_NODISCARD constexpr compare_three_way_result_t<_Ty1, _Ty2> + operator<=>(const optional<_Ty1>& _Left, const _Ty2& _Right) { + // clang-format on + if (_Left) { + return *_Left <=> _Right; + } + + return strong_ordering::less; +} +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE swap [optional.specalg] template && is_swappable_v<_Ty>, int> = 0> void swap(optional<_Ty>& _Left, optional<_Ty>& _Right) noexcept(noexcept(_Left.swap(_Right))) { diff --git a/stl/inc/queue b/stl/inc/queue index 8954046397d..bcd510658e4 100644 --- a/stl/inc/queue +++ b/stl/inc/queue @@ -54,6 +54,14 @@ _NODISCARD bool operator>=(const queue<_Ty, _Container>& _Left, const queue<_Ty, return _Left.c >= _Right.c; } +#ifdef __cpp_lib_concepts +template +_NODISCARD compare_three_way_result_t<_Container> operator<=>( + const queue<_Ty, _Container>& _Left, const queue<_Ty, _Container>& _Right) { + return _Left.c <=> _Right.c; +} +#endif // __cpp_lib_concepts + template class queue { public: @@ -150,6 +158,12 @@ public: friend bool operator>= <>(const queue&, const queue&); // clang-format on +#ifdef __cpp_lib_concepts + template + friend compare_three_way_result_t<_Container2> operator<=>( + const queue<_Ty2, _Container2>&, const queue<_Ty2, _Container2>&); +#endif // __cpp_lib_concepts + protected: _Container c{}; }; diff --git a/stl/inc/ranges b/stl/inc/ranges index 1720c95babd..367648ffe80 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -13,6 +13,7 @@ #else // ^^^ !defined(__cpp_lib_concepts) / defined(__cpp_lib_concepts) vvv #include #include +#include #include #include @@ -534,6 +535,331 @@ namespace ranges { inline constexpr _Single_fn single; } // namespace views + // CLASS TEMPLATE ranges::iota_view + template + using _Iota_diff_t = conditional_t, conditional_t<(sizeof(_Ty) < sizeof(int)), int, long long>, + iter_difference_t<_Ty>>; + + // clang-format off + template + concept _Decrementable = incrementable<_Ty> && requires(_Ty __t) { + { --__t } -> same_as<_Ty&>; + { __t-- } -> same_as<_Ty>; + }; + + template + concept _Advanceable = _Decrementable<_Ty> && totally_ordered<_Ty> + && requires(_Ty __i, const _Ty __j, const _Iota_diff_t<_Ty> __n) { + { __i += __n } -> same_as<_Ty&>; + { __i -= __n } -> same_as<_Ty&>; + _Ty(__j + __n); + _Ty(__n + __j); + _Ty(__j - __n); + { __j - __j } -> convertible_to<_Iota_diff_t<_Ty>>; + }; + + template + requires semiregular<_Wi> + struct _Ioterator { + // clang-format on + /* [[no_unique_address]] */ _Wi _Current{}; + + using iterator_concept = conditional_t<_Advanceable<_Wi>, random_access_iterator_tag, + conditional_t<_Decrementable<_Wi>, bidirectional_iterator_tag, + conditional_t, forward_iterator_tag, input_iterator_tag>>>; + using iterator_category = input_iterator_tag; + using value_type = _Wi; + using difference_type = _Iota_diff_t<_Wi>; + + _NODISCARD constexpr _Wi operator*() const noexcept(is_nothrow_copy_constructible_v<_Wi>) { + return _Current; + } + + constexpr _Ioterator& operator++() noexcept(noexcept(++_Current)) /* strengthened */ { + ++_Current; + return *this; + } + + constexpr auto operator++(int) noexcept( + noexcept(++_Current) && (!incrementable<_Wi> || is_nothrow_copy_constructible_v<_Wi>) ) /* strengthened */ { + if constexpr (incrementable<_Wi>) { + auto _Tmp = *this; + ++_Current; + return _Tmp; + } else { + ++_Current; + } + } + + constexpr _Ioterator& operator--() noexcept( + noexcept(--_Current)) /* strengthened */ requires _Decrementable<_Wi> { + --_Current; + return *this; + } + + constexpr _Ioterator operator--(int) noexcept(is_nothrow_copy_constructible_v<_Wi>&& noexcept( + --_Current)) /* strengthened */ requires _Decrementable<_Wi> { + auto _Tmp = *this; + --_Current; + return _Tmp; + } + +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1347136 + private: + template + static constexpr bool _Nothrow_plus_equal = noexcept(_STD declval<_Left&>() += _STD declval()); + template <_Integer_like _Left, class _Right> + static constexpr bool _Nothrow_plus_equal<_Left, _Right> = true; + + template + static constexpr bool _Nothrow_minus_equal = noexcept(_STD declval<_Left&>() -= _STD declval()); + template <_Integer_like _Left, class _Right> + static constexpr bool _Nothrow_minus_equal<_Left, _Right> = true; + + public: +#endif // TRANSITION, DevCom-1347136 + + constexpr _Ioterator& operator+=(const difference_type _Off) +#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-1347136 + noexcept(noexcept(_Current += _Off)) /* strengthened */ +#else // ^^^ no workaround / workaround vvv + noexcept(_Nothrow_plus_equal<_Wi, difference_type>) /* strengthened */ +#endif // TRANSITION, DevCom-1347136 + requires _Advanceable<_Wi> { + if constexpr (_Integer_like<_Wi>) { + if constexpr (_Signed_integer_like<_Wi>) { + _Current = static_cast<_Wi>(_Current + _Off); + } else { + if (_Off >= difference_type{0}) { + _Current += static_cast<_Wi>(_Off); + } else { + _Current -= static_cast<_Wi>(-_Off); + } + } + } else { + _Current += _Off; + } + return *this; + } + + constexpr _Ioterator& operator-=(const difference_type _Off) +#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-1347136 + noexcept(noexcept(_Current -= _Off)) /* strengthened */ +#else // ^^^ no workaround / workaround vvv + noexcept(_Nothrow_minus_equal<_Wi, difference_type>) /* strengthened */ +#endif // TRANSITION, DevCom-1347136 + requires _Advanceable<_Wi> { + if constexpr (_Integer_like<_Wi>) { + if constexpr (_Signed_integer_like<_Wi>) { + _Current = static_cast<_Wi>(_Current - _Off); + } else { + if (_Off >= difference_type{0}) { + _Current -= static_cast<_Wi>(_Off); + } else { + _Current += static_cast<_Wi>(-_Off); + } + } + } else { + _Current -= _Off; + } + return *this; + } + + _NODISCARD constexpr _Wi operator[](const difference_type _Idx) const + noexcept(noexcept(static_cast<_Wi>(_Current + _Idx))) /* strengthened */ requires _Advanceable<_Wi> { + if constexpr (_Integer_like<_Wi>) { + return static_cast<_Wi>(_Current + static_cast<_Wi>(_Idx)); + } else { + return static_cast<_Wi>(_Current + _Idx); + } + } + + _NODISCARD friend constexpr bool operator==( + const _Ioterator&, const _Ioterator&) requires equality_comparable<_Wi> = default; + _NODISCARD friend constexpr bool operator<(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( + noexcept(_Left._Current < _Right._Current)) /* strengthened */ requires totally_ordered<_Wi> { + return _Left._Current < _Right._Current; + } + _NODISCARD friend constexpr bool operator>(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( + noexcept(_Right._Current < _Left._Current)) /* strengthened */ requires totally_ordered<_Wi> { + return _Right._Current < _Left._Current; + } + _NODISCARD friend constexpr bool operator<=(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( + noexcept(!(_Right._Current < _Left._Current))) /* strengthened */ requires totally_ordered<_Wi> { + return !(_Right._Current < _Left._Current); + } + _NODISCARD friend constexpr bool operator>=(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( + noexcept(!(_Left._Current < _Right._Current))) /* strengthened */ requires totally_ordered<_Wi> { + return !(_Left._Current < _Right._Current); + } + // clang-format off + _NODISCARD friend constexpr auto operator<=>(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( + noexcept(_Left._Current <=> _Right._Current)) /* strengthened */ + requires totally_ordered<_Wi> && three_way_comparable<_Wi> { + // clang-format on + return _Left._Current <=> _Right._Current; + } + + _NODISCARD friend constexpr _Ioterator operator+(_Ioterator _It, const difference_type _Off) noexcept( + noexcept(static_cast<_Wi>(_It._Current + _Off))) /* strengthened */ requires _Advanceable<_Wi> { + return _Ioterator{static_cast<_Wi>(_It._Current + _Off)}; + } + _NODISCARD friend constexpr _Ioterator operator+(const difference_type _Off, _Ioterator _It) noexcept( + noexcept(static_cast<_Wi>(_It._Current + _Off))) /* strengthened */ requires _Advanceable<_Wi> { + return _Ioterator{static_cast<_Wi>(_It._Current + _Off)}; + } + _NODISCARD friend constexpr _Ioterator operator-(_Ioterator _It, const difference_type _Off) noexcept( + noexcept(static_cast<_Wi>(_It._Current - _Off))) /* strengthened */ requires _Advanceable<_Wi> { + return _Ioterator{static_cast<_Wi>(_It._Current - _Off)}; + } + _NODISCARD friend constexpr difference_type + operator-(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( + noexcept(_Left._Current - _Right._Current)) /* strengthened */ requires _Advanceable<_Wi> { + return static_cast(_Left._Current - _Right._Current); + } + }; + + // clang-format off + template + requires _Weakly_equality_comparable_with<_Wi, _Bo> && semiregular<_Wi> + struct _Iotinel { + // clang-format on + private: + using _It = _Ioterator<_Wi>; + + _NODISCARD constexpr bool _Equal(const _It& _That) const noexcept(noexcept(_That._Current == _Last)) { + return _That._Current == _Last; + } + + _NODISCARD constexpr iter_difference_t<_Wi> _Delta(const _It& _That) const + noexcept(noexcept(_Last - _That._Current)) { + _STL_INTERNAL_STATIC_ASSERT(sized_sentinel_for<_Bo, _Wi>); + return _Last - _That._Current; + } + + public: + /* [[no_unique_address]] */ _Bo _Last{}; + + _NODISCARD friend constexpr bool operator==(const _It& _Left, const _Iotinel& _Right) noexcept( + noexcept(_Right._Equal(_Left))) /* strengthened */ { + return _Right._Equal(_Left); + } + + _NODISCARD friend constexpr iter_difference_t<_Wi> operator-(const _It& _Left, const _Iotinel& _Right) noexcept( + noexcept(_Right._Delta(_Left))) /* strengthened */ requires sized_sentinel_for<_Bo, _Wi> { + return -_Right._Delta(_Left); + } + + _NODISCARD friend constexpr iter_difference_t<_Wi> operator-(const _Iotinel& _Left, const _It& _Right) noexcept( + noexcept(_Left._Delta(_Right))) /* strengthened */ requires sized_sentinel_for<_Bo, _Wi> { + return _Left._Delta(_Right); + } + }; + + // clang-format off + template + requires _Weakly_equality_comparable_with<_Wi, _Bo> && semiregular<_Wi> + class iota_view : public view_interface> { + // clang-format on + private: + /* [[no_unique_address]] */ _Wi _Value{}; + /* [[no_unique_address]] */ _Bo _Bound{}; + + using _It = _Ioterator<_Wi>; + using _Se = conditional_t, _It, + conditional_t, _Bo, _Iotinel<_Wi, _Bo>>>; + + _NODISCARD static constexpr _Bo& _Bound_from(_Se& _Last) noexcept { + if constexpr (same_as<_Wi, _Bo>) { + return _Last._Current; + } else if constexpr (same_as<_Bo, unreachable_sentinel_t>) { + return _Last; + } else { + return _Last._Last; + } + } + + public: + iota_view() = default; + + constexpr explicit iota_view(_Wi _Value_) noexcept( + is_nothrow_move_constructible_v<_Wi>&& is_nothrow_default_constructible_v<_Bo>) // strengthened + : _Value(_STD move(_Value_)) {} + + constexpr iota_view(type_identity_t<_Wi> _Value_, type_identity_t<_Bo> _Bound_) noexcept( + is_nothrow_move_constructible_v<_Wi>&& is_nothrow_move_constructible_v<_Bo>) // strengthened + : _Value(_STD move(_Value_)), _Bound(_STD move(_Bound_)) { + if constexpr (totally_ordered_with<_Wi, _Bo>) { + _STL_ASSERT(_Value_ <= _Bound_, "Per N4878 [range.iota.view]/8, the first argument must precede the " + "second when their types are totally ordered."); + } + } + + constexpr iota_view(_It _First, _Se _Last) noexcept( // Per LWG-3523 + is_nothrow_move_constructible_v<_Wi>&& is_nothrow_move_constructible_v<_Bo>) // strengthened + : _Value(_STD move(_First._Current)), _Bound(_STD move(_Bound_from(_Last))) {} + + _NODISCARD constexpr _It begin() const noexcept(is_nothrow_copy_constructible_v<_Wi>) /* strengthened */ { + return _It{_Value}; + } + + _NODISCARD constexpr _Se end() const noexcept(is_nothrow_copy_constructible_v<_Bo>) /* strengthened */ { + if constexpr (same_as<_Bo, unreachable_sentinel_t>) { + return unreachable_sentinel; + } else { + return _Se{_Bound}; + } + } + + // clang-format off + _NODISCARD constexpr auto size() const noexcept(noexcept(_Bound - _Value)) /* strengthened */ + requires (same_as<_Wi, _Bo> && _Advanceable<_Wi>) + || (integral<_Wi> && integral<_Bo>) + || sized_sentinel_for<_Bo, _Wi> { + // clang-format on + if constexpr (_Integer_like<_Wi> && _Integer_like<_Bo>) { +#pragma warning(suppress : 4146) // unary minus operator applied to unsigned type, result still unsigned + return (_Value < 0) ? ((_Bound < 0) ? (_To_unsigned_like(-_Value) - _To_unsigned_like(-_Bound)) +#pragma warning(suppress : 4146) // unary minus operator applied to unsigned type, result still unsigned + : (_To_unsigned_like(_Bound) + _To_unsigned_like(-_Value))) + : (_To_unsigned_like(_Bound) - _To_unsigned_like(_Value)); + } else { + return _To_unsigned_like(_Bound - _Value); + } + } + }; + + // clang-format off + template + requires (!_Integer_like<_Wi> || !_Integer_like<_Bo> + || (_Signed_integer_like<_Wi> == _Signed_integer_like<_Bo>)) + iota_view(_Wi, _Bo) -> iota_view<_Wi, _Bo>; + // clang-format on + + template + inline constexpr bool enable_borrowed_range> = true; + + namespace views { + // VARIABLE views::iota + struct _Iota_fn { + template + _NODISCARD constexpr auto operator()(_Ty&& _Val) const + noexcept(noexcept(iota_view{static_cast<_Ty&&>(_Val)})) requires requires { + iota_view{static_cast<_Ty&&>(_Val)}; + } + { return iota_view{static_cast<_Ty&&>(_Val)}; } + + template + _NODISCARD constexpr auto operator()(_Ty1&& _Val1, _Ty2&& _Val2) const noexcept( + noexcept(iota_view{static_cast<_Ty1&&>(_Val1), static_cast<_Ty2&&>(_Val2)})) requires requires { + iota_view{static_cast<_Ty1&&>(_Val1), static_cast<_Ty2&&>(_Val2)}; + } + { return iota_view{static_cast<_Ty1&&>(_Val1), static_cast<_Ty2&&>(_Val2)}; } + }; + + inline constexpr _Iota_fn iota; + } // namespace views + // CLASS TEMPLATE ranges::istream_view template concept _Stream_extractable = requires(basic_istream<_Elem, _Traits>& __is, _Ty& __t) { @@ -1548,7 +1874,7 @@ namespace ranges { } // clang-format off - template + template requires sentinel_for<_Base_sentinel, _Base_iterator<_OtherConst>> _NODISCARD friend constexpr bool operator==( const _Counted_iter<_OtherConst>& _Left, const _Sentinel& _Right) { @@ -1666,25 +1992,25 @@ namespace ranges { namespace views { // VARIABLE views::take template - static constexpr bool _Is_dynamic_span = false; - template - static constexpr bool _Is_dynamic_span> = true; - - template - static constexpr bool _Is_subrange = false; + inline constexpr bool _Is_subrange = false; template - static constexpr bool _Is_subrange> = true; + inline constexpr bool _Is_subrange> = true; + // clang-format off template - concept _Reconstructible_range = random_access_range<_Rng> && sized_range<_Rng> - && (_Is_dynamic_span> - || _Is_specialization_v, basic_string_view> - // || _Is_specialization_v, iota_view> // TRANSITION, iota_view - || _Is_subrange>); + concept _Random_sized_range = random_access_range<_Rng> && sized_range<_Rng>; + // clang-format on class _Take_fn { private: - enum class _St { _Empty, _Preserve, _Take_view }; + enum class _St { + _Empty, + _Reconstruct_span, + _Reconstruct_string_view, + _Reconstruct_iota_view, + _Reconstruct_subrange, + _Take_view + }; template _NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept { @@ -1692,9 +2018,16 @@ namespace ranges { if constexpr (_Is_specialization_v<_Ty, empty_view>) { return {_St::_Empty, true}; - } else if constexpr (_Reconstructible_range<_Rng>) { - return {_St::_Preserve, - noexcept(_Ty{_RANGES begin(_STD declval<_Rng&>()), + } else if constexpr (_Is_span_v<_Ty>) { + return {_St::_Reconstruct_span, true}; + } else if constexpr (_Is_specialization_v<_Ty, basic_string_view>) { + return {_St::_Reconstruct_string_view, true}; + } else if constexpr (_Random_sized_range<_Ty> && _Is_specialization_v<_Ty, iota_view>) { + return {_St::_Reconstruct_iota_view, + noexcept(_RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>()))}; + } else if constexpr (_Random_sized_range<_Ty> && _Is_subrange<_Ty>) { + return {_St::_Reconstruct_subrange, + noexcept(subrange{_RANGES begin(_STD declval<_Rng&>()), _RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>())})}; } else { return {_St::_Take_view, noexcept(take_view{_STD declval<_Rng>(), range_difference_t<_Rng>{0}})}; @@ -1730,13 +2063,26 @@ namespace ranges { if constexpr (_Strat == _St::_Empty) { // it's an empty_view: return another empty view return remove_cvref_t<_Rng>{}; - } else if constexpr (_Strat == _St::_Preserve) { + } else if constexpr (_Strat == _St::_Take_view) { + return take_view{_STD forward<_Rng>(_Range), _Count}; + } else { // it's a "reconstructible range"; return the same kind of range with a restricted extent _Count = (_STD min)(_RANGES distance(_Range), _Count); const auto _First = _RANGES begin(_Range); - return remove_cvref_t<_Rng>{_First, _First + _Count}; - } else if constexpr (_Strat == _St::_Take_view) { - return take_view{_STD forward<_Rng>(_Range), _Count}; + + // The following are all per the proposed resolution of LWG-3407 + if constexpr (_Strat == _St::_Reconstruct_span) { + return span{_First, _First + _Count}; + } else if constexpr (_Strat == _St::_Reconstruct_string_view) { + return remove_cvref_t<_Rng>{_First, _First + _Count}; + } else if constexpr (_Strat == _St::_Reconstruct_iota_view) { + using _Vt = range_value_t<_Rng>; + return iota_view<_Vt, _Vt>{_First, _First + _Count}; + } else if constexpr (_Strat == _St::_Reconstruct_subrange) { + return subrange{_First, _First + _Count}; + } else { + static_assert(_Always_false<_Rng>, "Should be unreachable"); + } } } @@ -2053,7 +2399,7 @@ namespace ranges { // VARIABLE views::drop class _Drop_fn { private: - enum class _St { _Empty, _Preserve, _Drop_view }; + enum class _St { _Empty, _Reconstruct_span, _Reconstruct_subrange, _Reconstruct_other, _Drop_view }; template _NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept { @@ -2061,8 +2407,22 @@ namespace ranges { if constexpr (_Is_specialization_v<_Ty, empty_view>) { return {_St::_Empty, true}; - } else if constexpr (_Reconstructible_range<_Rng>) { - return {_St::_Preserve, + } else if constexpr (_Is_span_v<_Ty>) { + return {_St::_Reconstruct_span, true}; + } else if constexpr (_Is_specialization_v<_Ty, basic_string_view>) { + return {_St::_Reconstruct_other, true}; + } else if constexpr (_Random_sized_range<_Ty> && _Is_subrange<_Ty>) { + if constexpr (sized_sentinel_for, iterator_t<_Ty>>) { + return {_St::_Reconstruct_subrange, + noexcept(_Ty{_RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>()), + _RANGES end(_STD declval<_Rng&>())})}; + } else { + return {_St::_Reconstruct_subrange, + noexcept(_Ty{_RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>()), + _RANGES end(_STD declval<_Rng&>()), range_difference_t<_Rng>{0}})}; + } + } else if constexpr (_Random_sized_range<_Ty> && _Is_specialization_v<_Ty, iota_view>) { + return {_St::_Reconstruct_other, noexcept(_Ty{_RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>()), _RANGES end(_STD declval<_Rng&>())})}; } else { @@ -2099,12 +2459,27 @@ namespace ranges { if constexpr (_Strat == _St::_Empty) { // it's an empty_view: return another empty view return remove_cvref_t<_Rng>{}; - } else if constexpr (_Strat == _St::_Preserve) { - // it's a "reconstructible range"; return the same kind of range with a restricted extent - _Count = (_STD min)(_RANGES distance(_Range), _Count); - return remove_cvref_t<_Rng>{_RANGES begin(_Range) + _Count, _RANGES end(_Range)}; } else if constexpr (_Strat == _St::_Drop_view) { return drop_view{_STD forward<_Rng>(_Range), _Count}; + } else { + // it's a "reconstructible range"; return the same kind of range with a restricted extent + _Count = (_STD min)(_RANGES distance(_Range), _Count); + + // The following are all per the proposed resolution of LWG-3407 + if constexpr (_Strat == _St::_Reconstruct_span) { + return span{_Ubegin(_Range) + _Count, _Uend(_Range)}; + } else if constexpr (_Strat == _St::_Reconstruct_subrange) { + if constexpr (sized_sentinel_for, iterator_t<_Rng>>) { + return remove_cvref_t<_Rng>{_RANGES begin(_Range) + _Count, _RANGES end(_Range)}; + } else { + return remove_cvref_t<_Rng>{ + _RANGES begin(_Range) + _Count, _RANGES end(_Range), _RANGES size(_Range) - _Count}; + } + } else if constexpr (_Strat == _St::_Reconstruct_other) { + return remove_cvref_t<_Rng>{_RANGES begin(_Range) + _Count, _RANGES end(_Range)}; + } else { + static_assert(_Always_false<_Rng>, "Should be unreachable"); + } } } diff --git a/stl/inc/regex b/stl/inc/regex index 330944b8a28..2f2949e2683 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -593,6 +593,18 @@ bool _Is_word(_Elem _Ch) { return _UCh <= static_cast<_UElem>('z') && _Is_word(static_cast(_UCh)); } +#if _HAS_CXX20 +template +struct _Get_member_comparison_category { + using type = weak_ordering; +}; + +template +struct _Get_member_comparison_category<_Ty, void_t> { + using type = typename _Ty::comparison_category; +}; +#endif // _HAS_CXX20 + // CLASS TEMPLATE sub_match template class sub_match : public pair<_BidIt, _BidIt> { // class to hold contents of a capture group @@ -607,6 +619,10 @@ public: // Note that _Size_type should always be std::size_t using _Size_type = typename string_type::size_type; +#if _HAS_CXX20 + using _Comparison_category = typename _Get_member_comparison_category<_Traits>::type; +#endif // _HAS_CXX20 + constexpr sub_match() : _Mybase(), matched(false) {} bool matched; @@ -709,6 +725,12 @@ _NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const sub_match<_BidI return _Left._Match_equal(_Right); } +#if _HAS_CXX20 +template +_NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { + return static_cast::_Comparison_category>(_Left.compare(_Right) <=> 0); +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv template _NODISCARD bool operator!=(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { return !(_Left == _Right); @@ -733,9 +755,21 @@ template _NODISCARD bool operator>=(const sub_match<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { return !(_Left < _Right); } +#endif // !_HAS_CXX20 // COMPARE sub_match AND NTBS template +_NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { + return _Left._Match_equal(_Right); +} + +#if _HAS_CXX20 +template +_NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { + return static_cast::_Comparison_category>(_Left.compare(_Right) <=> 0); +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +template _NODISCARD bool operator==(const _Iter_value_t<_BidIt>* _Left, const sub_match<_BidIt>& _Right) { return _Right._Match_equal(_Left); } @@ -765,11 +799,6 @@ _NODISCARD bool operator>=(const _Iter_value_t<_BidIt>* _Left, const sub_match<_ return !(_Left < _Right); } -template -_NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { - return _Left._Match_equal(_Right); -} - template _NODISCARD bool operator!=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { return !(_Left == _Right); @@ -794,9 +823,22 @@ template _NODISCARD bool operator>=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>* _Right) { return !(_Left < _Right); } +#endif // !_HAS_CXX20 // COMPARE sub_match AND ELEMENT template +_NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { + return _Left._Match_equal(_STD addressof(_Right), 1); +} + +#if _HAS_CXX20 +template +_NODISCARD auto operator<=>(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { + return static_cast::_Comparison_category>( + _Left._Compare(_STD addressof(_Right), 1) <=> 0); +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +template _NODISCARD bool operator==(const _Iter_value_t<_BidIt>& _Left, const sub_match<_BidIt>& _Right) { return _Right._Match_equal(_STD addressof(_Left), 1); } @@ -826,11 +868,6 @@ _NODISCARD bool operator>=(const _Iter_value_t<_BidIt>& _Left, const sub_match<_ return !(_Left < _Right); } -template -_NODISCARD bool operator==(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { - return _Left._Match_equal(_STD addressof(_Right), 1); -} - template _NODISCARD bool operator!=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { return !(_Left == _Right); @@ -855,6 +892,7 @@ template _NODISCARD bool operator>=(const sub_match<_BidIt>& _Left, const _Iter_value_t<_BidIt>& _Right) { return !(_Left < _Right); } +#endif // !_HAS_CXX20 // COMPARE sub_match AND string template @@ -863,71 +901,79 @@ _NODISCARD bool operator==( return _Left._Match_equal(_Right.data(), _Right.size()); } +#if _HAS_CXX20 template -_NODISCARD bool operator!=( +_NODISCARD auto operator<=>( const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { + return static_cast::_Comparison_category>(_Left.compare(_Right) <=> 0); +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +template +_NODISCARD bool operator==( + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + return _Right._Match_equal(_Left.data(), _Left.size()); +} + +template +_NODISCARD bool operator!=( + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { return !(_Left == _Right); } template _NODISCARD bool operator<( - const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { - return _Left._Less(_Right.data(), _Right.size()); + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + return _Right._Greater(_Left.data(), _Left.size()); } template _NODISCARD bool operator>( - const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { return _Right < _Left; } template _NODISCARD bool operator<=( - const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { + const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { return !(_Right < _Left); } template _NODISCARD bool operator>=( - const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { - return !(_Left < _Right); -} - -template -_NODISCARD bool operator==( const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { - return _Right._Match_equal(_Left.data(), _Left.size()); + return !(_Left < _Right); } template _NODISCARD bool operator!=( - const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { return !(_Left == _Right); } template _NODISCARD bool operator<( - const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { - return _Right._Greater(_Left.data(), _Left.size()); + const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { + return _Left._Less(_Right.data(), _Right.size()); } template _NODISCARD bool operator>( - const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { return _Right < _Left; } template _NODISCARD bool operator<=( - const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { return !(_Right < _Left); } template _NODISCARD bool operator>=( - const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Left, const sub_match<_BidIt>& _Right) { + const sub_match<_BidIt>& _Left, const basic_string<_Iter_value_t<_BidIt>, _Traits, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // !_HAS_CXX20 // INSERT sub_match IN STREAM template @@ -1135,10 +1181,12 @@ _NODISCARD bool operator==(const match_results<_BidIt, _Alloc>& _Left, const mat } } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const match_results<_BidIt, _Alloc>& _Left, const match_results<_BidIt, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // NFA PROPERTIES const unsigned int _BRE_MAX_GRP = 9U; @@ -2429,9 +2477,11 @@ public: && _MyVal._At(0) == _Right._MyVal._At(0); } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const regex_iterator& _Right) const { return !(*this == _Right); } +#endif // !_HAS_CXX20 _NODISCARD const value_type& operator*() const { #if _ITERATOR_DEBUG_LEVEL != 0 @@ -2612,9 +2662,11 @@ public: return *_Res == *_Right._Res && _Pos == _Right._Pos && _Subs == _Right._Subs; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const regex_token_iterator& _Right) const { return !(*this == _Right); } +#endif // !_HAS_CXX20 _NODISCARD const value_type& operator*() const { #if _ITERATOR_DEBUG_LEVEL != 0 diff --git a/stl/inc/scoped_allocator b/stl/inc/scoped_allocator index 76f95f24f36..e35dfaf2db3 100644 --- a/stl/inc/scoped_allocator +++ b/stl/inc/scoped_allocator @@ -230,7 +230,16 @@ public: template void construct(_Ty* _Ptr, _Types&&... _Args) { // construct with varying allocator styles +#if _HAS_CXX20 + _STD apply( + [_Ptr, this](auto&&... _New_args) { + _Scoped_outermost_traits::construct( + _Scoped_outermost(*this), _Ptr, _STD forward(_New_args)...); + }, + _STD uses_allocator_construction_args<_Ty>(inner_allocator(), _STD forward<_Types>(_Args)...)); +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _Uses_allocator_construct(_Ptr, _Scoped_outermost(*this), inner_allocator(), _STD forward<_Types>(_Args)...); +#endif // ^^^ !_HAS_CXX20 ^^^ } template @@ -248,23 +257,23 @@ scoped_allocator_adaptor(_Outer, _Inner...) -> scoped_allocator_adaptor<_Outer, template _NODISCARD bool operator==(const scoped_allocator_adaptor<_Outer1, _Inner1, _Inner...>& _Left, - const scoped_allocator_adaptor<_Outer2, _Inner1, _Inner...>& - _Right) noexcept { // compare scoped_allocator_adaptors for equality + const scoped_allocator_adaptor<_Outer2, _Inner1, _Inner...>& _Right) noexcept { return _Left.outer_allocator() == _Right.outer_allocator() && _Left.inner_allocator() == _Right.inner_allocator(); } template -_NODISCARD bool operator==(const scoped_allocator_adaptor<_Outer1>& _Left, - const scoped_allocator_adaptor<_Outer2>& _Right) noexcept { // compare scoped_allocator_adaptors for equality +_NODISCARD bool operator==( + const scoped_allocator_adaptor<_Outer1>& _Left, const scoped_allocator_adaptor<_Outer2>& _Right) noexcept { return _Left.outer_allocator() == _Right.outer_allocator(); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const scoped_allocator_adaptor<_Outer1, _Inner...>& _Left, - const scoped_allocator_adaptor<_Outer2, _Inner...>& - _Right) noexcept { // compare scoped_allocator_adaptors for equality + const scoped_allocator_adaptor<_Outer2, _Inner...>& _Right) noexcept { return !(_Left == _Right); } +#endif // !_HAS_CXX20 _STD_END #pragma pop_macro("new") diff --git a/stl/inc/semaphore b/stl/inc/semaphore index 79d2760603b..903c4800bdd 100644 --- a/stl/inc/semaphore +++ b/stl/inc/semaphore @@ -177,6 +177,7 @@ public: template _NODISCARD bool try_acquire_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) { + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); ptrdiff_t _Current = _Counter.load(memory_order_relaxed); for (;;) { while (_Current == 0) { @@ -274,6 +275,7 @@ public: template _NODISCARD bool try_acquire_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) { + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); for (;;) { // "happens after release" ordering is provided by this exchange, so loads and waits can be relaxed // TRANSITION, GH-1133: should be memory_order_acquire diff --git a/stl/inc/set b/stl/inc/set index 409f0b7b22a..f45bd81991d 100644 --- a/stl/inc/set +++ b/stl/inc/set @@ -180,11 +180,21 @@ _NODISCARD bool operator==(const set<_Kty, _Pr, _Alloc>& _Left, const set<_Kty, && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const set<_Kty, _Pr, _Alloc>& _Left, const set<_Kty, _Pr, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Kty> operator<=>( + const set<_Kty, _Pr, _Alloc>& _Left, const set<_Kty, _Pr, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), + _Right._Unchecked_begin(), _Right._Unchecked_end_iter(), _Synth_three_way{}); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template _NODISCARD bool operator<(const set<_Kty, _Pr, _Alloc>& _Left, const set<_Kty, _Pr, _Alloc>& _Right) { return _STD lexicographical_compare( @@ -205,6 +215,7 @@ template _NODISCARD bool operator>=(const set<_Kty, _Pr, _Alloc>& _Left, const set<_Kty, _Pr, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ template void swap(set<_Kty, _Pr, _Alloc>& _Left, set<_Kty, _Pr, _Alloc>& _Right) noexcept(noexcept(_Left.swap(_Right))) { @@ -352,11 +363,21 @@ _NODISCARD bool operator==(const multiset<_Kty, _Pr, _Alloc>& _Left, const multi && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), _Right._Unchecked_begin()); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const multiset<_Kty, _Pr, _Alloc>& _Left, const multiset<_Kty, _Pr, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 +#ifdef __cpp_lib_concepts +template +_NODISCARD _Synth_three_way_result<_Kty> operator<=>( + const multiset<_Kty, _Pr, _Alloc>& _Left, const multiset<_Kty, _Pr, _Alloc>& _Right) { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end_iter(), + _Right._Unchecked_begin(), _Right._Unchecked_end_iter(), _Synth_three_way{}); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template _NODISCARD bool operator<(const multiset<_Kty, _Pr, _Alloc>& _Left, const multiset<_Kty, _Pr, _Alloc>& _Right) { return _STD lexicographical_compare( @@ -377,6 +398,7 @@ template _NODISCARD bool operator>=(const multiset<_Kty, _Pr, _Alloc>& _Left, const multiset<_Kty, _Pr, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ template void swap(multiset<_Kty, _Pr, _Alloc>& _Left, multiset<_Kty, _Pr, _Alloc>& _Right) noexcept( diff --git a/stl/inc/shared_mutex b/stl/inc/shared_mutex index 4509b0cb62b..a735ca0938c 100644 --- a/stl/inc/shared_mutex +++ b/stl/inc/shared_mutex @@ -114,8 +114,11 @@ public: } template - _NODISCARD bool try_lock_until( - const chrono::time_point<_Clock, _Duration>& _Abs_time) { // try to lock until time point + _NODISCARD bool try_lock_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) { + // try to lock until time point +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 auto _Not_writing = [this] { return !_Writing; }; auto _Zero_readers = [this] { return _Readers == 0; }; unique_lock _Lock(_Mymtx); @@ -166,8 +169,8 @@ public: } template - _NODISCARD bool try_lock_shared_for( - const chrono::duration<_Rep, _Period>& _Rel_time) { // try to lock non-exclusive for relative time + _NODISCARD bool try_lock_shared_for(const chrono::duration<_Rep, _Period>& _Rel_time) { + // try to lock non-exclusive for relative time return try_lock_shared_until(_To_absolute_time(_Rel_time)); } @@ -186,8 +189,11 @@ public: } template - _NODISCARD bool try_lock_shared_until( - const chrono::time_point<_Clock, _Duration>& _Abs_time) { // try to lock non-exclusive until absolute time + _NODISCARD bool try_lock_shared_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) { + // try to lock non-exclusive until absolute time +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 return _Try_lock_shared_until(_Abs_time); } @@ -257,6 +263,9 @@ public: _NODISCARD_CTOR shared_lock(mutex_type& _Mtx, const chrono::time_point<_Clock, _Duration>& _Abs_time) : _Pmtx(_STD addressof(_Mtx)), _Owns(_Mtx.try_lock_shared_until(_Abs_time)) { // construct with mutex and try to lock until absolute time +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 } ~shared_lock() noexcept { @@ -298,16 +307,19 @@ public: } template - _NODISCARD bool try_lock_for( - const chrono::duration<_Rep, _Period>& _Rel_time) { // try to lock the mutex for _Rel_time + _NODISCARD bool try_lock_for(const chrono::duration<_Rep, _Period>& _Rel_time) { + // try to lock the mutex for _Rel_time _Validate(); _Owns = _Pmtx->try_lock_shared_for(_Rel_time); return _Owns; } template - _NODISCARD bool try_lock_until( - const chrono::time_point<_Clock, _Duration>& _Abs_time) { // try to lock the mutex until _Abs_time + _NODISCARD bool try_lock_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) { + // try to lock the mutex until _Abs_time +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 _Validate(); _Owns = _Pmtx->try_lock_shared_until(_Abs_time); return _Owns; diff --git a/stl/inc/source_location b/stl/inc/source_location new file mode 100644 index 00000000000..a9e6154c44f --- /dev/null +++ b/stl/inc/source_location @@ -0,0 +1,66 @@ +// source_location standard header (core) + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef _SOURCE_LOCATION_ +#define _SOURCE_LOCATION_ +#include +#if _STL_COMPILER_PREPROCESSOR +#ifndef __cpp_consteval +#pragma message("The contents of are available only with C++20 consteval support.") +#else // ^^^ !defined(__cpp_consteval) / defined(__cpp_consteval) vvv + +#include + +#pragma pack(push, _CRT_PACKING) +#pragma warning(push, _STL_WARNING_LEVEL) +#pragma warning(disable : _STL_DISABLED_WARNINGS) +_STL_DISABLE_CLANG_WARNINGS +#pragma push_macro("new") +#undef new + +_STD_BEGIN +struct source_location { + _NODISCARD static consteval source_location current(const uint_least32_t _Line_ = __builtin_LINE(), + const uint_least32_t _Column_ = __builtin_COLUMN(), const char* const _File_ = __builtin_FILE(), + const char* const _Function_ = __builtin_FUNCTION()) noexcept { + source_location _Result; + _Result._Line = _Line_; + _Result._Column = _Column_; + _Result._File = _File_; + _Result._Function = _Function_; + return _Result; + } + + _NODISCARD_CTOR constexpr source_location() noexcept = default; + + _NODISCARD constexpr uint_least32_t line() const noexcept { + return _Line; + } + _NODISCARD constexpr uint_least32_t column() const noexcept { + return _Column; + } + _NODISCARD constexpr const char* file_name() const noexcept { + return _File; + } + _NODISCARD constexpr const char* function_name() const noexcept { + return _Function; + } + +private: + uint_least32_t _Line{}; + uint_least32_t _Column{}; + const char* _File = ""; + const char* _Function = ""; +}; +_STD_END + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) +#endif // !defined(__cpp_consteval) +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _SOURCE_LOCATION_ diff --git a/stl/inc/stack b/stl/inc/stack index 55889064e87..2cc4d964f56 100644 --- a/stl/inc/stack +++ b/stl/inc/stack @@ -52,6 +52,14 @@ _NODISCARD bool operator>=(const stack<_Ty, _Container>& _Left, const stack<_Ty, return _Left.c >= _Right.c; } +#ifdef __cpp_lib_concepts +template +_NODISCARD compare_three_way_result_t<_Container> operator<=>( + const stack<_Ty, _Container>& _Left, const stack<_Ty, _Container>& _Right) { + return _Left.c <=> _Right.c; +} +#endif // __cpp_lib_concepts + template class stack { public: @@ -140,6 +148,12 @@ public: friend bool operator>= <>(const stack&, const stack&); // clang-format on +#ifdef __cpp_lib_concepts + template + friend compare_three_way_result_t<_Container2> operator<=>( + const stack<_Ty2, _Container2>&, const stack<_Ty2, _Container2>&); +#endif // __cpp_lib_concepts + protected: _Container c{}; }; diff --git a/stl/inc/system_error b/stl/inc/system_error index 42b03990542..a9b527db251 100644 --- a/stl/inc/system_error +++ b/stl/inc/system_error @@ -16,7 +16,11 @@ #include #ifndef _M_CEE_PURE #include -#endif +#endif // _M_CEE_PURE + +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) @@ -90,13 +94,22 @@ public: return _Addr == _Right._Addr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const error_category& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 +// TRANSITION, GH-489 +#ifdef __cpp_lib_concepts + _NODISCARD strong_ordering operator<=>(const error_category& _Right) const noexcept { + return compare_three_way{}(_Addr, _Right._Addr); + } +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv _NODISCARD bool operator<(const error_category& _Right) const noexcept { return _Addr < _Right._Addr; } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ error_category(const error_category&) = delete; error_category& operator=(const error_category&) = delete; @@ -173,6 +186,21 @@ public: return _System_error_equal(_Left, _Right); } +// TRANSITION, GH-489 +#ifdef __cpp_lib_concepts + _NODISCARD friend strong_ordering operator<=>(const error_code& _Left, const error_code& _Right) noexcept { + if (const auto _Result = _Left.category() <=> _Right.category(); _Result != 0) { + return _Result; + } + return _Left.value() <=> _Right.value(); + } +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv + _NODISCARD friend bool operator<(const error_code& _Left, const error_code& _Right) noexcept { + return _Left.category() < _Right.category() + || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); + } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ +#if !_HAS_CXX20 _NODISCARD friend bool operator==(const error_condition& _Left, const error_code& _Right) noexcept { return _System_error_equal(_Right, _Left); } @@ -188,11 +216,7 @@ public: _NODISCARD friend bool operator!=(const error_condition& _Left, const error_code& _Right) noexcept { return !_System_error_equal(_Right, _Left); } - - _NODISCARD friend bool operator<(const error_code& _Left, const error_code& _Right) noexcept { - return _Left.category() < _Right.category() - || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); - } +#endif // !_HAS_CXX20 #endif // _STL_OPTIMIZE_SYSTEM_ERROR_OPERATORS private: @@ -249,22 +273,36 @@ public: return _Left.category() == _Right.category() && _Left.value() == _Right.value(); } - _NODISCARD friend bool operator!=(const error_condition& _Left, const error_condition& _Right) noexcept { - return !(_Left == _Right); +// TRANSITION, GH-489 +#ifdef __cpp_lib_concepts + _NODISCARD friend strong_ordering operator<=>( + const error_condition& _Left, const error_condition& _Right) noexcept { + if (const auto _Result = _Left.category() <=> _Right.category(); _Result != 0) { + return _Result; + } + return _Left.value() <=> _Right.value(); } - +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv _NODISCARD friend bool operator<(const error_condition& _Left, const error_condition& _Right) noexcept { return _Left.category() < _Right.category() || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ +#if !_HAS_CXX20 + _NODISCARD friend bool operator!=(const error_condition& _Left, const error_condition& _Right) noexcept { + return !(_Left == _Right); + } +#endif // !_HAS_CXX20 // We grant friendship to the operators from error_code here to allow is_error_code_enum_v but not // is_error_condition_enum_v enums to be compared directly with error_condition; for example: // io_errc::stream == make_error_condition(errc::out_of_memory) friend bool operator==(const error_code& _Left, const error_condition& _Right) noexcept; +#if !_HAS_CXX20 friend bool operator==(const error_condition& _Left, const error_code& _Right) noexcept; friend bool operator!=(const error_code& _Left, const error_condition& _Right) noexcept; friend bool operator!=(const error_condition& _Left, const error_code& _Right) noexcept; +#endif // !_HAS_CXX20 #endif // _STL_OPTIMIZE_SYSTEM_ERROR_OPERATORS private: @@ -285,14 +323,42 @@ _NODISCARD inline bool operator==(const error_code& _Left, const error_condition return _Left.category().equivalent(_Left.value(), _Right) || _Right.category().equivalent(_Left, _Right.value()); } -_NODISCARD inline bool operator==(const error_condition& _Left, const error_code& _Right) noexcept { - return _Right.category().equivalent(_Right.value(), _Left) || _Left.category().equivalent(_Right, _Left.value()); -} - _NODISCARD inline bool operator==(const error_condition& _Left, const error_condition& _Right) noexcept { return _Left.category() == _Right.category() && _Left.value() == _Right.value(); } +// TRANSITION, GH-489 +#ifdef __cpp_lib_concepts +_NODISCARD inline strong_ordering operator<=>(const error_code& _Left, const error_code& _Right) noexcept { + if (const auto _Result = _Left.category() <=> _Right.category(); _Result != 0) { + return _Result; + } + return _Left.value() <=> _Right.value(); +} + +_NODISCARD inline strong_ordering operator<=>(const error_condition& _Left, const error_condition& _Right) noexcept { + if (const auto _Result = _Left.category() <=> _Right.category(); _Result != 0) { + return _Result; + } + return _Left.value() <=> _Right.value(); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv +_NODISCARD inline bool operator<(const error_code& _Left, const error_code& _Right) noexcept { + return _Left.category() < _Right.category() + || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); +} + +_NODISCARD inline bool operator<(const error_condition& _Left, const error_condition& _Right) noexcept { + return _Left.category() < _Right.category() + || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); +} +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ + +#if !_HAS_CXX20 +_NODISCARD inline bool operator==(const error_condition& _Left, const error_code& _Right) noexcept { + return _Right.category().equivalent(_Right.value(), _Left) || _Left.category().equivalent(_Right, _Left.value()); +} + _NODISCARD inline bool operator!=(const error_code& _Left, const error_code& _Right) noexcept { return !(_Left == _Right); } @@ -308,16 +374,7 @@ _NODISCARD inline bool operator!=(const error_condition& _Left, const error_code _NODISCARD inline bool operator!=(const error_condition& _Left, const error_condition& _Right) noexcept { return !(_Left == _Right); } - -_NODISCARD inline bool operator<(const error_code& _Left, const error_code& _Right) noexcept { - return _Left.category() < _Right.category() - || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); -} - -_NODISCARD inline bool operator<(const error_condition& _Left, const error_condition& _Right) noexcept { - return _Left.category() < _Right.category() - || (_Left.category() == _Right.category() && _Left.value() < _Right.value()); -} +#endif // !_HAS_CXX20 #endif // _STL_OPTIMIZE_SYSTEM_ERROR_OPERATORS // VIRTUALS FOR error_category diff --git a/stl/inc/thread b/stl/inc/thread index 2d46b78936d..0d7be907f61 100644 --- a/stl/inc/thread +++ b/stl/inc/thread @@ -14,6 +14,7 @@ #include #include #if _HAS_CXX20 +#include #include #endif // _HAS_CXX20 @@ -185,6 +186,9 @@ namespace this_thread { template void sleep_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) { +#if _HAS_CXX20 + static_assert(chrono::is_clock_v<_Clock>, "Clock type required"); +#endif // _HAS_CXX20 for (;;) { const auto _Now = _Clock::now(); if (_Abs_time <= _Now) { @@ -215,7 +219,11 @@ private: friend thread::id thread::get_id() const noexcept; friend thread::id this_thread::get_id() noexcept; friend bool operator==(thread::id _Left, thread::id _Right) noexcept; +#if _HAS_CXX20 + friend strong_ordering operator<=>(thread::id _Left, thread::id _Right) noexcept; +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv friend bool operator<(thread::id _Left, thread::id _Right) noexcept; +#endif // !_HAS_CXX20 template friend basic_ostream<_Ch, _Tr>& operator<<(basic_ostream<_Ch, _Tr>& _Str, thread::id _Id); friend hash; @@ -237,6 +245,11 @@ _NODISCARD inline bool operator==(thread::id _Left, thread::id _Right) noexcept return _Left._Id == _Right._Id; } +#if _HAS_CXX20 +_NODISCARD inline strong_ordering operator<=>(thread::id _Left, thread::id _Right) noexcept { + return _Left._Id <=> _Right._Id; +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD inline bool operator!=(thread::id _Left, thread::id _Right) noexcept { return !(_Left == _Right); } @@ -256,6 +269,7 @@ _NODISCARD inline bool operator>(thread::id _Left, thread::id _Right) noexcept { _NODISCARD inline bool operator>=(thread::id _Left, thread::id _Right) noexcept { return !(_Left < _Right); } +#endif // !_HAS_CXX20 template basic_ostream<_Ch, _Tr>& operator<<(basic_ostream<_Ch, _Tr>& _Str, thread::id _Id) { diff --git a/stl/inc/tuple b/stl/inc/tuple index d0734801e84..388a8b237e9 100644 --- a/stl/inc/tuple +++ b/stl/inc/tuple @@ -8,6 +8,9 @@ #define _TUPLE_ #include #if _STL_COMPILER_PREPROCESSOR +#ifdef __cpp_lib_concepts +#include +#endif // __cpp_lib_concepts #include #include @@ -233,9 +236,15 @@ public: return true; } - constexpr bool _Less(const tuple&) const noexcept { +#ifdef __cpp_lib_concepts + _NODISCARD constexpr strong_ordering _Three_way_compare(const tuple&) const noexcept { + return strong_ordering::equal; + } +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv + _NODISCARD constexpr bool _Less(const tuple&) const noexcept { return false; } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ }; template @@ -673,11 +682,23 @@ public: return _Myfirst._Val == _Right._Myfirst._Val && _Mybase::_Equals(_Right._Get_rest()); } +#ifdef __cpp_lib_concepts + template , // TRANSITION, DevCom-1344701 + _Synth_three_way_result<_Rest, _Other>...>> // (should be a normal or trailing return type) + _NODISCARD constexpr _Ret _Three_way_compare(const tuple<_First, _Other...>& _Right) const { + if (auto _Result = _Synth_three_way{}(_Myfirst._Val, _Right._Myfirst._Val); _Result != 0) { + return _Result; + } + return _Mybase::_Three_way_compare(_Right._Get_rest()); + } +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template - constexpr bool _Less(const tuple<_Other...>& _Right) const { + _NODISCARD constexpr bool _Less(const tuple<_Other...>& _Right) const { return _Myfirst._Val < _Right._Myfirst._Val || (!(_Right._Myfirst._Val < _Myfirst._Val) && _Mybase::_Less(_Right._Get_rest())); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ template friend constexpr tuple_element_t<_Index, tuple<_Types...>>& get(tuple<_Types...>& _Tuple) noexcept; @@ -733,10 +754,20 @@ _NODISCARD constexpr bool operator==(const tuple<_Types1...>& _Left, const tuple return _Left._Equals(_Right); } +#ifdef __cpp_lib_concepts +template +_NODISCARD constexpr common_comparison_category_t<_Synth_three_way_result<_Types1, _Types2>...> operator<=>( + const tuple<_Types1...>& _Left, const tuple<_Types2...>& _Right) { + static_assert(sizeof...(_Types1) == sizeof...(_Types2), "cannot compare tuples of different sizes"); + return _Left._Three_way_compare(_Right); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv +#if !_HAS_CXX20 template _NODISCARD constexpr bool operator!=(const tuple<_Types1...>& _Left, const tuple<_Types2...>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 template _NODISCARD constexpr bool operator<(const tuple<_Types1...>& _Left, const tuple<_Types2...>& _Right) { @@ -758,6 +789,7 @@ template _NODISCARD constexpr bool operator<=(const tuple<_Types1...>& _Left, const tuple<_Types2...>& _Right) { return !(_Right < _Left); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ template ...>, int> = 0> _CONSTEXPR20 void swap(tuple<_Types...>& _Left, tuple<_Types...>& _Right) noexcept(noexcept(_Left.swap(_Right))) { diff --git a/stl/inc/type_traits b/stl/inc/type_traits index 2d50aa726cf..e1ef10aafc1 100644 --- a/stl/inc/type_traits +++ b/stl/inc/type_traits @@ -1809,57 +1809,28 @@ inline constexpr bool is_nothrow_invocable_r_v = #ifndef __clang__ // TRANSITION, LLVM-48860 // STRUCT TEMPLATE is_layout_compatible template -#ifdef _IS_LAYOUT_COMPATIBLE_SUPPORTED -struct is_layout_compatible : bool_constant<__is_layout_compatible(_Ty1, _Ty2)> { -#else // ^^^ _IS_LAYOUT_COMPATIBLE_SUPPORTED / !_IS_LAYOUT_COMPATIBLE_SUPPORTED vvv -struct is_layout_compatible : bool_constant<__builtin_is_layout_compatible(_Ty1, _Ty2)> { -#endif // _IS_LAYOUT_COMPATIBLE_SUPPORTED -}; +struct is_layout_compatible : bool_constant<__is_layout_compatible(_Ty1, _Ty2)> {}; template -#ifdef _IS_LAYOUT_COMPATIBLE_SUPPORTED inline constexpr bool is_layout_compatible_v = __is_layout_compatible(_Ty1, _Ty2); -#else // ^^^ _IS_LAYOUT_COMPATIBLE_SUPPORTED / !_IS_LAYOUT_COMPATIBLE_SUPPORTED vvv -inline constexpr bool is_layout_compatible_v = __builtin_is_layout_compatible(_Ty1, _Ty2); -#endif // _IS_LAYOUT_COMPATIBLE_SUPPORTED // STRUCT TEMPLATE is_pointer_interconvertible_base_of template -struct is_pointer_interconvertible_base_of -#ifdef _IS_LAYOUT_COMPATIBLE_SUPPORTED - : bool_constant<__is_pointer_interconvertible_base_of(_Base, _Derived)> { -}; -#else // ^^^ _IS_LAYOUT_COMPATIBLE_SUPPORTED / !_IS_LAYOUT_COMPATIBLE_SUPPORTED vvv - : bool_constant<__builtin_is_pointer_interconvertible_base_of(_Base, _Derived)> { -}; -#endif // _IS_LAYOUT_COMPATIBLE_SUPPORTED +struct is_pointer_interconvertible_base_of : bool_constant<__is_pointer_interconvertible_base_of(_Base, _Derived)> {}; template -#ifdef _IS_LAYOUT_COMPATIBLE_SUPPORTED inline constexpr bool is_pointer_interconvertible_base_of_v = __is_pointer_interconvertible_base_of(_Base, _Derived); -#else // ^^^ _IS_LAYOUT_COMPATIBLE_SUPPORTED / !_IS_LAYOUT_COMPATIBLE_SUPPORTED vvv -inline constexpr bool is_pointer_interconvertible_base_of_v = __builtin_is_pointer_interconvertible_base_of( - _Base, _Derived); -#endif // _IS_LAYOUT_COMPATIBLE_SUPPORTED // FUNCTION TEMPLATE is_pointer_interconvertible_with_class template _NODISCARD constexpr bool is_pointer_interconvertible_with_class(_MemberTy _ClassTy::*_Pm) noexcept { -#ifdef _IS_LAYOUT_COMPATIBLE_SUPPORTED return __is_pointer_interconvertible_with_class(_ClassTy, _Pm); -#else // ^^^ _IS_LAYOUT_COMPATIBLE_SUPPORTED / !_IS_LAYOUT_COMPATIBLE_SUPPORTED vvv - return __builtin_is_pointer_interconvertible_with_class(_ClassTy, _Pm); -#endif // _IS_LAYOUT_COMPATIBLE_SUPPORTED } // FUNCTION TEMPLATE is_corresponding_member template _NODISCARD constexpr bool is_corresponding_member(_MemberTy1 _ClassTy1::*_Pm1, _MemberTy2 _ClassTy2::*_Pm2) noexcept { -#ifdef _IS_LAYOUT_COMPATIBLE_SUPPORTED return __is_corresponding_member(_ClassTy1, _ClassTy2, _Pm1, _Pm2); -#else // ^^^ _IS_LAYOUT_COMPATIBLE_SUPPORTED / !_IS_LAYOUT_COMPATIBLE_SUPPORTED vvv - return __builtin_is_corresponding_member(_ClassTy1, _ClassTy2, _Pm1, _Pm2); -#endif // _IS_LAYOUT_COMPATIBLE_SUPPORTED } #endif // __clang__ #endif // __EDG__ diff --git a/stl/inc/typeindex b/stl/inc/typeindex index 0aa47537679..68d02e809f7 100644 --- a/stl/inc/typeindex +++ b/stl/inc/typeindex @@ -38,9 +38,17 @@ public: return *_Tptr == *_Right._Tptr; } +#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; + } +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD bool operator!=(const type_index& _Right) const noexcept { return !(*this == _Right); } +#endif // ^^^ !_HAS_CXX20 ^^^ _NODISCARD bool operator<(const type_index& _Right) const noexcept { return _Tptr->before(*_Right._Tptr); diff --git a/stl/inc/unordered_map b/stl/inc/unordered_map index 0a564aaba2e..c5633ff84a4 100644 --- a/stl/inc/unordered_map +++ b/stl/inc/unordered_map @@ -468,11 +468,13 @@ _NODISCARD bool operator==(const unordered_map<_Kty, _Ty, _Hasher, _Keyeq, _Allo return _Hash_equal(_Left, _Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const unordered_map<_Kty, _Ty, _Hasher, _Keyeq, _Alloc>& _Left, const unordered_map<_Kty, _Ty, _Hasher, _Keyeq, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // CLASS TEMPLATE unordered_multimap template , class _Keyeq = equal_to<_Kty>, @@ -758,11 +760,13 @@ _NODISCARD bool operator==(const unordered_multimap<_Kty, _Ty, _Hasher, _Keyeq, return _Hash_equal(_Left, _Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const unordered_multimap<_Kty, _Ty, _Hasher, _Keyeq, _Alloc>& _Left, const unordered_multimap<_Kty, _Ty, _Hasher, _Keyeq, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 #if _HAS_TR1_NAMESPACE namespace _DEPRECATE_TR1_NAMESPACE tr1 { diff --git a/stl/inc/unordered_set b/stl/inc/unordered_set index 1f32080e230..2a7125567b6 100644 --- a/stl/inc/unordered_set +++ b/stl/inc/unordered_set @@ -322,11 +322,13 @@ _NODISCARD bool operator==(const unordered_set<_Kty, _Hasher, _Keyeq, _Alloc>& _ return _Hash_equal(_Left, _Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const unordered_set<_Kty, _Hasher, _Keyeq, _Alloc>& _Left, const unordered_set<_Kty, _Hasher, _Keyeq, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // CLASS TEMPLATE unordered_multiset template , class _Keyeq = equal_to<_Kty>, class _Alloc = allocator<_Kty>> @@ -584,11 +586,13 @@ _NODISCARD bool operator==(const unordered_multiset<_Kty, _Hasher, _Keyeq, _Allo return _Hash_equal(_Left, _Right); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=(const unordered_multiset<_Kty, _Hasher, _Keyeq, _Alloc>& _Left, const unordered_multiset<_Kty, _Hasher, _Keyeq, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 #if _HAS_TR1_NAMESPACE namespace _DEPRECATE_TR1_NAMESPACE tr1 { diff --git a/stl/inc/utility b/stl/inc/utility index cc191b85356..a909684b495 100644 --- a/stl/inc/utility +++ b/stl/inc/utility @@ -347,10 +347,22 @@ _NODISCARD constexpr bool operator==(const pair<_Ty1, _Ty2>& _Left, const pair<_ return _Left.first == _Right.first && _Left.second == _Right.second; } +#ifdef __cpp_lib_concepts +template +_NODISCARD constexpr common_comparison_category_t<_Synth_three_way_result<_Ty1>, _Synth_three_way_result<_Ty2>> + operator<=>(const pair<_Ty1, _Ty2>& _Left, const pair<_Ty1, _Ty2>& _Right) { + if (auto _Result = _Synth_three_way{}(_Left.first, _Right.first); _Result != 0) { + return _Result; + } + return _Synth_three_way{}(_Left.second, _Right.second); +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv +#if !_HAS_CXX20 template _NODISCARD constexpr bool operator!=(const pair<_Ty1, _Ty2>& _Left, const pair<_Ty1, _Ty2>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 template _NODISCARD constexpr bool operator<(const pair<_Ty1, _Ty2>& _Left, const pair<_Ty1, _Ty2>& _Right) { @@ -371,6 +383,7 @@ template _NODISCARD constexpr bool operator>=(const pair<_Ty1, _Ty2>& _Left, const pair<_Ty1, _Ty2>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ // ALIAS TEMPLATE _Unrefwrap_t template diff --git a/stl/inc/valarray b/stl/inc/valarray index 63b69f34fa4..5f5a6ab33fc 100644 --- a/stl/inc/valarray +++ b/stl/inc/valarray @@ -1373,6 +1373,12 @@ public: return _Stride; } +#if _HAS_CXX20 + _NODISCARD friend bool operator==(const slice& _Left, const slice& _Right) noexcept /* strengthened */ { + return _Left.start() == _Right.start() && _Left.size() == _Right.size() && _Left.stride() == _Right.stride(); + } +#endif // _HAS_CXX20 + protected: size_t _Start = 0; // the starting offset size_t _Len = 0; // the number of elements diff --git a/stl/inc/variant b/stl/inc/variant index 339410bfd7d..036e1d602aa 100644 --- a/stl/inc/variant +++ b/stl/inc/variant @@ -1383,13 +1383,14 @@ _NODISCARD constexpr add_pointer_t get_if( } // RELATIONAL OPERATORS [variant.relops] -template -struct _Variant_relop_visitor { // evaluate _Op with the contained value of two variants that hold the same alternative +template +struct _Variant_relop_visitor2 { // evaluate _Op with the contained value of two variants that hold the same alternative const _Variant_storage<_Types...>& _Left; template - _NODISCARD constexpr bool operator()(_Tagged _Right) const noexcept( - disjunction_v, is_nothrow_invocable_r>) { + _NODISCARD constexpr _Result operator()(_Tagged _Right) const + noexcept(disjunction_v, + is_nothrow_invocable_r<_Result, _Op, const _Ty&, const _Ty&>>) { // determine the relationship between the stored values of _Left and _Right // pre: _Left.index() == _Idx && _Right.index() == _Idx if constexpr (_Idx != variant_npos) { @@ -1405,7 +1406,7 @@ template _NODISCARD constexpr bool operator==(const variant<_Types...>& _Left, const variant<_Types...>& _Right) noexcept( conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if the arguments are both valueless or contain equal values - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Right_index = _Right.index(); return _Left.index() == _Right_index && _Variant_raw_visit(_Right_index, _Right._Storage(), _Visitor{_Left._Storage()}); @@ -1415,7 +1416,7 @@ template _NODISCARD constexpr bool operator!=(const variant<_Types...>& _Left, const variant<_Types...>& _Right) noexcept( conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if the arguments have different active alternatives or contain unequal values - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Right_index = _Right.index(); return _Left.index() != _Right_index || _Variant_raw_visit(_Right_index, _Right._Storage(), _Visitor{_Left._Storage()}); @@ -1426,7 +1427,7 @@ _NODISCARD constexpr bool operator<(const variant<_Types...>& _Left, const varia conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if _Left has a lesser index(), or equal index() and lesser // contained value than _Right - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Left_offset = _Left.index() + 1; const size_t _Right_offset = _Right.index() + 1; return _Left_offset < _Right_offset @@ -1439,7 +1440,7 @@ _NODISCARD constexpr bool operator>(const variant<_Types...>& _Left, const varia conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if _Left has a greater index(), or equal index() and // greater contained value than _Right - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Left_offset = _Left.index() + 1; const size_t _Right_offset = _Right.index() + 1; return _Left_offset > _Right_offset @@ -1452,7 +1453,7 @@ _NODISCARD constexpr bool operator<=(const variant<_Types...>& _Left, const vari conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if _Left's index() is less than _Right's, or equal and // _Left contains a value less than or equal to _Right - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Left_offset = _Left.index() + 1; const size_t _Right_offset = _Right.index() + 1; return _Left_offset < _Right_offset @@ -1465,7 +1466,7 @@ _NODISCARD constexpr bool operator>=(const variant<_Types...>& _Left, const vari conjunction_v, const _Types&, const _Types&>...>) /* strengthened */ { // determine if _Left's index() is greater than _Right's, or equal and // _Left contains a value greater than or equal to _Right - using _Visitor = _Variant_relop_visitor, _Types...>; + using _Visitor = _Variant_relop_visitor2, bool, _Types...>; const size_t _Left_offset = _Left.index() + 1; const size_t _Right_offset = _Right.index() + 1; return _Left_offset > _Right_offset @@ -1473,6 +1474,27 @@ _NODISCARD constexpr bool operator>=(const variant<_Types...>& _Left, const vari && _Variant_raw_visit(_Right_offset - 1, _Right._Storage(), _Visitor{_Left._Storage()})); } +#ifdef __cpp_lib_concepts +// clang-format off +template + requires (three_way_comparable<_Types> && ...) +_NODISCARD constexpr common_comparison_category_t...> + operator<=>(const variant<_Types...>& _Left, const variant<_Types...>& _Right) noexcept( + conjunction_v...>, + compare_three_way, const _Types&, const _Types&>...>) /* strengthened */ { + // clang-format on + // determine the three-way comparison of _Left's and _Right's index, if equal + // return the three-way comparison of the contained values of _Left and _Right + using _Visitor = _Variant_relop_visitor2...>, _Types...>; + const size_t _Left_offset = _Left.index() + 1; + const size_t _Right_offset = _Right.index() + 1; + const auto _Offset_order = _Left_offset <=> _Right_offset; + return _Offset_order != 0 ? _Offset_order + : _Variant_raw_visit(_Right_offset - 1, _Right._Storage(), _Visitor{_Left._Storage()}); +} +#endif // __cpp_lib_concepts + // VISITATION [variant.visit] template inline constexpr size_t _Variant_total_states = @@ -1716,6 +1738,12 @@ struct monostate {}; _NODISCARD constexpr bool operator==(monostate, monostate) noexcept { return true; } + +#if _HAS_CXX20 +_NODISCARD constexpr strong_ordering operator<=>(monostate, monostate) noexcept { + return strong_ordering::equal; +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv _NODISCARD constexpr bool operator!=(monostate, monostate) noexcept { return false; } @@ -1731,6 +1759,7 @@ _NODISCARD constexpr bool operator<=(monostate, monostate) noexcept { _NODISCARD constexpr bool operator>=(monostate, monostate) noexcept { return true; } +#endif // !_HAS_CXX20 // SPECIALIZED ALGORITHMS [variant.specalg] template (const _Vector_const_iterator& _Right) const noexcept { + _Compat(_Right); + return _Unfancy(_Ptr) <=> _Unfancy(_Right._Ptr); + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv + _NODISCARD bool operator!=(const _Vector_const_iterator& _Right) const noexcept { return !(*this == _Right); } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator<(const _Vector_const_iterator& _Right) const noexcept { + _NODISCARD bool operator<(const _Vector_const_iterator& _Right) const noexcept { _Compat(_Right); return _Ptr < _Right._Ptr; } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator>(const _Vector_const_iterator& _Right) const noexcept { + _NODISCARD bool operator>(const _Vector_const_iterator& _Right) const noexcept { return _Right < *this; } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator<=(const _Vector_const_iterator& _Right) const noexcept { + _NODISCARD bool operator<=(const _Vector_const_iterator& _Right) const noexcept { return !(_Right < *this); } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator>=(const _Vector_const_iterator& _Right) const noexcept { + _NODISCARD bool operator>=(const _Vector_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 _CONSTEXPR20_CONTAINER void _Compat(const _Vector_const_iterator& _Right) const noexcept { // test for compatible iterator pair @@ -300,7 +309,8 @@ public: _NODISCARD _CONSTEXPR20_CONTAINER _Vector_iterator operator+(const difference_type _Off) const noexcept { _Vector_iterator _Tmp = *this; - return _Tmp += _Off; + _Tmp += _Off; // TRANSITION, LLVM-49342 + return _Tmp; } _CONSTEXPR20_CONTAINER _Vector_iterator& operator-=(const difference_type _Off) noexcept { @@ -312,7 +322,8 @@ public: _NODISCARD _CONSTEXPR20_CONTAINER _Vector_iterator operator-(const difference_type _Off) const noexcept { _Vector_iterator _Tmp = *this; - return _Tmp -= _Off; + _Tmp -= _Off; // TRANSITION, LLVM-49342 + return _Tmp; } _NODISCARD _CONSTEXPR20_CONTAINER reference operator[](const difference_type _Off) const noexcept { @@ -1830,26 +1841,126 @@ template >, vector(_Iter, _Iter, _Alloc = _Alloc()) -> vector<_Iter_value_t<_Iter>, _Alloc>; #endif // _HAS_CXX17 -template -_CONSTEXPR20_CONTAINER void swap(vector<_Ty, _Alloc>& _Left, vector<_Ty, _Alloc>& _Right) noexcept /* strengthened */ { - _Left.swap(_Right); -} +template +class vector; + +using _Vbase = unsigned int; // word type for vector representation +constexpr int _VBITS = 8 * sizeof(_Vbase); // at least CHAR_BITS bits per word template _NODISCARD _CONSTEXPR20_CONTAINER bool operator==(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { - return _Left.size() == _Right.size() - && _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); + if (_Left.size() != _Right.size()) { + return false; + } + + if constexpr (is_same_v<_Ty, bool>) { + return _STD equal( + _Left._Myvec._Unchecked_begin(), _Left._Myvec._Unchecked_end(), _Right._Myvec._Unchecked_begin()); + } else { + return _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); + } } +#if !_HAS_CXX20 template -_NODISCARD _CONSTEXPR20_CONTAINER bool operator!=(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { +_NODISCARD bool operator!=(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { return !(_Left == _Right); } +#endif // !_HAS_CXX20 + +// Optimize vector lexicographical comparisons. + +// There are several endianness/ordering issues to consider here. +// * Machine endianness is irrelevant. (That affects how an unsigned int is stored +// as a sequence of bytes. While all of our supported architectures are little-endian, +// that's irrelevant as long as we avoid reinterpreting unsigned int as a sequence of bytes.) +// * Appending bits to vector eventually appends words to its underlying storage. +// For example, vb[10] is stored within vb._Myvec[0], while vb[100] is stored within vb._Myvec[3]. +// This allows us to translate lexicographical comparisons from theoretical bits to physical words. +// * Unsigned integers are written and compared as big-endian (most significant bit first). +// For example, 0x10u > 0x07u. +// * However, vector packs bits into words as little-endian (least significant bit first). +// For example, vector{false, true, true, true} stores 0b0000'0000'0000'0000'0000'0000'0000'1110u. +// We could bit-reverse words before comparing, but we just need to find the least significant bit that differs. + +template +struct _Vbase_compare_three_way { + _NODISCARD constexpr _Ret operator()(const _Vbase _Left, const _Vbase _Right) const noexcept { + const _Vbase _Differing_bits = _Left ^ _Right; + + if (_Differing_bits == 0) { // improves _Countr_zero codegen below +#ifdef __cpp_lib_concepts + return strong_ordering::equal; +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv + return 0; +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ + } + + const int _Bit_index = _Countr_zero(_Differing_bits); // number of least significant bits that match + _STL_INTERNAL_CHECK(_Bit_index < _VBITS); // because we return early for equality + const _Vbase _Mask = _Vbase{1} << _Bit_index; // selects the least significant bit that differs + + // Instead of comparing (_Left & _Mask) to (_Right & _Mask), we know that exactly one side will be zero. +#ifdef __cpp_lib_concepts + return (_Left & _Mask) == 0 ? strong_ordering::less : strong_ordering::greater; +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv + return (_Left & _Mask) == 0 ? -1 : 1; +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ + } +}; + +#ifdef __cpp_lib_concepts +template +_NODISCARD _CONSTEXPR20_CONTAINER _Synth_three_way_result<_Ty> operator<=>( + const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { + if constexpr (is_same_v<_Ty, bool>) { + // This optimization works because vector "trims" its underlying storage by zeroing out unused bits. + const auto _Min_word_size = (_STD min)(_Left._Myvec.size(), _Right._Myvec.size()); + const auto _Left_words = _Left._Myvec._Unchecked_begin(); + const auto _Right_words = _Right._Myvec._Unchecked_begin(); + + using _Comp = _Vbase_compare_three_way; + + const strong_ordering _Word_comparison = _STD lexicographical_compare_three_way( + _Left_words, _Left_words + _Min_word_size, _Right_words, _Right_words + _Min_word_size, _Comp{}); + + if (_Word_comparison != 0) { + return _Word_comparison; + } + + return _Left.size() <=> _Right.size(); + } else { + return _STD lexicographical_compare_three_way(_Left._Unchecked_begin(), _Left._Unchecked_end(), + _Right._Unchecked_begin(), _Right._Unchecked_end(), _Synth_three_way{}); + } +} +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv template _NODISCARD _CONSTEXPR20_CONTAINER bool operator<(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { - return _STD lexicographical_compare( - _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()); + if constexpr (is_same_v<_Ty, bool>) { + // This optimization works because vector "trims" its underlying storage by zeroing out unused bits. + auto _First = _Left._Myvec._Unchecked_begin(); + auto _Other = _Right._Myvec._Unchecked_begin(); + + const auto _Last = _First + (_STD min)(_Left._Myvec.size(), _Right._Myvec.size()); + + for (; _First != _Last; ++_First, (void) ++_Other) { + using _Comp = _Vbase_compare_three_way; + const auto _Result = _Comp{}(*_First, *_Other); + + if (_Result < 0) { + return true; + } else if (_Result > 0) { + return false; + } + } + + return _Left.size() < _Right.size(); + } else { + return _STD lexicographical_compare( + _Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin(), _Right._Unchecked_end()); + } } template @@ -1866,11 +1977,26 @@ template _NODISCARD _CONSTEXPR20_CONTAINER bool operator>=(const vector<_Ty, _Alloc>& _Left, const vector<_Ty, _Alloc>& _Right) { return !(_Left < _Right); } +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ -// CLASS TEMPLATE vector AND FRIENDS -using _Vbase = unsigned int; // word type for vector representation -constexpr int _VBITS = 8 * sizeof(_Vbase); // at least CHAR_BITS bits per word +template +_CONSTEXPR20_CONTAINER void swap(vector<_Ty, _Alloc>& _Left, vector<_Ty, _Alloc>& _Right) noexcept /* strengthened */ { + _Left.swap(_Right); +} + +#if _HAS_CXX20 +template +_CONSTEXPR20_CONTAINER typename vector<_Ty, _Alloc>::size_type erase(vector<_Ty, _Alloc>& _Cont, const _Uty& _Val) { + return _Erase_remove(_Cont, _Val); +} +template +_CONSTEXPR20_CONTAINER typename vector<_Ty, _Alloc>::size_type erase_if(vector<_Ty, _Alloc>& _Cont, _Pr _Pred) { + return _Erase_remove_if(_Cont, _Pass_fn(_Pred)); +} +#endif // _HAS_CXX20 + +// CLASS TEMPLATE vector AND FRIENDS template struct _Wrap_alloc { // TRANSITION, ABI compat, preserves symbol names of vector::iterator using _Alloc = _Alloc0; @@ -2063,7 +2189,8 @@ public: _NODISCARD _CONSTEXPR20_CONTAINER _Vb_const_iterator operator+(const difference_type _Off) const noexcept { _Vb_const_iterator _Tmp = *this; - return _Tmp += _Off; + _Tmp += _Off; // TRANSITION, LLVM-49342 + return _Tmp; } _CONSTEXPR20_CONTAINER _Vb_const_iterator& operator-=(const difference_type _Off) noexcept { @@ -2072,7 +2199,8 @@ public: _NODISCARD _CONSTEXPR20_CONTAINER _Vb_const_iterator operator-(const difference_type _Off) const noexcept { _Vb_const_iterator _Tmp = *this; - return _Tmp -= _Off; + _Tmp -= _Off; // TRANSITION, LLVM-49342 + return _Tmp; } _NODISCARD _CONSTEXPR20_CONTAINER difference_type operator-(const _Vb_const_iterator& _Right) const noexcept { @@ -2090,26 +2218,36 @@ public: return this->_Myptr == _Right._Myptr && this->_Myoff == _Right._Myoff; } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator!=(const _Vb_const_iterator& _Right) const noexcept { +#if _HAS_CXX20 + _NODISCARD _CONSTEXPR20_CONTAINER strong_ordering operator<=>(const _Vb_const_iterator& _Right) const noexcept { + _Compat(_Right); + if (const auto _CmpResult = this->_Myptr <=> _Right._Myptr; _CmpResult != 0) { + return _CmpResult; + } + return this->_Myoff <=> _Right._Myoff; + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv + _NODISCARD bool operator!=(const _Vb_const_iterator& _Right) const noexcept { return !(*this == _Right); } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator<(const _Vb_const_iterator& _Right) const noexcept { + _NODISCARD bool operator<(const _Vb_const_iterator& _Right) const noexcept { _Compat(_Right); return this->_Myptr < _Right._Myptr || (this->_Myptr == _Right._Myptr && this->_Myoff < _Right._Myoff); } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator>(const _Vb_const_iterator& _Right) const noexcept { + _NODISCARD bool operator>(const _Vb_const_iterator& _Right) const noexcept { return _Right < *this; } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator<=(const _Vb_const_iterator& _Right) const noexcept { + _NODISCARD bool operator<=(const _Vb_const_iterator& _Right) const noexcept { return !(_Right < *this); } - _NODISCARD _CONSTEXPR20_CONTAINER bool operator>=(const _Vb_const_iterator& _Right) const noexcept { + _NODISCARD bool operator>=(const _Vb_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 _CONSTEXPR20_CONTAINER void _Compat(const _Vb_const_iterator& _Right) const noexcept { // test for compatible iterator pair @@ -2231,7 +2369,8 @@ public: _NODISCARD _CONSTEXPR20_CONTAINER _Vb_iterator operator+(const difference_type _Off) const noexcept { _Vb_iterator _Tmp = *this; - return _Tmp += _Off; + _Tmp += _Off; // TRANSITION, LLVM-49342 + return _Tmp; } _CONSTEXPR20_CONTAINER _Vb_iterator& operator-=(const difference_type _Off) noexcept { @@ -2243,7 +2382,8 @@ public: _NODISCARD _CONSTEXPR20_CONTAINER _Vb_iterator operator-(const difference_type _Off) const noexcept { _Vb_iterator _Tmp = *this; - return _Tmp -= _Off; + _Tmp -= _Off; // TRANSITION, LLVM-49342 + return _Tmp; } _NODISCARD _CONSTEXPR20_CONTAINER reference operator[](const difference_type _Off) const noexcept { @@ -2899,12 +3039,18 @@ public: _Iterator_base12** _Pnext = &this->_Myproxy->_Myfirstiter; while (*_Pnext) { // test offset from beginning of vector const auto& _Pnextiter = static_cast(**_Pnext); - const auto _Off = static_cast(_VBITS * (_Pnextiter._Myptr - _Base)) + _Pnextiter._Myoff; + const auto _Temp = *_Pnext; // TRANSITION, VSO-1269037 + if (!_Pnextiter._Myptr) { // orphan the iterator + _Temp->_Myproxy = nullptr; + *_Pnext = _Temp->_Mynextiter; + continue; + } + const auto _Off = static_cast(_VBITS * (_Pnextiter._Myptr - _Base)) + _Pnextiter._Myoff; if (_Off < _Offlo || _Offhi < _Off) { - _Pnext = &(*_Pnext)->_Mynextiter; + _Pnext = &_Temp->_Mynextiter; } else { // orphan the iterator - (*_Pnext)->_Myproxy = nullptr; - *_Pnext = (*_Pnext)->_Mynextiter; + _Temp->_Myproxy = nullptr; + *_Pnext = _Temp->_Mynextiter; } } } @@ -2952,18 +3098,6 @@ public: } }; -template -_NODISCARD _CONSTEXPR20_CONTAINER bool operator==( - const vector& _Left, const vector& _Right) { - return _Left.size() == _Right.size() && _Left._Myvec == _Right._Myvec; -} - -template -_NODISCARD _CONSTEXPR20_CONTAINER bool operator!=( - const vector& _Left, const vector& _Right) { - return !(_Left == _Right); -} - // STRUCT TEMPLATE SPECIALIZATION hash template struct hash> { @@ -2975,18 +3109,6 @@ struct hash> { } }; -#if _HAS_CXX20 -template -_CONSTEXPR20_CONTAINER typename vector<_Ty, _Alloc>::size_type erase(vector<_Ty, _Alloc>& _Cont, const _Uty& _Val) { - return _Erase_remove(_Cont, _Val); -} - -template -_CONSTEXPR20_CONTAINER typename vector<_Ty, _Alloc>::size_type erase_if(vector<_Ty, _Alloc>& _Cont, _Pr _Pred) { - return _Erase_remove_if(_Cont, _Pass_fn(_Pred)); -} -#endif // _HAS_CXX20 - #if _HAS_CXX17 namespace pmr { template diff --git a/stl/inc/xatomic.h b/stl/inc/xatomic.h index c5f97d76db9..a03ba25dde4 100644 --- a/stl/inc/xatomic.h +++ b/stl/inc/xatomic.h @@ -31,9 +31,6 @@ _STL_DISABLE_CLANG_WARNINGS #ifdef _M_CEE_PURE #define _YIELD_PROCESSOR() #else // ^^^ _M_CEE_PURE / !_M_CEE_PURE vvv -#if 1 // TRANSITION, VS 2019 16.10 -extern "C" void _mm_pause(void); -#endif // TRANSITION, VS 2019 16.10 #define _YIELD_PROCESSOR() _mm_pause() #endif // ^^^ !_M_CEE_PURE ^^^ diff --git a/stl/inc/xcharconv.h b/stl/inc/xcharconv.h index ecd69b6d02e..fff630353ae 100644 --- a/stl/inc/xcharconv.h +++ b/stl/inc/xcharconv.h @@ -39,6 +39,9 @@ _BITMASK_OPS(chars_format) struct to_chars_result { char* ptr; errc ec; +#if _HAS_CXX20 + _NODISCARD friend bool operator==(const to_chars_result&, const to_chars_result&) = default; +#endif // _HAS_CXX20 }; _STD_END diff --git a/stl/inc/xcharconv_ryu.h b/stl/inc/xcharconv_ryu.h index 1b2b95a8e05..5ed23a839db 100644 --- a/stl/inc/xcharconv_ryu.h +++ b/stl/inc/xcharconv_ryu.h @@ -1121,7 +1121,7 @@ _NODISCARD inline __floating_decimal_32 __f2d(const uint32_t __ieeeMantissa, con // Step 4: Find the shortest decimal representation in the interval of valid representations. int32_t __removed = 0; - uint32_t __output; + uint32_t _Output; if (__vmIsTrailingZeros || __vrIsTrailingZeros) { // General case, which happens rarely (~4.0%). while (__vp / 10 > __vm / 10) { @@ -1152,7 +1152,7 @@ _NODISCARD inline __floating_decimal_32 __f2d(const uint32_t __ieeeMantissa, con __lastRemovedDigit = 4; } // We need to take __vr + 1 if __vr is outside bounds or we need to round up. - __output = __vr + ((__vr == __vm && (!__acceptBounds || !__vmIsTrailingZeros)) || __lastRemovedDigit >= 5); + _Output = __vr + ((__vr == __vm && (!__acceptBounds || !__vmIsTrailingZeros)) || __lastRemovedDigit >= 5); } else { // Specialized for the common case (~96.0%). Percentages below are relative to this. // Loop iterations below (approximately): @@ -1165,13 +1165,13 @@ _NODISCARD inline __floating_decimal_32 __f2d(const uint32_t __ieeeMantissa, con ++__removed; } // We need to take __vr + 1 if __vr is outside bounds or we need to round up. - __output = __vr + (__vr == __vm || __lastRemovedDigit >= 5); + _Output = __vr + (__vr == __vm || __lastRemovedDigit >= 5); } const int32_t __exp = __e10 + __removed; __floating_decimal_32 __fd; __fd.__exponent = __exp; - __fd.__mantissa = __output; + __fd.__mantissa = _Output; return __fd; } @@ -1302,9 +1302,9 @@ _NODISCARD inline to_chars_result _Large_integer_to_chars(char* const _First, ch _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _Last, const __floating_decimal_32 __v, chars_format _Fmt, const uint32_t __ieeeMantissa, const uint32_t __ieeeExponent) { // Step 5: Print the decimal representation. - uint32_t __output = __v.__mantissa; + uint32_t _Output = __v.__mantissa; int32_t _Ryu_exponent = __v.__exponent; - const uint32_t __olength = __decimalLength9(__output); + const uint32_t __olength = __decimalLength9(_Output); int32_t _Scientific_exponent = _Ryu_exponent + static_cast(__olength) - 1; if (_Fmt == chars_format{}) { @@ -1344,7 +1344,7 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La } if (_Fmt == chars_format::fixed) { - // Example: __output == 1729, __olength == 4 + // Example: _Output == 1729, __olength == 4 // _Ryu_exponent | Printed | _Whole_digits | _Total_fixed_length | Notes // --------------|----------|---------------|----------------------|--------------------------------------- @@ -1366,7 +1366,7 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La uint32_t _Total_fixed_length; if (_Ryu_exponent >= 0) { // cases "172900" and "1729" _Total_fixed_length = static_cast(_Whole_digits); - if (__output == 1) { + if (_Output == 1) { // Rounding can affect the number of digits. // For example, 1e11f is exactly "99999997952" which is 11 digits instead of 12. // We can use a lookup table to detect this and adjust the total length. @@ -1439,28 +1439,28 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La _Mid = _First + _Total_fixed_length; } - while (__output >= 10000) { + while (_Output >= 10000) { #ifdef __clang__ // TRANSITION, LLVM-38217 - const uint32_t __c = __output - 10000 * (__output / 10000); + const uint32_t __c = _Output - 10000 * (_Output / 10000); #else - const uint32_t __c = __output % 10000; + const uint32_t __c = _Output % 10000; #endif - __output /= 10000; + _Output /= 10000; const uint32_t __c0 = (__c % 100) << 1; const uint32_t __c1 = (__c / 100) << 1; _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c0, 2); _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c1, 2); } - if (__output >= 100) { - const uint32_t __c = (__output % 100) << 1; - __output /= 100; + if (_Output >= 100) { + const uint32_t __c = (_Output % 100) << 1; + _Output /= 100; _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c, 2); } - if (__output >= 10) { - const uint32_t __c = __output << 1; + if (_Output >= 10) { + const uint32_t __c = _Output << 1; _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __c, 2); } else { - *--_Mid = static_cast('0' + __output); + *--_Mid = static_cast('0' + _Output); } if (_Ryu_exponent > 0) { // case "172900" with _Can_use_ryu @@ -1491,32 +1491,32 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La // Print the decimal digits. uint32_t __i = 0; - while (__output >= 10000) { + while (_Output >= 10000) { #ifdef __clang__ // TRANSITION, LLVM-38217 - const uint32_t __c = __output - 10000 * (__output / 10000); + const uint32_t __c = _Output - 10000 * (_Output / 10000); #else - const uint32_t __c = __output % 10000; + const uint32_t __c = _Output % 10000; #endif - __output /= 10000; + _Output /= 10000; const uint32_t __c0 = (__c % 100) << 1; const uint32_t __c1 = (__c / 100) << 1; _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE + __c0, 2); _CSTD memcpy(__result + __olength - __i - 3, __DIGIT_TABLE + __c1, 2); __i += 4; } - if (__output >= 100) { - const uint32_t __c = (__output % 100) << 1; - __output /= 100; + if (_Output >= 100) { + const uint32_t __c = (_Output % 100) << 1; + _Output /= 100; _CSTD memcpy(__result + __olength - __i - 1, __DIGIT_TABLE + __c, 2); __i += 2; } - if (__output >= 10) { - const uint32_t __c = __output << 1; + if (_Output >= 10) { + const uint32_t __c = _Output << 1; // We can't use memcpy here: the decimal dot goes between these two digits. __result[2] = __DIGIT_TABLE[__c + 1]; __result[0] = __DIGIT_TABLE[__c]; } else { - __result[0] = static_cast('0' + __output); + __result[0] = static_cast('0' + _Output); } // Print decimal point if needed. @@ -1809,7 +1809,7 @@ _NODISCARD inline __floating_decimal_64 __d2d(const uint64_t __ieeeMantissa, con // Step 4: Find the shortest decimal representation in the interval of valid representations. int32_t __removed = 0; uint8_t __lastRemovedDigit = 0; - uint64_t __output; + uint64_t _Output; // On average, we remove ~2 digits. if (__vmIsTrailingZeros || __vrIsTrailingZeros) { // General case, which happens rarely (~0.7%). @@ -1853,7 +1853,7 @@ _NODISCARD inline __floating_decimal_64 __d2d(const uint64_t __ieeeMantissa, con __lastRemovedDigit = 4; } // We need to take __vr + 1 if __vr is outside bounds or we need to round up. - __output = __vr + ((__vr == __vm && (!__acceptBounds || !__vmIsTrailingZeros)) || __lastRemovedDigit >= 5); + _Output = __vr + ((__vr == __vm && (!__acceptBounds || !__vmIsTrailingZeros)) || __lastRemovedDigit >= 5); } else { // Specialized for the common case (~99.3%). Percentages below are relative to this. bool __roundUp = false; @@ -1887,22 +1887,22 @@ _NODISCARD inline __floating_decimal_64 __d2d(const uint64_t __ieeeMantissa, con ++__removed; } // We need to take __vr + 1 if __vr is outside bounds or we need to round up. - __output = __vr + (__vr == __vm || __roundUp); + _Output = __vr + (__vr == __vm || __roundUp); } const int32_t __exp = __e10 + __removed; __floating_decimal_64 __fd; __fd.__exponent = __exp; - __fd.__mantissa = __output; + __fd.__mantissa = _Output; return __fd; } _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _Last, const __floating_decimal_64 __v, chars_format _Fmt, const double __f) { // Step 5: Print the decimal representation. - uint64_t __output = __v.__mantissa; + uint64_t _Output = __v.__mantissa; int32_t _Ryu_exponent = __v.__exponent; - const uint32_t __olength = __decimalLength17(__output); + const uint32_t __olength = __decimalLength17(_Output); int32_t _Scientific_exponent = _Ryu_exponent + static_cast(__olength) - 1; if (_Fmt == chars_format{}) { @@ -1942,7 +1942,7 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La } if (_Fmt == chars_format::fixed) { - // Example: __output == 1729, __olength == 4 + // Example: _Output == 1729, __olength == 4 // _Ryu_exponent | Printed | _Whole_digits | _Total_fixed_length | Notes // --------------|----------|---------------|----------------------|--------------------------------------- @@ -1964,7 +1964,7 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La uint32_t _Total_fixed_length; if (_Ryu_exponent >= 0) { // cases "172900" and "1729" _Total_fixed_length = static_cast(_Whole_digits); - if (__output == 1) { + if (_Output == 1) { // Rounding can affect the number of digits. // For example, 1e23 is exactly "99999999999999991611392" which is 23 digits instead of 24. // We can use a lookup table to detect this and adjust the total length. @@ -2056,13 +2056,13 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La // We prefer 32-bit operations, even on 64-bit platforms. // We have at most 17 digits, and uint32_t can store 9 digits. - // If __output doesn't fit into uint32_t, we cut off 8 digits, + // If _Output doesn't fit into uint32_t, we cut off 8 digits, // so the rest will fit into uint32_t. - if ((__output >> 32) != 0) { + if ((_Output >> 32) != 0) { // Expensive 64-bit division. - const uint64_t __q = __div1e8(__output); - uint32_t __output2 = static_cast(__output - 100000000 * __q); - __output = __q; + const uint64_t __q = __div1e8(_Output); + uint32_t __output2 = static_cast(_Output - 100000000 * __q); + _Output = __q; const uint32_t __c = __output2 % 10000; __output2 /= 10000; @@ -2077,7 +2077,7 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __d0, 2); _CSTD memcpy(_Mid -= 2, __DIGIT_TABLE + __d1, 2); } - uint32_t __output2 = static_cast(__output); + uint32_t __output2 = static_cast(_Output); while (__output2 >= 10000) { #ifdef __clang__ // TRANSITION, LLVM-38217 const uint32_t __c = __output2 - 10000 * (__output2 / 10000); @@ -2132,13 +2132,13 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La uint32_t __i = 0; // We prefer 32-bit operations, even on 64-bit platforms. // We have at most 17 digits, and uint32_t can store 9 digits. - // If __output doesn't fit into uint32_t, we cut off 8 digits, + // If _Output doesn't fit into uint32_t, we cut off 8 digits, // so the rest will fit into uint32_t. - if ((__output >> 32) != 0) { + if ((_Output >> 32) != 0) { // Expensive 64-bit division. - const uint64_t __q = __div1e8(__output); - uint32_t __output2 = static_cast(__output) - 100000000 * static_cast(__q); - __output = __q; + const uint64_t __q = __div1e8(_Output); + uint32_t __output2 = static_cast(_Output) - 100000000 * static_cast(__q); + _Output = __q; const uint32_t __c = __output2 % 10000; __output2 /= 10000; @@ -2153,7 +2153,7 @@ _NODISCARD inline to_chars_result __to_chars(char* const _First, char* const _La _CSTD memcpy(__result + __olength - __i - 7, __DIGIT_TABLE + __d1, 2); __i += 8; } - uint32_t __output2 = static_cast(__output); + uint32_t __output2 = static_cast(_Output); while (__output2 >= 10000) { #ifdef __clang__ // TRANSITION, LLVM-38217 const uint32_t __c = __output2 - 10000 * (__output2 / 10000); diff --git a/stl/inc/xhash b/stl/inc/xhash index 3c8a7bbaba2..a7fc07566a0 100644 --- a/stl/inc/xhash +++ b/stl/inc/xhash @@ -196,12 +196,14 @@ struct _Reinterpret_move_iter { return _Lhs._Base == _Rhs._Base; } +#if !_HAS_CXX20 #ifndef __CUDACC__ // TRANSITION, VSO-568006 _NODISCARD #endif // TRANSITION, VSO-568006 friend bool operator!=(const _Reinterpret_move_iter& _Lhs, const _Reinterpret_move_iter& _Rhs) { return _Lhs._Base != _Rhs._Base; } +#endif // !_HAS_CXX20 }; // STRUCT TEMPLATE _List_head_construct_ptr diff --git a/stl/inc/xkeycheck.h b/stl/inc/xkeycheck.h index 6baa330a1fd..60ab98f3e21 100644 --- a/stl/inc/xkeycheck.h +++ b/stl/inc/xkeycheck.h @@ -6,7 +6,10 @@ #pragma once #ifndef _XKEYCHECK_H #define _XKEYCHECK_H -#include + +// xkeycheck.h assumes that it's being included by yvals_core.h in a specific order. +// Nothing else should include xkeycheck.h. + #if _STL_COMPILER_PREPROCESSOR #if defined(__cplusplus) && !defined(_ALLOW_KEYWORD_MACROS) && !defined(__INTELLISENSE__) diff --git a/stl/inc/xlocale b/stl/inc/xlocale index fcdbf63bb17..5fe52ccd945 100644 --- a/stl/inc/xlocale +++ b/stl/inc/xlocale @@ -389,9 +389,11 @@ public: return _Ptr == _Loc._Ptr || (name().compare("*") != 0 && name().compare(_Loc.name()) == 0); } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const locale& _Right) const { return !(*this == _Right); } +#endif // !_HAS_CXX20 static _MRTIMP2_PURE const locale& __CLRCALL_PURE_OR_CDECL classic(); // classic "C" locale diff --git a/stl/inc/xloctime b/stl/inc/xloctime index d812c1b5743..b29d17822bc 100644 --- a/stl/inc/xloctime +++ b/stl/inc/xloctime @@ -93,37 +93,38 @@ public: _State = ios_base::goodbit; for (; _Fmtfirst != _Fmtlast; ++_Fmtfirst) { - if (_Ctype_fac.narrow(*_Fmtfirst) != '%') { // match literal element + if (_State != ios_base::goodbit) { + // N4878 [locale.time.get.members]/8.2 + // _State is fail, eof, or bad. Do not proceed to the next fields. Return with current _State. + break; + } else if (_First == _Last) { + // N4878 [locale.time.get.members]/8.3 + _State = ios_base::eofbit | ios_base::failbit; + break; + } else if (_Ctype_fac.narrow(*_Fmtfirst) != '%') { // match literal element if (_Ctype_fac.is(_Ctype::space, *_Fmtfirst)) { while (_First != _Last && _Ctype_fac.is(_Ctype::space, *_First)) { ++_First; } - } else if (*_First != *_Fmtfirst) { // bad literal match + } else if (_Ctype_fac.tolower(*_First) != _Ctype_fac.tolower(*_Fmtfirst)) { // bad literal match _State |= ios_base::failbit; break; } else { ++_First; } - } else if (++_Fmtfirst == _Fmtlast) { // treat trailing % as literal match - if (*_First != _Fmtfirst[-1]) { - _State |= ios_base::failbit; - } else { - ++_First; - } + } else if (++_Fmtfirst == _Fmtlast) { + // N4878 [locale.time.get.members]/8.4: "If the number of elements in the range [fmt, fmtend) is not + // sufficient to unambiguously determine whether the conversion specification is complete and valid, + // the function evaluates err = ios_base::failbit." + _State = ios_base::failbit; break; } else { // get specifier after % char _Specifier = _Ctype_fac.narrow(*_Fmtfirst); char _Modifier = '\0'; - _Elem _Percent = _Fmtfirst[-1]; if (_Specifier == 'E' || _Specifier == 'O' || _Specifier == 'Q' || _Specifier == '#') { - if (++_Fmtfirst == _Fmtlast) { // no specifier, treat %[E0Q#] as literal match - if (*_First != _Percent || ++_First == _Last || _Ctype_fac.narrow(*_First) != _Specifier) { - _State |= ios_base::failbit; - } else { - ++_First; - } - + if (++_Fmtfirst == _Fmtlast) { // no specifier + _State = ios_base::failbit; break; } else { // save both qualifier and specifier _Modifier = _Specifier; @@ -132,18 +133,9 @@ public: } _First = do_get(_First, _Last, _Iosbase, _State, _Pt, _Specifier, _Modifier); // convert a single field - - if (_State != ios_base::goodbit) { - // _State is fail, eof, or bad. Do not proceed to the next fields. Return with current _State. - break; - } } } - if (_First == _Last) { - _State |= ios_base::eofbit; - } - return _First; } @@ -223,7 +215,7 @@ protected: if (_State != ios_base::goodbit || _Ctype_fac.narrow(*_First) != ':') { _State |= ios_base::failbit; // min field is bad } else { - _State |= _Getint(++_First, _Last, 0, 59, _Pt->tm_sec, _Ctype_fac); + _State |= _Getint(++_First, _Last, 0, 60, _Pt->tm_sec, _Ctype_fac); } return _First; @@ -546,7 +538,14 @@ private: char* _Ptr = _Ac; char _Ch; - if (_First != _Last) { + int _Digits_seen = 0; + + while (_First != _Last && _Digits_seen < _Hi_digits && _Ctype_fac.is(ctype_base::space, *_First)) { + ++_First; + ++_Digits_seen; + } + + if (_First != _Last && _Digits_seen < _Hi_digits) { if ((_Ch = _Ctype_fac.narrow(*_First)) == '+') { // copy plus sign *_Ptr++ = '+'; ++_First; @@ -556,9 +555,8 @@ private: } } - int _Digits_seen = 0; - - for (; _First != _Last && _Ctype_fac.narrow(*_First) == '0'; ++_First) { // strip leading zeros + for (; _First != _Last && _Digits_seen < _Hi_digits && _Ctype_fac.narrow(*_First) == '0'; + ++_First) { // strip leading zeros ++_Digits_seen; } diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 764b7449716..ca3bb9bc9bf 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -15,6 +15,10 @@ #include #include +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 + #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) @@ -887,10 +891,12 @@ _NODISCARD _CONSTEXPR20_DYNALLOC bool operator==(const allocator<_Ty>&, const al return true; } +#if !_HAS_CXX20 template -_NODISCARD _CONSTEXPR20_DYNALLOC bool operator!=(const allocator<_Ty>&, const allocator<_Other>&) noexcept { +_NODISCARD bool operator!=(const allocator<_Ty>&, const allocator<_Other>&) noexcept { return false; } +#endif // !_HAS_CXX20 #if _HAS_CXX17 // ALIAS TEMPLATE _Guide_size_type_t FOR DEDUCTION GUIDES, N4687 26.5.4.1 [unord.map.overview]/4 @@ -1172,8 +1178,8 @@ public: static constexpr bool _Unwrap_when_unverified = _ITERATOR_DEBUG_LEVEL == 0; - _Container_proxy* _Myproxy = nullptr; - _Iterator_base12* _Mynextiter = nullptr; + mutable _Container_proxy* _Myproxy = nullptr; + mutable _Iterator_base12* _Mynextiter = nullptr; #if _ITERATOR_DEBUG_LEVEL == 2 private: @@ -1332,7 +1338,8 @@ struct _Container_proxy_ptr12 : _Basic_container_proxy_ptr12 { }; #if _ITERATOR_DEBUG_LEVEL == 0 -#define _GET_PROXY_ALLOCATOR(_Alty, _Al) _Fake_allocator() +_INLINE_VAR constexpr _Fake_allocator _Fake_alloc{}; +#define _GET_PROXY_ALLOCATOR(_Alty, _Al) _Fake_alloc // TRANSITION, VSO-1284799, should be _Fake_allocator{} template using _Container_proxy_ptr = _Fake_proxy_ptr_impl; #else // _ITERATOR_DEBUG_LEVEL == 0 @@ -1575,7 +1582,7 @@ namespace ranges { uninitialized_move_result<_It, _Out> _Uninitialized_move_unchecked( _It _IFirst, const _Se _ILast, _Out _OFirst, const _OSe _OLast) { // clang-format on - if constexpr (is_same_v<_Se, _It> && _Ptr_move_cat<_It, _Out>::_Really_trivial) { + if constexpr (is_same_v<_Se, _It> && is_same_v<_OSe, _Out> && _Ptr_move_cat<_It, _Out>::_Really_trivial) { return _Copy_memcpy_common(_IFirst, _ILast, _OFirst, _OLast); } else { _Uninitialized_backout _Backout{_STD move(_OFirst)}; @@ -2015,6 +2022,98 @@ typename _Container::size_type _Erase_nodes_if(_Container& _Cont, _Pr _Pred) { } return _Old_size - _Cont.size(); } + +#if _HAS_CXX20 +template , int> = 0> +_NODISCARD constexpr auto uses_allocator_construction_args(const _Alloc& _Al, _Types&&... _Args) noexcept { + if constexpr (!uses_allocator_v<_Ty, _Alloc>) { + static_assert(is_constructible_v<_Ty, _Types...>, + "If uses_allocator_v does not hold, T must be constructible from Types..."); + (void) _Al; + return _STD forward_as_tuple(_STD forward<_Types>(_Args)...); + } else if constexpr (is_constructible_v<_Ty, allocator_arg_t, const _Alloc&, _Types...>) { + using _ReturnType = tuple; + return _ReturnType{allocator_arg, _Al, _STD forward<_Types>(_Args)...}; + } else if constexpr (is_constructible_v<_Ty, _Types..., const _Alloc&>) { + return _STD forward_as_tuple(_STD forward<_Types>(_Args)..., _Al); + } else { + static_assert(_Always_false<_Ty>, + "T must be constructible from either (allocator_arg_t, const Alloc&, Types...) " + "or (Types..., const Alloc&) if uses_allocator_v is true"); + } +} + +template , int> = 0> +_NODISCARD constexpr auto uses_allocator_construction_args( + const _Alloc& _Al, piecewise_construct_t, _Tuple1&& _Tup1, _Tuple2&& _Tup2) noexcept { + return _STD make_tuple(piecewise_construct, + _STD apply( + [&_Al](auto&&... _Tuple_args) { + return _STD uses_allocator_construction_args( + _Al, _STD forward(_Tuple_args)...); + }, + _STD forward<_Tuple1>(_Tup1)), + _STD apply( + [&_Al](auto&&... _Tuple_args) { + return _STD uses_allocator_construction_args( + _Al, _STD forward(_Tuple_args)...); + }, + _STD forward<_Tuple2>(_Tup2))); +} + +template , int> = 0> +_NODISCARD constexpr auto uses_allocator_construction_args(const _Alloc& _Al) noexcept { + // equivalent to + // return _STD uses_allocator_construction_args<_Ty>(_Al, piecewise_construct, tuple<>{}, tuple<>{}); + return _STD make_tuple(piecewise_construct, _STD uses_allocator_construction_args(_Al), + _STD uses_allocator_construction_args(_Al)); +} + +template , int> = 0> +_NODISCARD constexpr auto uses_allocator_construction_args(const _Alloc& _Al, _Uty1&& _Val1, _Uty2&& _Val2) noexcept { + // equivalent to + // return _STD uses_allocator_construction_args<_Ty>(_Al, piecewise_construct, + // _STD forward_as_tuple(_STD forward<_Uty1>(_Val1)), _STD forward_as_tuple(_STD forward<_Uty2>(_Val2))); + return _STD make_tuple(piecewise_construct, + _STD uses_allocator_construction_args(_Al, _STD forward<_Uty1>(_Val1)), + _STD uses_allocator_construction_args(_Al, _STD forward<_Uty2>(_Val2))); +} + +template , int> = 0> +_NODISCARD constexpr auto uses_allocator_construction_args( + const _Alloc& _Al, const pair<_Uty1, _Uty2>& _Pair) noexcept { + // equivalent to + // return _STD uses_allocator_construction_args<_Ty>(_Al, piecewise_construct, + // _STD forward_as_tuple(_Pair.first), _STD forward_as_tuple(_Pair.second)); + return _STD make_tuple(piecewise_construct, + _STD uses_allocator_construction_args(_Al, _Pair.first), + _STD uses_allocator_construction_args(_Al, _Pair.second)); +} + +template , int> = 0> +_NODISCARD constexpr auto uses_allocator_construction_args(const _Alloc& _Al, pair<_Uty1, _Uty2>&& _Pair) noexcept { + // equivalent to + // return _STD uses_allocator_construction_args<_Ty>(_Al, piecewise_construct, + // _STD forward_as_tuple(_STD move(_Pair).first), _STD forward_as_tuple(_STD move(_Pair).second)); + return _STD make_tuple(piecewise_construct, + _STD uses_allocator_construction_args(_Al, _STD move(_Pair).first), + _STD uses_allocator_construction_args(_Al, _STD move(_Pair).second)); +} + +template +_NODISCARD constexpr _Ty make_obj_using_allocator(const _Alloc& _Al, _Types&&... _Args) { + return _STD make_from_tuple<_Ty>(_STD uses_allocator_construction_args<_Ty>(_Al, _STD forward<_Types>(_Args)...)); +} + +template +constexpr _Ty* uninitialized_construct_using_allocator(_Ty* _Ptr, const _Alloc& _Al, _Types&&... _Args) { + return _STD apply( + [&](auto&&... _Construct_args) { + return _STD construct_at(_Ptr, _STD forward(_Construct_args)...); + }, + _STD uses_allocator_construction_args<_Ty>(_Al, _STD forward<_Types>(_Args)...)); +} +#endif // _HAS_CXX20 _STD_END #pragma pop_macro("new") diff --git a/stl/inc/xpolymorphic_allocator.h b/stl/inc/xpolymorphic_allocator.h index 47b1bdc68ae..6df84ce7f46 100644 --- a/stl/inc/xpolymorphic_allocator.h +++ b/stl/inc/xpolymorphic_allocator.h @@ -22,6 +22,7 @@ _STL_DISABLE_CLANG_WARNINGS _STD_BEGIN +#if !_HAS_CXX20 // FUNCTION TEMPLATE _Uses_allocator_construct template void _Uses_allocator_construct2( @@ -133,6 +134,7 @@ void _Uses_allocator_construct( _Uses_allocator_construct_pair(_Ptr, _Outer, _Inner, _STD forward_as_tuple(_STD forward<_Uty>(_Pair.first)), _STD forward_as_tuple(_STD forward<_Vty>(_Pair.second))); } +#endif // !_HAS_CXX20 #if _HAS_CXX17 namespace pmr { @@ -168,9 +170,11 @@ namespace pmr { return &_Left == &_Right || _Left.is_equal(_Right); } +#if !_HAS_CXX20 _NODISCARD inline bool operator!=(const memory_resource& _Left, const memory_resource& _Right) noexcept { return !(_Left == _Right); } +#endif // !_HAS_CXX20 // FUNCTION get_default_resource extern "C" _CRT_SATELLITE_1 memory_resource* __cdecl _Aligned_get_default_resource() noexcept; @@ -269,8 +273,12 @@ namespace pmr { template void construct(_Uty* const _Ptr, _Types&&... _Args) { // propagate allocator *this if uses_allocator_v<_Uty, polymorphic_allocator> +#if _HAS_CXX20 + _STD uninitialized_construct_using_allocator(_Ptr, *this, _STD forward<_Types>(_Args)...); +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv allocator _Al{}; _Uses_allocator_construct(_Ptr, _Al, *this, _STD forward<_Types>(_Args)...); +#endif // ^^^ !_HAS_CXX20 ^^^ } template @@ -299,11 +307,13 @@ namespace pmr { return *_Left.resource() == *_Right.resource(); } +#if !_HAS_CXX20 template _NODISCARD bool operator!=( const polymorphic_allocator<_Ty1>& _Left, const polymorphic_allocator<_Ty2>& _Right) noexcept { return !(_Left == _Right); } +#endif // !_HAS_CXX20 } // namespace pmr diff --git a/stl/inc/xstring b/stl/inc/xstring index 80355987c2c..9e63cab02b2 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -39,6 +39,9 @@ struct _Char_traits { // properties of a string or stream element using pos_type = streampos; using off_type = streamoff; using state_type = _Mbstatet; +#if _HAS_CXX20 + using comparison_category = strong_ordering; +#endif // _HAS_CXX20 // For copy/move, we can uniformly call memcpy/memmove (or their builtin versions) for all element types. @@ -67,8 +70,8 @@ struct _Char_traits { // properties of a string or stream element _Pre_satisfies_(_Dest_size >= _Count) static _CONSTEXPR20 _Elem* _Copy_s(_Out_writes_all_(_Dest_size) _Elem* const _First1, - const size_t _Dest_size, _In_reads_(_Count) const _Elem* const _First2, - const size_t _Count) noexcept { // copy [_First2, _First2 + _Count) to [_First1, _First1 + _Dest_size) + const size_t _Dest_size, _In_reads_(_Count) const _Elem* const _First2, const size_t _Count) noexcept { + // copy [_First2, _First2 + _Count) to [_First1, _First1 + _Dest_size) _STL_VERIFY(_Count <= _Dest_size, "invalid argument"); return copy(_First1, _First2, _Count); } @@ -161,18 +164,33 @@ struct _Char_traits { // properties of a string or stream element } static _CONSTEXPR20 _Elem* assign( - _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept - /* strengthened */ { + _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept /* strengthened */ { // assign _Count * _Ch to [_First, ...) - for (_Elem* _Next = _First; _Count > 0; --_Count, ++_Next) { - *_Next = _Ch; +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { + for (_Elem* _Next = _First; _Count > 0; --_Count, ++_Next) { + _STD construct_at(_Next, _Ch); + } + } else +#endif // __cpp_lib_constexpr_string + { + for (_Elem* _Next = _First; _Count > 0; --_Count, ++_Next) { + *_Next = _Ch; + } } return _First; } static _CONSTEXPR17 void assign(_Elem& _Left, const _Elem& _Right) noexcept { - _Left = _Right; +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { + _STD construct_at(_STD addressof(_Left), _Right); + } else +#endif // __cpp_lib_constexpr_string + { + _Left = _Right; + } } _NODISCARD static constexpr bool eq(const _Elem& _Left, const _Elem& _Right) noexcept { @@ -217,6 +235,9 @@ public: using pos_type = streampos; using off_type = streamoff; using state_type = mbstate_t; +#if _HAS_CXX20 + using comparison_category = strong_ordering; +#endif // _HAS_CXX20 using _Primary_char_traits::_Copy_s; using _Primary_char_traits::copy; @@ -265,8 +286,7 @@ public: } static _CONSTEXPR20 _Elem* assign( - _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept - /* strengthened */ { + _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept /* strengthened */ { // assign _Count * _Ch to [_First, ...) #ifdef __cpp_lib_is_constant_evaluated if (_STD is_constant_evaluated()) { @@ -355,6 +375,9 @@ public: using pos_type = streampos; using off_type = streamoff; using state_type = mbstate_t; +#if _HAS_CXX20 + using comparison_category = strong_ordering; +#endif // _HAS_CXX20 using _Primary_char_traits::_Copy_s; using _Primary_char_traits::copy; @@ -412,8 +435,7 @@ public: } static _CONSTEXPR20 _Elem* assign( - _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept - /* strengthened */ { + _Out_writes_all_(_Count) _Elem* const _First, size_t _Count, const _Elem _Ch) noexcept /* strengthened */ { // assign _Count * _Ch to [_First, ...) #ifdef __cpp_lib_is_constant_evaluated if (_STD is_constant_evaluated()) { @@ -1115,6 +1137,17 @@ public: #endif // _ITERATOR_DEBUG_LEVEL } +#if _HAS_CXX20 + _NODISCARD constexpr strong_ordering operator<=>(const _String_view_iterator& _Right) const noexcept { +#if _ITERATOR_DEBUG_LEVEL >= 1 + _STL_VERIFY(_Mydata == _Right._Mydata && _Mysize == _Right._Mysize, + "cannot compare incompatible string_view iterators"); + return _Myoff <=> _Right._Myoff; +#else // ^^^ _ITERATOR_DEBUG_LEVEL >= 1 ^^^ // vvv _ITERATOR_DEBUG_LEVEL == 0 vvv + return _Myptr <=> _Right._Myptr; +#endif // _ITERATOR_DEBUG_LEVEL + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD constexpr bool operator!=(const _String_view_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -1140,6 +1173,7 @@ public: _NODISCARD constexpr bool operator>=(const _String_view_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL >= 1 friend constexpr void _Verify_range(const _String_view_iterator& _First, const _String_view_iterator& _Last) { @@ -1659,11 +1693,13 @@ _NODISCARD constexpr bool operator==( return _Lhs._Equal(_Rhs); } +#if !_HAS_CXX20 template // TRANSITION, VSO-409326 _NODISCARD constexpr bool operator==( const _Identity_t> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { return _Lhs._Equal(_Rhs); } +#endif // !_HAS_CXX20 template // TRANSITION, VSO-409326 _NODISCARD constexpr bool operator==( @@ -1671,7 +1707,7 @@ _NODISCARD constexpr bool operator==( return _Lhs._Equal(_Rhs); } - +#if !_HAS_CXX20 // FUNCTION TEMPLATES operator!= FOR basic_string_view template _NODISCARD constexpr bool operator!=( @@ -1770,7 +1806,37 @@ _NODISCARD constexpr bool operator>=( const basic_string_view<_Elem, _Traits> _Lhs, const _Identity_t> _Rhs) noexcept { return _Lhs.compare(_Rhs) >= 0; } +#endif // !_HAS_CXX20 +#if _HAS_CXX20 +template +struct _Get_comparison_category { + using type = weak_ordering; +}; + +template +struct _Get_comparison_category<_Traits, void_t> { + using type = typename _Traits::comparison_category; + + static_assert(_Is_any_of_v, + "N4878 [string.view.comparison]/4: Mandates: R denotes a comparison category type."); +}; + +template +using _Get_comparison_category_t = typename _Get_comparison_category<_Traits>::type; + +template +_NODISCARD constexpr _Get_comparison_category_t<_Traits> operator<=>( + const basic_string_view<_Elem, _Traits> _Lhs, const basic_string_view<_Elem, _Traits> _Rhs) noexcept { + return static_cast<_Get_comparison_category_t<_Traits>>(_Lhs.compare(_Rhs) <=> 0); +} + +template // TRANSITION, VSO-409326 +_NODISCARD constexpr _Get_comparison_category_t<_Traits> operator<=>( + const basic_string_view<_Elem, _Traits> _Lhs, const _Identity_t> _Rhs) noexcept { + return static_cast<_Get_comparison_category_t<_Traits>>(_Lhs.compare(_Rhs) <=> 0); +} +#endif // _HAS_CXX20 // TYPEDEFS FOR basic_string_view using string_view = basic_string_view; @@ -1841,13 +1907,17 @@ public: using pointer = typename _Mystr::const_pointer; using reference = const value_type&; - _String_const_iterator() noexcept : _Ptr() {} + _CONSTEXPR20_CONTAINER _String_const_iterator() noexcept : _Ptr() {} - _String_const_iterator(pointer _Parg, const _Container_base* _Pstring) noexcept : _Ptr(_Parg) { + _CONSTEXPR20_CONTAINER _String_const_iterator(pointer _Parg, const _Container_base* _Pstring) noexcept + : _Ptr(_Parg) { this->_Adopt(_Pstring); } - _NODISCARD reference operator*() const noexcept { + // TRANSITION, DevCom-1331017 + _CONSTEXPR20_CONTAINER _String_const_iterator& operator=(const _String_const_iterator&) noexcept = default; + + _NODISCARD _CONSTEXPR20_CONTAINER reference operator*() const noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 _STL_VERIFY(_Ptr, "cannot dereference value-initialized string iterator"); const auto _Mycont = static_cast(this->_Getcont()); @@ -1863,11 +1933,11 @@ public: return *_Ptr; } - _NODISCARD pointer operator->() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER pointer operator->() const noexcept { return pointer_traits::pointer_to(**this); } - _String_const_iterator& operator++() noexcept { + _CONSTEXPR20_CONTAINER _String_const_iterator& operator++() noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 _STL_VERIFY(_Ptr, "cannot increment value-initialized string iterator"); const auto _Mycont = static_cast(this->_Getcont()); @@ -1880,13 +1950,13 @@ public: return *this; } - _String_const_iterator operator++(int) noexcept { + _CONSTEXPR20_CONTAINER _String_const_iterator operator++(int) noexcept { _String_const_iterator _Tmp = *this; ++*this; return _Tmp; } - _String_const_iterator& operator--() noexcept { + _CONSTEXPR20_CONTAINER _String_const_iterator& operator--() noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 _STL_VERIFY(_Ptr, "cannot decrement value-initialized string iterator"); const auto _Mycont = static_cast(this->_Getcont()); @@ -1899,13 +1969,13 @@ public: return *this; } - _String_const_iterator operator--(int) noexcept { + _CONSTEXPR20_CONTAINER _String_const_iterator operator--(int) noexcept { _String_const_iterator _Tmp = *this; --*this; return _Tmp; } - void _Verify_offset(const difference_type _Off) const noexcept { + _CONSTEXPR20_CONTAINER void _Verify_offset(const difference_type _Off) const noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 if (_Off == 0) { return; @@ -1932,7 +2002,7 @@ public: #endif // _ITERATOR_DEBUG_LEVEL >= 1 } - _String_const_iterator& operator+=(const difference_type _Off) noexcept { + _CONSTEXPR20_CONTAINER _String_const_iterator& operator+=(const difference_type _Off) noexcept { #if _ITERATOR_DEBUG_LEVEL >= 1 _Verify_offset(_Off); #endif // _ITERATOR_DEBUG_LEVEL >= 1 @@ -1940,34 +2010,42 @@ public: return *this; } - _NODISCARD _String_const_iterator operator+(const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _String_const_iterator operator+(const difference_type _Off) const noexcept { _String_const_iterator _Tmp = *this; - return _Tmp += _Off; + _Tmp += _Off; // TRANSITION, LLVM-49342 + return _Tmp; } - _String_const_iterator& operator-=(const difference_type _Off) noexcept { + _CONSTEXPR20_CONTAINER _String_const_iterator& operator-=(const difference_type _Off) noexcept { return *this += -_Off; } - _NODISCARD _String_const_iterator operator-(const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _String_const_iterator operator-(const difference_type _Off) const noexcept { _String_const_iterator _Tmp = *this; - return _Tmp -= _Off; + _Tmp -= _Off; // TRANSITION, LLVM-49342 + return _Tmp; } - _NODISCARD difference_type operator-(const _String_const_iterator& _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER difference_type operator-(const _String_const_iterator& _Right) const noexcept { _Compat(_Right); return _Ptr - _Right._Ptr; } - _NODISCARD reference operator[](const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER reference operator[](const difference_type _Off) const noexcept { return *(*this + _Off); } - _NODISCARD bool operator==(const _String_const_iterator& _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool operator==(const _String_const_iterator& _Right) const noexcept { _Compat(_Right); return _Ptr == _Right._Ptr; } +#if _HAS_CXX20 + _NODISCARD _CONSTEXPR20_CONTAINER strong_ordering operator<=>(const _String_const_iterator& _Right) const noexcept { + _Compat(_Right); + return _Unfancy(_Ptr) <=> _Unfancy(_Right._Ptr); + } +#else // ^^^ _HAS_CXX20 ^^^ / vvv !_HAS_CXX20 vvv _NODISCARD bool operator!=(const _String_const_iterator& _Right) const noexcept { return !(*this == _Right); } @@ -1988,8 +2066,10 @@ public: _NODISCARD bool operator>=(const _String_const_iterator& _Right) const noexcept { return !(*this < _Right); } +#endif // !_HAS_CXX20 - void _Compat(const _String_const_iterator& _Right) const noexcept { // test for compatible iterator pair + _CONSTEXPR20_CONTAINER void _Compat(const _String_const_iterator& _Right) const noexcept { + // test for compatible iterator pair #if _ITERATOR_DEBUG_LEVEL >= 1 _STL_VERIFY(this->_Getcont() == _Right._Getcont(), "string iterators incompatible (e.g." " point to different string instances)"); @@ -1999,7 +2079,8 @@ public: } #if _ITERATOR_DEBUG_LEVEL >= 1 - friend void _Verify_range(const _String_const_iterator& _First, const _String_const_iterator& _Last) noexcept { + friend _CONSTEXPR20_CONTAINER void _Verify_range( + const _String_const_iterator& _First, const _String_const_iterator& _Last) noexcept { _STL_VERIFY(_First._Getcont() == _Last._Getcont(), "string iterators in range are from different containers"); _STL_VERIFY(_First._Ptr <= _Last._Ptr, "string iterator range transposed"); } @@ -2007,11 +2088,11 @@ public: using _Prevent_inheriting_unwrap = _String_const_iterator; - _NODISCARD const value_type* _Unwrapped() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const value_type* _Unwrapped() const noexcept { return _Unfancy(_Ptr); } - void _Seek_to(const value_type* _It) noexcept { + _CONSTEXPR20_CONTAINER void _Seek_to(const value_type* _It) noexcept { _Ptr = _Refancy(const_cast(_It)); } @@ -2019,7 +2100,7 @@ public: }; template -_NODISCARD _String_const_iterator<_Mystr> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER _String_const_iterator<_Mystr> operator+( typename _String_const_iterator<_Mystr>::difference_type _Off, _String_const_iterator<_Mystr> _Next) noexcept { return _Next += _Off; } @@ -2072,71 +2153,76 @@ public: using _Mybase::_Mybase; - _NODISCARD reference operator*() const noexcept { + // TRANSITION, DevCom-1331017 + _CONSTEXPR20_CONTAINER _String_iterator& operator=(const _String_iterator&) noexcept = default; + + _NODISCARD _CONSTEXPR20_CONTAINER reference operator*() const noexcept { return const_cast(_Mybase::operator*()); } - _NODISCARD pointer operator->() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER pointer operator->() const noexcept { return pointer_traits::pointer_to(**this); } - _String_iterator& operator++() noexcept { + _CONSTEXPR20_CONTAINER _String_iterator& operator++() noexcept { _Mybase::operator++(); return *this; } - _String_iterator operator++(int) noexcept { + _CONSTEXPR20_CONTAINER _String_iterator operator++(int) noexcept { _String_iterator _Tmp = *this; _Mybase::operator++(); return _Tmp; } - _String_iterator& operator--() noexcept { + _CONSTEXPR20_CONTAINER _String_iterator& operator--() noexcept { _Mybase::operator--(); return *this; } - _String_iterator operator--(int) noexcept { + _CONSTEXPR20_CONTAINER _String_iterator operator--(int) noexcept { _String_iterator _Tmp = *this; _Mybase::operator--(); return _Tmp; } - _String_iterator& operator+=(const difference_type _Off) noexcept { + _CONSTEXPR20_CONTAINER _String_iterator& operator+=(const difference_type _Off) noexcept { _Mybase::operator+=(_Off); return *this; } - _NODISCARD _String_iterator operator+(const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _String_iterator operator+(const difference_type _Off) const noexcept { _String_iterator _Tmp = *this; - return _Tmp += _Off; + _Tmp += _Off; // TRANSITION, LLVM-49342 + return _Tmp; } - _String_iterator& operator-=(const difference_type _Off) noexcept { + _CONSTEXPR20_CONTAINER _String_iterator& operator-=(const difference_type _Off) noexcept { _Mybase::operator-=(_Off); return *this; } using _Mybase::operator-; - _NODISCARD _String_iterator operator-(const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _String_iterator operator-(const difference_type _Off) const noexcept { _String_iterator _Tmp = *this; - return _Tmp -= _Off; + _Tmp -= _Off; // TRANSITION, LLVM-49342 + return _Tmp; } - _NODISCARD reference operator[](const difference_type _Off) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER reference operator[](const difference_type _Off) const noexcept { return const_cast(_Mybase::operator[](_Off)); } using _Prevent_inheriting_unwrap = _String_iterator; - _NODISCARD value_type* _Unwrapped() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER value_type* _Unwrapped() const noexcept { return const_cast(_Unfancy(this->_Ptr)); } }; template -_NODISCARD _String_iterator<_Mystr> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER _String_iterator<_Mystr> operator+( typename _String_iterator<_Mystr>::difference_type _Off, _String_iterator<_Mystr> _Next) noexcept { return _Next += _Off; } @@ -2195,7 +2281,7 @@ public: using reference = value_type&; using const_reference = const value_type&; - _String_val() noexcept : _Bx(), _Mysize(0), _Myres(0) {} + _CONSTEXPR20_CONTAINER _String_val() noexcept : _Bx() {} // length of internal buffer, [1, 16]: static constexpr size_type _BUF_SIZE = 16 / sizeof(value_type) < 1 ? 1 : 16 / sizeof(value_type); @@ -2206,7 +2292,7 @@ public: : sizeof(value_type) <= 8 ? 1 : 0; - value_type* _Myptr() noexcept { + _CONSTEXPR20_CONTAINER value_type* _Myptr() noexcept { value_type* _Result = _Bx._Buf; if (_Large_string_engaged()) { _Result = _Unfancy(_Bx._Ptr); @@ -2215,7 +2301,7 @@ public: return _Result; } - const value_type* _Myptr() const noexcept { + _CONSTEXPR20_CONTAINER const value_type* _Myptr() const noexcept { const value_type* _Result = _Bx._Buf; if (_Large_string_engaged()) { _Result = _Unfancy(_Bx._Ptr); @@ -2224,17 +2310,24 @@ public: return _Result; } - bool _Large_string_engaged() const noexcept { + _CONSTEXPR20_CONTAINER bool _Large_string_engaged() const noexcept { +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { + return true; + } +#endif // __cpp_lib_constexpr_string return _BUF_SIZE <= _Myres; } - void _Check_offset(const size_type _Off) const { // checks whether _Off is in the bounds of [0, size()] + _CONSTEXPR20_CONTAINER void _Check_offset(const size_type _Off) const { + // checks whether _Off is in the bounds of [0, size()] if (_Mysize < _Off) { _Xran(); } } - void _Check_offset_exclusive(const size_type _Off) const { // checks whether _Off is in the bounds of [0, size()) + _CONSTEXPR20_CONTAINER void _Check_offset_exclusive(const size_type _Off) const { + // checks whether _Off is in the bounds of [0, size()) if (_Mysize <= _Off) { _Xran(); } @@ -2244,23 +2337,23 @@ public: _Xout_of_range("invalid string position"); } - size_type _Clamp_suffix_size(const size_type _Off, const size_type _Size) const noexcept { + _CONSTEXPR20_CONTAINER size_type _Clamp_suffix_size(const size_type _Off, const size_type _Size) const noexcept { // trims _Size to the longest it can be assuming a string at/after _Off return (_STD min)(_Size, _Mysize - _Off); } union _Bxty { // storage for small buffer or pointer to larger one - _Bxty() noexcept {} // user-provided, for fancy pointers + _CONSTEXPR20_CONTAINER _Bxty() noexcept : _Ptr() {} // user-provided, for fancy pointers - ~_Bxty() noexcept {} // user-provided, for fancy pointers + _CONSTEXPR20_CONTAINER ~_Bxty() noexcept {} // user-provided, for fancy pointers value_type _Buf[_BUF_SIZE]; pointer _Ptr; char _Alias[_BUF_SIZE]; // TRANSITION, ABI: _Alias is preserved for binary compatibility (especially /clr) } _Bx; - size_type _Mysize; // current length of string - size_type _Myres; // current storage reserved for string + size_type _Mysize = 0; // current length of string + size_type _Myres = 0; // current storage reserved for string }; // CLASS TEMPLATE basic_string @@ -2354,7 +2447,7 @@ private: #endif // _HAS_CXX17 public: - basic_string(const basic_string& _Right) + _CONSTEXPR20_CONTAINER basic_string(const basic_string& _Right) : _Mypair(_One_then_variadic_args_t{}, _Alty_traits::select_on_container_copy_construction(_Right._Getal())) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -2362,24 +2455,27 @@ public: _Proxy._Release(); } - basic_string(const basic_string& _Right, const _Alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _Al) { + _CONSTEXPR20_CONTAINER basic_string(const basic_string& _Right, const _Alloc& _Al) + : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); _Construct_lv_contents(_Right); _Proxy._Release(); } - basic_string() noexcept(is_nothrow_default_constructible_v<_Alty>) : _Mypair(_Zero_then_variadic_args_t{}) { + _CONSTEXPR20_CONTAINER basic_string() noexcept(is_nothrow_default_constructible_v<_Alty>) + : _Mypair(_Zero_then_variadic_args_t{}) { _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); _Tidy_init(); } - explicit basic_string(const _Alloc& _Al) noexcept : _Mypair(_One_then_variadic_args_t{}, _Al) { + _CONSTEXPR20_CONTAINER explicit basic_string(const _Alloc& _Al) noexcept + : _Mypair(_One_then_variadic_args_t{}, _Al) { _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); _Tidy_init(); } - basic_string(const basic_string& _Right, const size_type _Roff, const _Alloc& _Al = _Alloc()) + _CONSTEXPR20_CONTAINER basic_string(const basic_string& _Right, const size_type _Roff, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Right [_Roff, ) auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -2388,7 +2484,7 @@ public: _Proxy._Release(); } - basic_string( + _CONSTEXPR20_CONTAINER basic_string( const basic_string& _Right, const size_type _Roff, const size_type _Count, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Right [_Roff, _Roff + _Count) auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); @@ -2398,7 +2494,8 @@ public: _Proxy._Release(); } - basic_string(_In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) + _CONSTEXPR20_CONTAINER basic_string( + _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) : _Mypair(_Zero_then_variadic_args_t{}) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -2407,7 +2504,7 @@ public: _Proxy._Release(); } - basic_string( + _CONSTEXPR20_CONTAINER basic_string( _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count, const _Alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); @@ -2417,7 +2514,7 @@ public: _Proxy._Release(); } - basic_string(_In_z_ const _Elem* const _Ptr) : _Mypair(_Zero_then_variadic_args_t{}) { + _CONSTEXPR20_CONTAINER basic_string(_In_z_ const _Elem* const _Ptr) : _Mypair(_Zero_then_variadic_args_t{}) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); _Tidy_init(); @@ -2428,7 +2525,8 @@ public: #if _HAS_CXX17 template ::value, int> = 0> #endif // _HAS_CXX17 - basic_string(_In_z_ const _Elem* const _Ptr, const _Alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _Al) { + _CONSTEXPR20_CONTAINER basic_string(_In_z_ const _Elem* const _Ptr, const _Alloc& _Al) + : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); _Tidy_init(); @@ -2436,7 +2534,8 @@ public: _Proxy._Release(); } - basic_string(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) : _Mypair(_Zero_then_variadic_args_t{}) { + _CONSTEXPR20_CONTAINER basic_string(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) + : _Mypair(_Zero_then_variadic_args_t{}) { // construct from _Count * _Ch auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -2448,7 +2547,7 @@ public: #if _HAS_CXX17 template ::value, int> = 0> #endif // _HAS_CXX17 - basic_string(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch, const _Alloc& _Al) + _CONSTEXPR20_CONTAINER basic_string(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch, const _Alloc& _Al) : _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Count * _Ch with allocator auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -2458,7 +2557,8 @@ public: } template , int> = 0> - basic_string(_Iter _First, _Iter _Last, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { + _CONSTEXPR20_CONTAINER basic_string(_Iter _First, _Iter _Last, const _Alloc& _Al = _Alloc()) + : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); _Tidy_init(); @@ -2468,7 +2568,7 @@ public: } template - void _Construct(_Iter _First, const _Iter _Last, input_iterator_tag) { + _CONSTEXPR20_CONTAINER void _Construct(_Iter _First, const _Iter _Last, input_iterator_tag) { // initialize from [_First, _Last), input iterators _Tidy_deallocate_guard _Guard{this}; for (; _First != _Last; ++_First) { @@ -2479,33 +2579,35 @@ public: } template - void _Construct(const _Iter _First, const _Iter _Last, forward_iterator_tag) { + _CONSTEXPR20_CONTAINER void _Construct(const _Iter _First, const _Iter _Last, forward_iterator_tag) { // initialize from [_First, _Last), forward iterators const size_type _Count = _Convert_size(static_cast(_STD distance(_First, _Last))); reserve(_Count); _Construct(_First, _Last, input_iterator_tag{}); } - void _Construct(_Elem* const _First, _Elem* const _Last, random_access_iterator_tag) { + _CONSTEXPR20_CONTAINER void _Construct(_Elem* const _First, _Elem* const _Last, random_access_iterator_tag) { // initialize from [_First, _Last), pointers if (_First != _Last) { assign(_First, _Convert_size(static_cast(_Last - _First))); } } - void _Construct(const _Elem* const _First, const _Elem* const _Last, random_access_iterator_tag) { + _CONSTEXPR20_CONTAINER void _Construct( + const _Elem* const _First, const _Elem* const _Last, random_access_iterator_tag) { // initialize from [_First, _Last), const pointers if (_First != _Last) { assign(_First, _Convert_size(static_cast(_Last - _First))); } } - basic_string(basic_string&& _Right) noexcept : _Mypair(_One_then_variadic_args_t{}, _STD move(_Right._Getal())) { + _CONSTEXPR20_CONTAINER basic_string(basic_string&& _Right) noexcept + : _Mypair(_One_then_variadic_args_t{}, _STD move(_Right._Getal())) { _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); - _Take_contents(_Right, bool_constant<_Can_memcpy_val>{}); + _Take_contents(_Right); } - basic_string(basic_string&& _Right, const _Alloc& _Al) noexcept( + _CONSTEXPR20_CONTAINER basic_string(basic_string&& _Right, const _Alloc& _Al) noexcept( _Alty_traits::is_always_equal::value) // strengthened : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); @@ -2518,12 +2620,13 @@ public: } } - _Take_contents(_Right, bool_constant<_Can_memcpy_val>{}); + _Take_contents(_Right); _Proxy._Release(); } - basic_string(_String_constructor_concat_tag, const basic_string& _Source_of_al, const _Elem* const _Left_ptr, - const size_type _Left_size, const _Elem* const _Right_ptr, const size_type _Right_size) + _CONSTEXPR20_CONTAINER basic_string(_String_constructor_concat_tag, const basic_string& _Source_of_al, + const _Elem* const _Left_ptr, const size_type _Left_size, const _Elem* const _Right_ptr, + const size_type _Right_size) : _Mypair( _One_then_variadic_args_t{}, _Alty_traits::select_on_container_copy_construction(_Source_of_al._Getal())) { _STL_INTERNAL_CHECK(_Left_size <= max_size()); @@ -2535,13 +2638,28 @@ public: _Elem* _Ptr = _My_data._Bx._Buf; auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _My_data); // throws - if (_New_capacity < _New_size) { - _New_capacity = _Calculate_growth(_New_size, _BUF_SIZE - 1, max_size()); - const pointer _Fancyptr = _Getal().allocate(_New_capacity + 1); // throws - _Ptr = _Unfancy(_Fancyptr); + +#ifdef __cpp_lib_constexpr_string + const bool _Activate_large_mode = _New_capacity < _New_size || _STD is_constant_evaluated(); +#else // ^^^ __cpp_lib_constexpr_string / !__cpp_lib_constexpr_string vvv + const bool _Activate_large_mode = _New_capacity < _New_size; +#endif // __cpp_lib_constexpr_string + + if (_Activate_large_mode) { + // we should never allocate less than _BUF_SIZE space (_New_size could be small if constant evaluated) + const size_type _Requested_size = (_STD max)(_New_size, _BUF_SIZE); + _New_capacity = _Calculate_growth(_Requested_size, _BUF_SIZE - 1, max_size()); + const pointer _Fancyptr = _Getal().allocate(_New_capacity + 1); // throws + _Ptr = _Unfancy(_Fancyptr); _Construct_in_place(_My_data._Bx._Ptr, _Fancyptr); } +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Ptr, _New_capacity + 1, _Elem()); + } +#endif // __cpp_lib_constexpr_string + _My_data._Mysize = _New_size; _My_data._Myres = _New_capacity; _Traits::copy(_Ptr, _Left_ptr, _Left_size); @@ -2550,7 +2668,7 @@ public: _Proxy._Release(); } - basic_string(_String_constructor_concat_tag, basic_string& _Left, basic_string& _Right) + _CONSTEXPR20_CONTAINER basic_string(_String_constructor_concat_tag, basic_string& _Left, basic_string& _Right) : _Mypair(_One_then_variadic_args_t{}, _Left._Getal()) { auto& _My_data = _Mypair._Myval2; auto& _Left_data = _Left._Mypair._Myval2; @@ -2568,7 +2686,7 @@ public: if (_Fits_in_left && _Right_capacity <= _Left_capacity) { // take _Left's buffer, max_size() is OK because _Fits_in_left _My_data._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); // throws, hereafter nothrow in this block - _Take_contents(_Left, bool_constant<_Can_memcpy_val>{}); + _Take_contents(_Left); const auto _Ptr = _My_data._Myptr(); _Traits::copy(_Ptr + _Left_size, _Right_data._Myptr(), _Right_size + 1); _My_data._Mysize = _New_size; @@ -2588,7 +2706,7 @@ public: // therefore: _Right must have more than the minimum capacity, so it must be _Large_string_engaged() _STL_INTERNAL_CHECK(_Right_data._Large_string_engaged()); _My_data._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); // throws, hereafter nothrow in this block - _Take_contents(_Right, bool_constant<_Can_memcpy_val>{}); + _Take_contents(_Right); const auto _Ptr = _Unfancy(_My_data._Bx._Ptr); _Traits::move(_Ptr + _Left_size, _Ptr, _Right_size + 1); _Traits::copy(_Ptr, _Left_data._Myptr(), _Left_size); @@ -2607,6 +2725,11 @@ public: _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _My_data); // throws const pointer _Fancyptr = _Getal().allocate(_New_capacity + 1); // throws // nothrow hereafter +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Unfancy(_Fancyptr), _New_capacity + 1, _Elem()); + } +#endif // __cpp_lib_constexpr_string _Construct_in_place(_My_data._Bx._Ptr, _Fancyptr); _My_data._Mysize = _New_size; _My_data._Myres = _New_capacity; @@ -2618,7 +2741,7 @@ public: #if _HAS_CXX17 template = 0> - explicit basic_string(const _StringViewIsh& _Right, const _Alloc& _Al = _Alloc()) + _CONSTEXPR20_CONTAINER explicit basic_string(const _StringViewIsh& _Right, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -2628,7 +2751,7 @@ public: } template = 0> - basic_string( + _CONSTEXPR20_CONTAINER basic_string( const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count, const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t{}, _Al) { // construct from _Right [_Roff, _Roff + _Count) using _Al auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); @@ -2642,19 +2765,20 @@ public: #if _HAS_CXX20 basic_string(_String_constructor_rvalue_allocator_tag, _Alloc&& _Al) : _Mypair(_One_then_variadic_args_t{}, _STD move(_Al)) { + // Used exclusively by basic_stringbuf _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); _Tidy_init(); } #endif // _HAS_CXX20 private: - void _Move_assign(basic_string& _Right, _Equal_allocators) noexcept { + _CONSTEXPR20_CONTAINER void _Move_assign(basic_string& _Right, _Equal_allocators) noexcept { _Tidy_deallocate(); _Pocma(_Getal(), _Right._Getal()); - _Take_contents(_Right, bool_constant<_Can_memcpy_val>{}); + _Take_contents(_Right); } - void _Move_assign(basic_string& _Right, _Propagate_allocators) noexcept { + _CONSTEXPR20_CONTAINER void _Move_assign(basic_string& _Right, _Propagate_allocators) noexcept { if (_Getal() == _Right._Getal()) { _Move_assign(_Right, _Equal_allocators{}); } else { @@ -2663,11 +2787,11 @@ private: _Mypair._Myval2._Reload_proxy( _GET_PROXY_ALLOCATOR(_Alty, _Getal()), _GET_PROXY_ALLOCATOR(_Alty, _Right._Getal())); _Pocma(_Getal(), _Right._Getal()); - _Take_contents(_Right, bool_constant<_Can_memcpy_val>{}); + _Take_contents(_Right); } } - void _Move_assign(basic_string& _Right, _No_propagate_allocators) { + _CONSTEXPR20_CONTAINER void _Move_assign(basic_string& _Right, _No_propagate_allocators) { if (_Getal() == _Right._Getal()) { _Move_assign(_Right, _Equal_allocators{}); } else { @@ -2677,8 +2801,8 @@ private: public: #if _HAS_CXX20 - bool _Move_assign_from_buffer(_Elem* const _Right, const size_type _Size, const size_type _Res) { - // Move assign from a buffer, used by basic_stringbuf; returns _Large_string_engaged() + _NODISCARD bool _Move_assign_from_buffer(_Elem* const _Right, const size_type _Size, const size_type _Res) { + // Move assign from a buffer, used exclusively by basic_stringbuf; returns _Large_string_engaged() _Tidy_deallocate(); pointer _Fancy_right = _Refancy(_Right); auto& _My_data = _Mypair._Myval2; @@ -2702,7 +2826,7 @@ public: }; _NODISCARD _Released_buffer _Release_to_buffer(_Alloc& _Al) { - // Release to a buffer, or allocate a new one if in small string mode + // Release to a buffer, or allocate a new one if in small string mode; used exclusively by basic_stringbuf _Released_buffer _Result; auto& _My_data = _Mypair._Myval2; _Result._Size = _My_data._Mysize; @@ -2721,7 +2845,8 @@ public: } #endif // _HAS_CXX20 - basic_string& operator=(basic_string&& _Right) noexcept(noexcept(_Move_assign(_Right, _Choose_pocma<_Alty>{}))) { + _CONSTEXPR20_CONTAINER basic_string& operator=(basic_string&& _Right) noexcept( + noexcept(_Move_assign(_Right, _Choose_pocma<_Alty>{}))) { if (this != _STD addressof(_Right)) { _Move_assign(_Right, _Choose_pocma<_Alty>{}); } @@ -2729,7 +2854,7 @@ public: return *this; } - basic_string& assign(basic_string&& _Right) noexcept(noexcept(*this = _STD move(_Right))) { + _CONSTEXPR20_CONTAINER basic_string& assign(basic_string&& _Right) noexcept(noexcept(*this = _STD move(_Right))) { *this = _STD move(_Right); return *this; } @@ -2744,33 +2869,35 @@ private: _CSTD memcpy(_My_data_mem, _Right_data_mem, _Memcpy_val_size); } - void _Take_contents(basic_string& _Right, true_type) noexcept { - // assign by stealing _Right's buffer, memcpy optimization - // pre: this != &_Right - // pre: allocator propagation (POCMA) from _Right, if necessary, is complete - // pre: *this owns no memory, iterators orphaned (note: - // _Buf/_Ptr/_Mysize/_Myres may be garbage init) -#if _ITERATOR_DEBUG_LEVEL != 0 - if (_Right._Mypair._Myval2._Large_string_engaged()) { - // take ownership of _Right's iterators along with its buffer - _Swap_proxy_and_iterators(_Right); - } else { - _Right._Mypair._Myval2._Orphan_all(); - } -#endif // _ITERATOR_DEBUG_LEVEL != 0 - - _Memcpy_val_from(_Right); - _Right._Tidy_init(); - } - - void _Take_contents(basic_string& _Right, false_type) noexcept { - // assign by stealing _Right's buffer, general case + _CONSTEXPR20_CONTAINER void _Take_contents(basic_string& _Right) noexcept { + // assign by stealing _Right's buffer // pre: this != &_Right // pre: allocator propagation (POCMA) from _Right, if necessary, is complete // pre: *this owns no memory, iterators orphaned // (note: _Buf/_Ptr/_Mysize/_Myres may be garbage init) auto& _My_data = _Mypair._Myval2; auto& _Right_data = _Right._Mypair._Myval2; + + if constexpr (_Can_memcpy_val) { +#ifdef __cpp_lib_constexpr_string + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_constexpr_string + { +#if _ITERATOR_DEBUG_LEVEL != 0 + if (_Right_data._Large_string_engaged()) { + // take ownership of _Right's iterators along with its buffer + _Swap_proxy_and_iterators(_Right); + } else { + _Right_data._Orphan_all(); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + _Memcpy_val_from(_Right); + _Right._Tidy_init(); + return; + } + } + if (_Right_data._Large_string_engaged()) { // steal buffer _Construct_in_place(_My_data._Bx._Ptr, _Right_data._Bx._Ptr); _Right_data._Bx._Ptr = nullptr; @@ -2785,7 +2912,7 @@ private: _Right._Tidy_init(); } - void _Construct_lv_contents(const basic_string& _Right) { + _CONSTEXPR20_CONTAINER void _Construct_lv_contents(const basic_string& _Right) { // assign by copying data stored in _Right // pre: this != &_Right // pre: *this owns no memory, iterators orphaned (note: @@ -2794,7 +2921,16 @@ private: const size_type _Right_size = _Right_data._Mysize; const _Elem* const _Right_ptr = _Right_data._Myptr(); auto& _My_data = _Mypair._Myval2; - if (_Right_size < _BUF_SIZE) { // stay small, don't allocate + +#ifdef __cpp_lib_constexpr_string + const bool _Stay_small = _Right_size < _BUF_SIZE && !_STD is_constant_evaluated(); +#else // ^^^ __cpp_lib_constexpr_string / !__cpp_lib_constexpr_string vvv + const bool _Stay_small = _Right_size < _BUF_SIZE; +#endif // __cpp_lib_constexpr_string + + // NOTE: even if _Right is in large mode, we only go into large mode ourselves if the actual size of _Right + // requires it + if (_Stay_small) { // stay small, don't allocate _Traits::copy(_My_data._Bx._Buf, _Right_ptr, _BUF_SIZE); _My_data._Mysize = _Right_size; _My_data._Myres = _BUF_SIZE - 1; @@ -2805,13 +2941,19 @@ private: const size_type _New_capacity = (_STD min)(_Right_size | _ALLOC_MASK, max_size()); const pointer _New_array = _Al.allocate(_New_capacity + 1); // throws _Construct_in_place(_My_data._Bx._Ptr, _New_array); + +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Unfancy(_New_array), _New_capacity + 1, _Elem()); + } +#endif // __cpp_lib_constexpr_string _Traits::copy(_Unfancy(_New_array), _Right_ptr, _Right_size + 1); _My_data._Mysize = _Right_size; _My_data._Myres = _New_capacity; } public: - basic_string(initializer_list<_Elem> _Ilist, const _Alloc& _Al = allocator_type()) + _CONSTEXPR20_CONTAINER basic_string(initializer_list<_Elem> _Ilist, const _Alloc& _Al = allocator_type()) : _Mypair(_One_then_variadic_args_t{}, _Al) { auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); _Container_proxy_ptr<_Alty> _Proxy(_Alproxy, _Mypair._Myval2); @@ -2820,23 +2962,23 @@ public: _Proxy._Release(); } - basic_string& operator=(initializer_list<_Elem> _Ilist) { + _CONSTEXPR20_CONTAINER basic_string& operator=(initializer_list<_Elem> _Ilist) { return assign(_Ilist.begin(), _Convert_size(_Ilist.size())); } - basic_string& operator+=(initializer_list<_Elem> _Ilist) { + _CONSTEXPR20_CONTAINER basic_string& operator+=(initializer_list<_Elem> _Ilist) { return append(_Ilist.begin(), _Convert_size(_Ilist.size())); } - basic_string& assign(initializer_list<_Elem> _Ilist) { + _CONSTEXPR20_CONTAINER basic_string& assign(initializer_list<_Elem> _Ilist) { return assign(_Ilist.begin(), _Convert_size(_Ilist.size())); } - basic_string& append(initializer_list<_Elem> _Ilist) { + _CONSTEXPR20_CONTAINER basic_string& append(initializer_list<_Elem> _Ilist) { return append(_Ilist.begin(), _Convert_size(_Ilist.size())); } - iterator insert(const const_iterator _Where, const initializer_list<_Elem> _Ilist) { + _CONSTEXPR20_CONTAINER iterator insert(const const_iterator _Where, const initializer_list<_Elem> _Ilist) { #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 @@ -2845,7 +2987,7 @@ public: return begin() + static_cast(_Off); } - basic_string& replace( + _CONSTEXPR20_CONTAINER basic_string& replace( const const_iterator _First, const const_iterator _Last, const initializer_list<_Elem> _Ilist) { // replace with initializer_list _Adl_verify_range(_First, _Last); @@ -2857,7 +2999,7 @@ public: return replace(_Offset, _Length, _Ilist.begin(), _Convert_size(_Ilist.size())); } - ~basic_string() noexcept { + _CONSTEXPR20_CONTAINER ~basic_string() noexcept { _Tidy_deallocate(); #if _ITERATOR_DEBUG_LEVEL != 0 auto&& _Alproxy = _GET_PROXY_ALLOCATOR(_Alty, _Getal()); @@ -2872,23 +3014,28 @@ public: private: void _Copy_assign_val_from_small(const basic_string& _Right) { // TRANSITION, VSO-761321; inline into only caller when that's fixed +#ifdef __cpp_lib_constexpr_string + _STL_ASSERT(!_STD is_constant_evaluated(), "SSO should be disabled in a constexpr context"); +#endif // __cpp_lib_constexpr_string _Tidy_deallocate(); if constexpr (_Can_memcpy_val) { _Memcpy_val_from(_Right); } else { - _Traits::copy( - _Mypair._Myval2._Bx._Buf, _Right._Mypair._Myval2._Bx._Buf, _Right._Mypair._Myval2._Mysize + 1); - _Mypair._Myval2._Mysize = _Right._Mypair._Myval2._Mysize; - _Mypair._Myval2._Myres = _Right._Mypair._Myval2._Myres; + auto& _My_data = _Mypair._Myval2; + auto& _Right_data = _Right._Mypair._Myval2; + + _Traits::copy(_My_data._Bx._Buf, _Right_data._Bx._Buf, _Right_data._Mysize + 1); + _My_data._Mysize = _Right_data._Mysize; + _My_data._Myres = _Right_data._Myres; } } - void _Copy_assign(const basic_string& _Right, false_type) { + _CONSTEXPR20_CONTAINER void _Copy_assign(const basic_string& _Right, false_type) { _Pocca(_Getal(), _Right._Getal()); assign(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } - void _Copy_assign(const basic_string& _Right, true_type) { + _CONSTEXPR20_CONTAINER void _Copy_assign(const basic_string& _Right, true_type) { auto& _Al = _Getal(); const auto& _Right_al = _Right._Getal(); if (_Al == _Right_al) { @@ -2905,6 +3052,13 @@ private: const auto _New_capacity = _Calculate_growth(_New_size, 0, _Right.max_size()); auto _Right_al_non_const = _Right_al; const auto _New_ptr = _Right_al_non_const.allocate(_New_capacity); // throws + +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Unfancy(_New_ptr), _New_size + 1, _Elem()); + } +#endif // __cpp_lib_constexpr_string + _Traits::copy(_Unfancy(_New_ptr), _Unfancy(_Right._Mypair._Myval2._Bx._Ptr), _New_size + 1); _Tidy_deallocate(); _Mypair._Myval2._Bx._Ptr = _New_ptr; @@ -2919,7 +3073,7 @@ private: } public: - basic_string& operator=(const basic_string& _Right) { + _CONSTEXPR20_CONTAINER basic_string& operator=(const basic_string& _Right) { if (this != _STD addressof(_Right)) { _Copy_assign(_Right, _Choose_pocca<_Alty>{}); } @@ -2929,16 +3083,16 @@ public: #if _HAS_CXX17 template = 0> - basic_string& operator=(const _StringViewIsh& _Right) { + _CONSTEXPR20_CONTAINER basic_string& operator=(const _StringViewIsh& _Right) { return assign(_Right); } #endif // _HAS_CXX17 - basic_string& operator=(_In_z_ const _Elem* const _Ptr) { + _CONSTEXPR20_CONTAINER basic_string& operator=(_In_z_ const _Elem* const _Ptr) { return assign(_Ptr); } - basic_string& operator=(const _Elem _Ch) { // assign {_Ch, _Elem()} + _CONSTEXPR20_CONTAINER basic_string& operator=(const _Elem _Ch) { // assign {_Ch, _Elem()} _Mypair._Myval2._Mysize = 1; _Elem* const _Ptr = _Mypair._Myval2._Myptr(); _Traits::assign(_Ptr[0], _Ch); @@ -2946,31 +3100,32 @@ public: return *this; } - basic_string& operator+=(const basic_string& _Right) { + _CONSTEXPR20_CONTAINER basic_string& operator+=(const basic_string& _Right) { return append(_Right); } #if _HAS_CXX17 template = 0> - basic_string& operator+=(const _StringViewIsh& _Right) { + _CONSTEXPR20_CONTAINER basic_string& operator+=(const _StringViewIsh& _Right) { return append(_Right); } #endif // _HAS_CXX17 - basic_string& operator+=(_In_z_ const _Elem* const _Ptr) { // append [_Ptr, ) + _CONSTEXPR20_CONTAINER basic_string& operator+=(_In_z_ const _Elem* const _Ptr) { // append [_Ptr, ) return append(_Ptr); } - basic_string& operator+=(_Elem _Ch) { + _CONSTEXPR20_CONTAINER basic_string& operator+=(_Elem _Ch) { push_back(_Ch); return *this; } - basic_string& append(const basic_string& _Right) { + _CONSTEXPR20_CONTAINER basic_string& append(const basic_string& _Right) { return append(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } - basic_string& append(const basic_string& _Right, const size_type _Roff, size_type _Count = npos) { + _CONSTEXPR20_CONTAINER basic_string& append( + const basic_string& _Right, const size_type _Roff, size_type _Count = npos) { // append _Right [_Roff, _Roff + _Count) _Right._Mypair._Myval2._Check_offset(_Roff); _Count = _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count); @@ -2979,20 +3134,22 @@ public: #if _HAS_CXX17 template = 0> - basic_string& append(const _StringViewIsh& _Right) { + _CONSTEXPR20_CONTAINER basic_string& append(const _StringViewIsh& _Right) { const basic_string_view<_Elem, _Traits> _As_view = _Right; return append(_As_view.data(), _Convert_size(_As_view.size())); } template = 0> - basic_string& append(const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { + _CONSTEXPR20_CONTAINER basic_string& append( + const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { // append _Right [_Roff, _Roff + _Count) basic_string_view<_Elem, _Traits> _As_view = _Right; return append(_As_view.substr(_Roff, _Count)); } #endif // _HAS_CXX17 - basic_string& append(_In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { + _CONSTEXPR20_CONTAINER basic_string& append( + _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { // append [_Ptr, _Ptr + _Count) const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Count <= _Mypair._Myval2._Myres - _Old_size) { @@ -3014,11 +3171,12 @@ public: _Ptr, _Count); } - basic_string& append(_In_z_ const _Elem* const _Ptr) { // append [_Ptr, ) + _CONSTEXPR20_CONTAINER basic_string& append(_In_z_ const _Elem* const _Ptr) { // append [_Ptr, ) return append(_Ptr, _Convert_size(_Traits::length(_Ptr))); } - basic_string& append(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { // append _Count * _Ch + _CONSTEXPR20_CONTAINER basic_string& append(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { + // append _Count * _Ch const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Count <= _Mypair._Myval2._Myres - _Old_size) { _Mypair._Myval2._Mysize = _Old_size + _Count; @@ -3040,7 +3198,8 @@ public: } template , int> = 0> - basic_string& append(const _Iter _First, const _Iter _Last) { // append [_First, _Last), input iterators + _CONSTEXPR20_CONTAINER basic_string& append(const _Iter _First, const _Iter _Last) { + // append [_First, _Last), input iterators _Adl_verify_range(_First, _Last); const auto _UFirst = _Get_unwrapped(_First); const auto _ULast = _Get_unwrapped(_Last); @@ -3052,12 +3211,13 @@ public: } } - basic_string& assign(const basic_string& _Right) { + _CONSTEXPR20_CONTAINER basic_string& assign(const basic_string& _Right) { *this = _Right; return *this; } - basic_string& assign(const basic_string& _Right, const size_type _Roff, size_type _Count = npos) { + _CONSTEXPR20_CONTAINER basic_string& assign( + const basic_string& _Right, const size_type _Roff, size_type _Count = npos) { // assign _Right [_Roff, _Roff + _Count) _Right._Mypair._Myval2._Check_offset(_Roff); _Count = _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count); @@ -3066,20 +3226,22 @@ public: #if _HAS_CXX17 template = 0> - basic_string& assign(const _StringViewIsh& _Right) { + _CONSTEXPR20_CONTAINER basic_string& assign(const _StringViewIsh& _Right) { const basic_string_view<_Elem, _Traits> _As_view = _Right; return assign(_As_view.data(), _Convert_size(_As_view.size())); } template = 0> - basic_string& assign(const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { + _CONSTEXPR20_CONTAINER basic_string& assign( + const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { // assign _Right [_Roff, _Roff + _Count) basic_string_view<_Elem, _Traits> _As_view = _Right; return assign(_As_view.substr(_Roff, _Count)); } #endif // _HAS_CXX17 - basic_string& assign(_In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { + _CONSTEXPR20_CONTAINER basic_string& assign( + _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { // assign [_Ptr, _Ptr + _Count) if (_Count <= _Mypair._Myval2._Myres) { _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); @@ -3098,11 +3260,12 @@ public: _Ptr); } - basic_string& assign(_In_z_ const _Elem* const _Ptr) { + _CONSTEXPR20_CONTAINER basic_string& assign(_In_z_ const _Elem* const _Ptr) { return assign(_Ptr, _Convert_size(_Traits::length(_Ptr))); } - basic_string& assign(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { // assign _Count * _Ch + _CONSTEXPR20_CONTAINER basic_string& assign(_CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { + // assign _Count * _Ch if (_Count <= _Mypair._Myval2._Myres) { _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Mypair._Myval2._Mysize = _Count; @@ -3121,7 +3284,7 @@ public: } template , int> = 0> - basic_string& assign(const _Iter _First, const _Iter _Last) { + _CONSTEXPR20_CONTAINER basic_string& assign(const _Iter _First, const _Iter _Last) { _Adl_verify_range(_First, _Last); const auto _UFirst = _Get_unwrapped(_First); const auto _ULast = _Get_unwrapped(_Last); @@ -3131,7 +3294,7 @@ public: basic_string _Right(_UFirst, _ULast, get_allocator()); if (_Mypair._Myval2._Myres < _Right._Mypair._Myval2._Myres) { _Mypair._Myval2._Orphan_all(); - _Swap_data(_Right, bool_constant<_Can_memcpy_val>{}); + _Swap_data(_Right); return *this; } else { return assign(_Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); @@ -3139,11 +3302,12 @@ public: } } - basic_string& insert(const size_type _Off, const basic_string& _Right) { // insert _Right at _Off + _CONSTEXPR20_CONTAINER basic_string& insert(const size_type _Off, const basic_string& _Right) { + // insert _Right at _Off return insert(_Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } - basic_string& insert( + _CONSTEXPR20_CONTAINER basic_string& insert( const size_type _Off, const basic_string& _Right, const size_type _Roff, size_type _Count = npos) { // insert _Right [_Roff, _Roff + _Count) at _Off _Right._Mypair._Myval2._Check_offset(_Roff); @@ -3153,33 +3317,44 @@ public: #if _HAS_CXX17 template = 0> - basic_string& insert(const size_type _Off, const _StringViewIsh& _Right) { // insert _Right at _Off + _CONSTEXPR20_CONTAINER basic_string& insert(const size_type _Off, const _StringViewIsh& _Right) { + // insert _Right at _Off const basic_string_view<_Elem, _Traits> _As_view = _Right; return insert(_Off, _As_view.data(), _Convert_size(_As_view.size())); } template = 0> - basic_string& insert(const size_type _Off, const _StringViewIsh& _Right, const size_type _Roff, - const size_type _Count = npos) { // insert _Right [_Roff, _Roff + _Count) at _Off + _CONSTEXPR20_CONTAINER basic_string& insert( + const size_type _Off, const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { + // insert _Right [_Roff, _Roff + _Count) at _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return insert(_Off, _As_view.substr(_Roff, _Count)); } #endif // _HAS_CXX17 - basic_string& insert( + _CONSTEXPR20_CONTAINER basic_string& insert( const size_type _Off, _In_reads_(_Count) const _Elem* const _Ptr, _CRT_GUARDOVERFLOW const size_type _Count) { // insert [_Ptr, _Ptr + _Count) at _Off _Mypair._Myval2._Check_offset(_Off); const size_type _Old_size = _Mypair._Myval2._Mysize; - if (_Count <= _Mypair._Myval2._Myres - _Old_size) { + + // checking for overlapping ranges is technically UB (considering string literals), so just always reallocate + // and copy to the new buffer if constant evaluated +#ifdef __cpp_lib_constexpr_string + const bool _Check_overlap = _Count <= _Mypair._Myval2._Myres - _Old_size && !_STD is_constant_evaluated(); +#else // ^^^ __cpp_lib_constexpr_string / !__cpp_lib_constexpr_string vvv + const bool _Check_overlap = _Count <= _Mypair._Myval2._Myres - _Old_size; +#endif // __cpp_lib_constexpr_string + + if (_Check_overlap) { _Mypair._Myval2._Mysize = _Old_size + _Count; _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); _Elem* const _Insert_at = _Old_ptr + _Off; // the range [_Ptr, _Ptr + _Ptr_shifted_after) is left alone by moving the suffix out, // while the range [_Ptr + _Ptr_shifted_after, _Ptr + _Count) shifts down by _Count size_type _Ptr_shifted_after; - if (_Ptr + _Count <= _Insert_at - || _Ptr > _Old_ptr + _Old_size) { // inserted content is before the shifted region, or does not alias + if (_Ptr + _Count <= _Insert_at || _Ptr > _Old_ptr + _Old_size) { + // inserted content is before the shifted region, or does not alias _Ptr_shifted_after = _Count; // none of _Ptr's data shifts } else if (_Insert_at <= _Ptr) { // all of [_Ptr, _Ptr + _Count) shifts _Ptr_shifted_after = 0; @@ -3205,11 +3380,13 @@ public: _Off, _Ptr, _Count); } - basic_string& insert(const size_type _Off, _In_z_ const _Elem* const _Ptr) { // insert [_Ptr, ) at _Off + _CONSTEXPR20_CONTAINER basic_string& insert(const size_type _Off, _In_z_ const _Elem* const _Ptr) { + // insert [_Ptr, ) at _Off return insert(_Off, _Ptr, _Convert_size(_Traits::length(_Ptr))); } - basic_string& insert(const size_type _Off, _CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { + _CONSTEXPR20_CONTAINER basic_string& insert( + const size_type _Off, _CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { // insert _Count * _Ch at _Off _Mypair._Myval2._Check_offset(_Off); const size_type _Old_size = _Mypair._Myval2._Mysize; @@ -3233,7 +3410,7 @@ public: _Off, _Count, _Ch); } - iterator insert(const const_iterator _Where, const _Elem _Ch) { // insert _Ch at _Where + _CONSTEXPR20_CONTAINER iterator insert(const const_iterator _Where, const _Elem _Ch) { // insert _Ch at _Where #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 @@ -3242,7 +3419,8 @@ public: return begin() + static_cast(_Off); } - iterator insert(const const_iterator _Where, _CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { + _CONSTEXPR20_CONTAINER iterator insert( + const const_iterator _Where, _CRT_GUARDOVERFLOW const size_type _Count, const _Elem _Ch) { // insert _Count * _Elem at _Where #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible"); @@ -3253,7 +3431,7 @@ public: } template , int> = 0> - iterator insert(const const_iterator _Where, const _Iter _First, const _Iter _Last) { + _CONSTEXPR20_CONTAINER iterator insert(const const_iterator _Where, const _Iter _First, const _Iter _Last) { // insert [_First, _Last) at _Where, input iterators #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible"); @@ -3272,14 +3450,14 @@ public: return begin() + static_cast(_Off); } - basic_string& erase(const size_type _Off = 0) { // erase elements [_Off, ...) + _CONSTEXPR20_CONTAINER basic_string& erase(const size_type _Off = 0) { // erase elements [_Off, ...) _Mypair._Myval2._Check_offset(_Off); _Eos(_Off); return *this; } private: - basic_string& _Erase_noexcept(const size_type _Off, size_type _Count) noexcept { + _CONSTEXPR20_CONTAINER basic_string& _Erase_noexcept(const size_type _Off, size_type _Count) noexcept { _Count = _Mypair._Myval2._Clamp_suffix_size(_Off, _Count); const size_type _Old_size = _Mypair._Myval2._Mysize; _Elem* const _My_ptr = _Mypair._Myval2._Myptr(); @@ -3291,12 +3469,13 @@ private: } public: - basic_string& erase(const size_type _Off, const size_type _Count) { // erase elements [_Off, _Off + _Count) + _CONSTEXPR20_CONTAINER basic_string& erase(const size_type _Off, const size_type _Count) { + // erase elements [_Off, _Off + _Count) _Mypair._Myval2._Check_offset(_Off); return _Erase_noexcept(_Off, _Count); } - iterator erase(const const_iterator _Where) noexcept /* strengthened */ { + _CONSTEXPR20_CONTAINER iterator erase(const const_iterator _Where) noexcept /* strengthened */ { #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_Where._Getcont() == _STD addressof(_Mypair._Myval2), "string iterator incompatible"); #endif // _ITERATOR_DEBUG_LEVEL != 0 @@ -3305,7 +3484,8 @@ public: return begin() + static_cast(_Off); } - iterator erase(const const_iterator _First, const const_iterator _Last) noexcept /* strengthened */ { + _CONSTEXPR20_CONTAINER iterator erase(const const_iterator _First, const const_iterator _Last) noexcept + /* strengthened */ { _Adl_verify_range(_First, _Last); #if _ITERATOR_DEBUG_LEVEL != 0 _STL_VERIFY(_First._Getcont() == _STD addressof(_Mypair._Myval2), "string iterators incompatible"); @@ -3315,17 +3495,18 @@ public: return begin() + static_cast(_Off); } - void clear() noexcept { // erase all + _CONSTEXPR20_CONTAINER void clear() noexcept { // erase all _Eos(0); } - basic_string& replace(const size_type _Off, const size_type _Nx, const basic_string& _Right) { + _CONSTEXPR20_CONTAINER basic_string& replace( + const size_type _Off, const size_type _Nx, const basic_string& _Right) { // replace [_Off, _Off + _Nx) with _Right return replace(_Off, _Nx, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } - basic_string& replace(const size_type _Off, size_type _Nx, const basic_string& _Right, const size_type _Roff, - size_type _Count = npos) { + _CONSTEXPR20_CONTAINER basic_string& replace(const size_type _Off, size_type _Nx, const basic_string& _Right, + const size_type _Roff, size_type _Count = npos) { // replace [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count) _Right._Mypair._Myval2._Check_offset(_Roff); _Count = _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count); @@ -3334,22 +3515,23 @@ public: #if _HAS_CXX17 template = 0> - basic_string& replace(const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right) { + _CONSTEXPR20_CONTAINER basic_string& replace( + const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right) { // replace [_Off, _Off + _Nx) with _Right basic_string_view<_Elem, _Traits> _As_view = _Right; return replace(_Off, _Nx, _As_view.data(), _Convert_size(_As_view.size())); } template = 0> - basic_string& replace(const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right, - const size_type _Roff, const size_type _Count = npos) { + _CONSTEXPR20_CONTAINER basic_string& replace(const size_type _Off, const size_type _Nx, + const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) { // replace [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count) basic_string_view<_Elem, _Traits> _As_view = _Right; return replace(_Off, _Nx, _As_view.substr(_Roff, _Count)); } #endif // _HAS_CXX17 - basic_string& replace( + _CONSTEXPR20_CONTAINER basic_string& replace( const size_type _Off, size_type _Nx, _In_reads_(_Count) const _Elem* const _Ptr, const size_type _Count) { // replace [_Off, _Off + _Nx) with [_Ptr, _Ptr + _Count) _Mypair._Myval2._Check_offset(_Off); @@ -3371,31 +3553,39 @@ public: } const size_type _Growth = static_cast(_Count - _Nx); - if (_Growth <= _Mypair._Myval2._Myres - _Old_size) { // growth fits - _Mypair._Myval2._Mysize = _Old_size + _Growth; - _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); - _Elem* const _Insert_at = _Old_ptr + _Off; - _Elem* const _Suffix_at = _Insert_at + _Nx; - size_type _Ptr_shifted_after; // see rationale in insert - if (_Ptr + _Count <= _Insert_at || _Ptr > _Old_ptr + _Old_size) { - _Ptr_shifted_after = _Count; - } else if (_Suffix_at <= _Ptr) { - _Ptr_shifted_after = 0; - } else { - _Ptr_shifted_after = static_cast(_Suffix_at - _Ptr); - } + // checking for overlapping ranges is technically UB (considering string literals), so just always reallocate + // and copy to the new buffer if constant evaluated +#ifdef __cpp_lib_constexpr_string + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_constexpr_string + { + if (_Growth <= _Mypair._Myval2._Myres - _Old_size) { // growth fits + _Mypair._Myval2._Mysize = _Old_size + _Growth; + _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); + _Elem* const _Insert_at = _Old_ptr + _Off; + _Elem* const _Suffix_at = _Insert_at + _Nx; + + size_type _Ptr_shifted_after; // see rationale in insert + if (_Ptr + _Count <= _Insert_at || _Ptr > _Old_ptr + _Old_size) { + _Ptr_shifted_after = _Count; + } else if (_Suffix_at <= _Ptr) { + _Ptr_shifted_after = 0; + } else { + _Ptr_shifted_after = static_cast(_Suffix_at - _Ptr); + } - _Traits::move(_Suffix_at + _Growth, _Suffix_at, _Suffix_size); - // next case must be move, in case _Ptr begins before _Insert_at and contains part of the hole; - // this case doesn't occur in insert because the new content must come from outside the removed - // content there (because in insert there is no removed content) - _Traits::move(_Insert_at, _Ptr, _Ptr_shifted_after); - // the next case can be copy, because it comes from the chunk moved out of the way in the - // first move, and the hole we're filling can't alias the chunk we moved out of the way - _Traits::copy( - _Insert_at + _Ptr_shifted_after, _Ptr + _Growth + _Ptr_shifted_after, _Count - _Ptr_shifted_after); - return *this; + _Traits::move(_Suffix_at + _Growth, _Suffix_at, _Suffix_size); + // next case must be move, in case _Ptr begins before _Insert_at and contains part of the hole; + // this case doesn't occur in insert because the new content must come from outside the removed + // content there (because in insert there is no removed content) + _Traits::move(_Insert_at, _Ptr, _Ptr_shifted_after); + // the next case can be copy, because it comes from the chunk moved out of the way in the + // first move, and the hole we're filling can't alias the chunk we moved out of the way + _Traits::copy( + _Insert_at + _Ptr_shifted_after, _Ptr + _Growth + _Ptr_shifted_after, _Count - _Ptr_shifted_after); + return *this; + } } return _Reallocate_grow_by( @@ -3409,12 +3599,14 @@ public: _Off, _Nx, _Ptr, _Count); } - basic_string& replace(const size_type _Off, const size_type _Nx, _In_z_ const _Elem* const _Ptr) { + _CONSTEXPR20_CONTAINER basic_string& replace( + const size_type _Off, const size_type _Nx, _In_z_ const _Elem* const _Ptr) { // replace [_Off, _Off + _Nx) with [_Ptr, ) return replace(_Off, _Nx, _Ptr, _Convert_size(_Traits::length(_Ptr))); } - basic_string& replace(const size_type _Off, size_type _Nx, const size_type _Count, const _Elem _Ch) { + _CONSTEXPR20_CONTAINER basic_string& replace( + const size_type _Off, size_type _Nx, const size_type _Count, const _Elem _Ch) { // replace [_Off, _Off + _Nx) with _Count * _Ch _Mypair._Myval2._Check_offset(_Off); _Nx = _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx); @@ -3424,8 +3616,8 @@ public: } const size_type _Old_size = _Mypair._Myval2._Mysize; - if (_Count < _Nx - || _Count - _Nx <= _Mypair._Myval2._Myres - _Old_size) { // either we are shrinking, or the growth fits + if (_Count < _Nx || _Count - _Nx <= _Mypair._Myval2._Myres - _Old_size) { + // either we are shrinking, or the growth fits _Mypair._Myval2._Mysize = _Old_size + _Count - _Nx; // may temporarily overflow; // OK because size_type must be unsigned _Elem* const _Old_ptr = _Mypair._Myval2._Myptr(); @@ -3446,7 +3638,8 @@ public: _Off, _Nx, _Count, _Ch); } - basic_string& replace(const const_iterator _First, const const_iterator _Last, const basic_string& _Right) { + _CONSTEXPR20_CONTAINER basic_string& replace( + const const_iterator _First, const const_iterator _Last, const basic_string& _Right) { // replace [_First, _Last) with _Right _Adl_verify_range(_First, _Last); #if _ITERATOR_DEBUG_LEVEL != 0 @@ -3458,7 +3651,8 @@ public: #if _HAS_CXX17 template = 0> - basic_string& replace(const const_iterator _First, const const_iterator _Last, const _StringViewIsh& _Right) { + _CONSTEXPR20_CONTAINER basic_string& replace( + const const_iterator _First, const const_iterator _Last, const _StringViewIsh& _Right) { // replace [_First, _Last) with _Right _Adl_verify_range(_First, _Last); #if _ITERATOR_DEBUG_LEVEL != 0 @@ -3469,7 +3663,7 @@ public: } #endif // _HAS_CXX17 - basic_string& replace(const const_iterator _First, const const_iterator _Last, + _CONSTEXPR20_CONTAINER basic_string& replace(const const_iterator _First, const const_iterator _Last, _In_reads_(_Count) const _Elem* const _Ptr, const size_type _Count) { // replace [_First, _Last) with [_Ptr, _Ptr + _Count) _Adl_verify_range(_First, _Last); @@ -3480,7 +3674,8 @@ public: static_cast(_Last._Ptr - _First._Ptr), _Ptr, _Count); } - basic_string& replace(const const_iterator _First, const const_iterator _Last, _In_z_ const _Elem* const _Ptr) { + _CONSTEXPR20_CONTAINER basic_string& replace( + const const_iterator _First, const const_iterator _Last, _In_z_ const _Elem* const _Ptr) { // replace [_First, _Last) with [_Ptr, ) _Adl_verify_range(_First, _Last); #if _ITERATOR_DEBUG_LEVEL != 0 @@ -3490,7 +3685,7 @@ public: static_cast(_Last._Ptr - _First._Ptr), _Ptr); } - basic_string& replace( + _CONSTEXPR20_CONTAINER basic_string& replace( const const_iterator _First, const const_iterator _Last, const size_type _Count, const _Elem _Ch) { // replace [_First, _Last) with _Count * _Ch _Adl_verify_range(_First, _Last); @@ -3502,7 +3697,7 @@ public: } template , int> = 0> - basic_string& replace( + _CONSTEXPR20_CONTAINER basic_string& replace( const const_iterator _First, const const_iterator _Last, const _Iter _First2, const _Iter _Last2) { // replace [_First, _Last) with [_First2, _Last2), input iterators _Adl_verify_range(_First, _Last); @@ -3522,89 +3717,107 @@ public: } } - _NODISCARD iterator begin() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER iterator begin() noexcept { return iterator(_Refancy(_Mypair._Myval2._Myptr()), _STD addressof(_Mypair._Myval2)); } - _NODISCARD const_iterator begin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator begin() const noexcept { return const_iterator(_Refancy(_Mypair._Myval2._Myptr()), _STD addressof(_Mypair._Myval2)); } - _NODISCARD iterator end() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER iterator end() noexcept { return iterator( _Refancy(_Mypair._Myval2._Myptr()) + static_cast(_Mypair._Myval2._Mysize), _STD addressof(_Mypair._Myval2)); } - _NODISCARD const_iterator end() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator end() const noexcept { return const_iterator( _Refancy(_Mypair._Myval2._Myptr()) + static_cast(_Mypair._Myval2._Mysize), _STD addressof(_Mypair._Myval2)); } - _Elem* _Unchecked_begin() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Elem* _Unchecked_begin() noexcept { return _Mypair._Myval2._Myptr(); } - const _Elem* _Unchecked_begin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const _Elem* _Unchecked_begin() const noexcept { return _Mypair._Myval2._Myptr(); } - _Elem* _Unchecked_end() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Elem* _Unchecked_end() noexcept { return _Mypair._Myval2._Myptr() + _Mypair._Myval2._Mysize; } - const _Elem* _Unchecked_end() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const _Elem* _Unchecked_end() const noexcept { return _Mypair._Myval2._Myptr() + _Mypair._Myval2._Mysize; } - _NODISCARD reverse_iterator rbegin() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER reverse_iterator rbegin() noexcept { return reverse_iterator(end()); } - _NODISCARD const_reverse_iterator rbegin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } - _NODISCARD reverse_iterator rend() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER reverse_iterator rend() noexcept { return reverse_iterator(begin()); } - _NODISCARD const_reverse_iterator rend() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } - _NODISCARD const_iterator cbegin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator cbegin() const noexcept { return begin(); } - _NODISCARD const_iterator cend() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_iterator cend() const noexcept { return end(); } - _NODISCARD const_reverse_iterator crbegin() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reverse_iterator crbegin() const noexcept { return rbegin(); } - _NODISCARD const_reverse_iterator crend() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER const_reverse_iterator crend() const noexcept { return rend(); } - void shrink_to_fit() { // reduce capacity + _CONSTEXPR20_CONTAINER void shrink_to_fit() { // reduce capacity auto& _My_data = _Mypair._Myval2; - if (!_My_data._Large_string_engaged()) { // can't shrink from small mode - return; - } - if (_My_data._Mysize < _BUF_SIZE) { - _Become_small(); - return; +#ifdef __cpp_lib_constexpr_string + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_constexpr_string + { + if (!_My_data._Large_string_engaged()) { // can't shrink from small mode + return; + } + + if (_My_data._Mysize < _BUF_SIZE) { + _Become_small(); + return; + } } - const size_type _Target_capacity = (_STD min)(_My_data._Mysize | _ALLOC_MASK, max_size()); + size_type _Target_capacity = (_STD min)(_My_data._Mysize | _ALLOC_MASK, max_size()); +#ifdef __cpp_lib_constexpr_string + // must allocate at least _BUF_SIZE space + _Target_capacity = (_STD max)(_Target_capacity, _BUF_SIZE); +#endif // __cpp_lib_constexpr_string + if (_Target_capacity < _My_data._Myres) { // worth shrinking, do it auto& _Al = _Getal(); const pointer _New_ptr = _Al.allocate(_Target_capacity + 1); // throws + +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Unfancy(_New_ptr), _Target_capacity + 1, _Elem()); + } +#endif // __cpp_lib_constexpr_string + _My_data._Orphan_all(); _Traits::copy(_Unfancy(_New_ptr), _Unfancy(_My_data._Bx._Ptr), _My_data._Mysize + 1); _Al.deallocate(_My_data._Bx._Ptr, _My_data._Myres + 1); @@ -3613,24 +3826,25 @@ public: } } - _NODISCARD reference at(const size_type _Off) { + _NODISCARD _CONSTEXPR20_CONTAINER reference at(const size_type _Off) { _Mypair._Myval2._Check_offset_exclusive(_Off); return _Mypair._Myval2._Myptr()[_Off]; } - _NODISCARD const_reference at(const size_type _Off) const { + _NODISCARD _CONSTEXPR20_CONTAINER const_reference at(const size_type _Off) const { _Mypair._Myval2._Check_offset_exclusive(_Off); return _Mypair._Myval2._Myptr()[_Off]; } - _NODISCARD reference operator[](const size_type _Off) noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER reference operator[](const size_type _Off) noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Off <= _Mypair._Myval2._Mysize, "string subscript out of range"); #endif // _CONTAINER_DEBUG_LEVEL > 0 return _Mypair._Myval2._Myptr()[_Off]; } - _NODISCARD const_reference operator[](const size_type _Off) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER const_reference operator[](const size_type _Off) const noexcept + /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Off <= _Mypair._Myval2._Mysize, "string subscript out of range"); #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -3638,13 +3852,13 @@ public: } #if _HAS_CXX17 - /* implicit */ operator basic_string_view<_Elem, _Traits>() const noexcept { + /* implicit */ _CONSTEXPR20_CONTAINER operator basic_string_view<_Elem, _Traits>() const noexcept { // return a string_view around *this's character-type sequence return basic_string_view<_Elem, _Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize); } #endif // _HAS_CXX17 - void push_back(const _Elem _Ch) { // insert element at end + _CONSTEXPR20_CONTAINER void push_back(const _Elem _Ch) { // insert element at end const size_type _Old_size = _Mypair._Myval2._Mysize; if (_Old_size < _Mypair._Myval2._Myres) { _Mypair._Myval2._Mysize = _Old_size + 1; @@ -3664,7 +3878,7 @@ public: _Ch); } - void pop_back() noexcept /* strengthened */ { + _CONSTEXPR20_CONTAINER void pop_back() noexcept /* strengthened */ { const size_type _Old_size = _Mypair._Myval2._Mysize; #if _ITERATOR_DEBUG_LEVEL >= 1 _STL_VERIFY(_Old_size != 0, "invalid to pop_back empty string"); @@ -3672,7 +3886,7 @@ public: _Eos(_Old_size - 1); } - _NODISCARD reference front() noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER reference front() noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Mypair._Myval2._Mysize != 0, "front() called on empty string"); #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -3680,7 +3894,7 @@ public: return _Mypair._Myval2._Myptr()[0]; } - _NODISCARD const_reference front() const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER const_reference front() const noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Mypair._Myval2._Mysize != 0, "front() called on empty string"); #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -3688,7 +3902,7 @@ public: return _Mypair._Myval2._Myptr()[0]; } - _NODISCARD reference back() noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER reference back() noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Mypair._Myval2._Mysize != 0, "back() called on empty string"); #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -3696,7 +3910,7 @@ public: return _Mypair._Myval2._Myptr()[_Mypair._Myval2._Mysize - 1]; } - _NODISCARD const_reference back() const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER const_reference back() const noexcept /* strengthened */ { #if _CONTAINER_DEBUG_LEVEL > 0 _STL_VERIFY(_Mypair._Myval2._Mysize != 0, "back() called on empty string"); #endif // _CONTAINER_DEBUG_LEVEL > 0 @@ -3704,29 +3918,29 @@ public: return _Mypair._Myval2._Myptr()[_Mypair._Myval2._Mysize - 1]; } - _NODISCARD _Ret_z_ const _Elem* c_str() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Ret_z_ const _Elem* c_str() const noexcept { return _Mypair._Myval2._Myptr(); } - _NODISCARD _Ret_z_ const _Elem* data() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Ret_z_ const _Elem* data() const noexcept { return _Mypair._Myval2._Myptr(); } #if _HAS_CXX17 - _NODISCARD _Ret_z_ _Elem* data() noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER _Ret_z_ _Elem* data() noexcept { return _Mypair._Myval2._Myptr(); } #endif // _HAS_CXX17 - _NODISCARD size_type length() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type length() const noexcept { return _Mypair._Myval2._Mysize; } - _NODISCARD size_type size() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type size() const noexcept { return _Mypair._Myval2._Mysize; } - _NODISCARD size_type max_size() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type max_size() const noexcept { const size_type _Alloc_max = _Alty_traits::max_size(_Getal()); const size_type _Storage_max = // can always store small string (_STD max)(_Alloc_max, static_cast(_BUF_SIZE)); @@ -3735,7 +3949,7 @@ public: ); } - void resize(_CRT_GUARDOVERFLOW const size_type _Newsize, const _Elem _Ch = _Elem()) { + _CONSTEXPR20_CONTAINER void resize(_CRT_GUARDOVERFLOW const size_type _Newsize, const _Elem _Ch = _Elem()) { // determine new length, padding with _Ch elements as needed const size_type _Old_size = size(); if (_Newsize <= _Old_size) { @@ -3745,12 +3959,13 @@ public: } } - _NODISCARD size_type capacity() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type capacity() const noexcept { return _Mypair._Myval2._Myres; } #if _HAS_CXX20 - void reserve(_CRT_GUARDOVERFLOW const size_type _Newcap) { // determine new minimum length of allocated storage + _CONSTEXPR20_CONTAINER void reserve(_CRT_GUARDOVERFLOW const size_type _Newcap) { + // determine new minimum length of allocated storage if (_Mypair._Myval2._Myres >= _Newcap) { // requested capacity is not larger than current capacity, ignore return; // nothing to do } @@ -3800,11 +4015,12 @@ public: } #endif // _HAS_CXX20 - _NODISCARD bool empty() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool empty() const noexcept { return size() == 0; } - size_type copy(_Out_writes_(_Count) _Elem* const _Ptr, size_type _Count, const size_type _Off = 0) const { + _CONSTEXPR20_CONTAINER size_type copy( + _Out_writes_(_Count) _Elem* const _Ptr, size_type _Count, const size_type _Off = 0) const { // copy [_Off, _Off + _Count) to [_Ptr, _Ptr + _Count) _Mypair._Myval2._Check_offset(_Off); _Count = _Mypair._Myval2._Clamp_suffix_size(_Off, _Count); @@ -3812,8 +4028,9 @@ public: return _Count; } - _Pre_satisfies_(_Dest_size >= _Count) size_type _Copy_s(_Out_writes_all_(_Dest_size) _Elem* const _Dest, - const size_type _Dest_size, size_type _Count, const size_type _Off = 0) const { + _CONSTEXPR20_CONTAINER _Pre_satisfies_(_Dest_size >= _Count) size_type + _Copy_s(_Out_writes_all_(_Dest_size) _Elem* const _Dest, const size_type _Dest_size, size_type _Count, + const size_type _Off = 0) const { // copy [_Off, _Off + _Count) to [_Dest, _Dest + _Dest_size) _Mypair._Myval2._Check_offset(_Off); _Count = _Mypair._Myval2._Clamp_suffix_size(_Off, _Count); @@ -3821,30 +4038,37 @@ public: return _Count; } - void _Swap_data(basic_string& _Right, true_type) { - // exchange _String_val instances with _Right, memcpy optimization - const auto _My_data_mem = - reinterpret_cast(_STD addressof(_Mypair._Myval2)) + _Memcpy_val_offset; - const auto _Right_data_mem = - reinterpret_cast(_STD addressof(_Right._Mypair._Myval2)) + _Memcpy_val_offset; - unsigned char _Temp_mem[_Memcpy_val_size]; - _CSTD memcpy(_Temp_mem, _My_data_mem, _Memcpy_val_size); - _CSTD memcpy(_My_data_mem, _Right_data_mem, _Memcpy_val_size); - _CSTD memcpy(_Right_data_mem, _Temp_mem, _Memcpy_val_size); - } - void _Swap_bx_large_with_small(_Scary_val& _Starts_large, _Scary_val& _Starts_small) { // exchange a string in large mode with one in small mode + // (not _CONSTEXPR20_CONTAINER; SSO should be disabled in a constexpr context) + const pointer _Ptr = _Starts_large._Bx._Ptr; _Destroy_in_place(_Starts_large._Bx._Ptr); _Traits::copy(_Starts_large._Bx._Buf, _Starts_small._Bx._Buf, _BUF_SIZE); _Construct_in_place(_Starts_small._Bx._Ptr, _Ptr); } - void _Swap_data(basic_string& _Right, false_type) { - // exchange _String_val instances with _Right, general case - auto& _My_data = _Mypair._Myval2; - auto& _Right_data = _Right._Mypair._Myval2; + _CONSTEXPR20_CONTAINER void _Swap_data(basic_string& _Right) { + auto& _My_data = _Mypair._Myval2; + auto& _Right_data = _Right._Mypair._Myval2; + + if constexpr (_Can_memcpy_val) { +#ifdef __cpp_lib_constexpr_string + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_constexpr_string + { + const auto _My_data_mem = + reinterpret_cast(_STD addressof(_My_data)) + _Memcpy_val_offset; + const auto _Right_data_mem = + reinterpret_cast(_STD addressof(_Right_data)) + _Memcpy_val_offset; + unsigned char _Temp_mem[_Memcpy_val_size]; + _CSTD memcpy(_Temp_mem, _My_data_mem, _Memcpy_val_size); + _CSTD memcpy(_My_data_mem, _Right_data_mem, _Memcpy_val_size); + _CSTD memcpy(_Right_data_mem, _Temp_mem, _Memcpy_val_size); + return; + } + } + const bool _My_large = _My_data._Large_string_engaged(); const bool _Right_large = _Right_data._Large_string_engaged(); if (_My_large) { @@ -3868,7 +4092,7 @@ public: _STD swap(_My_data._Myres, _Right_data._Myres); } - void swap(basic_string& _Right) noexcept /* strengthened */ { + _CONSTEXPR20_CONTAINER void swap(basic_string& _Right) noexcept /* strengthened */ { if (this != _STD addressof(_Right)) { _Pocs(_Getal(), _Right._Getal()); @@ -3889,12 +4113,12 @@ public: #endif // _ITERATOR_DEBUG_LEVEL != 0 } - _Swap_data(_Right, bool_constant<_Can_memcpy_val>{}); + _Swap_data(_Right); } #if _HAS_CXX17 template = 0> - _NODISCARD size_type find(const _StringViewIsh& _Right, const size_type _Off = 0) const { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find(const _StringViewIsh& _Right, const size_type _Off = 0) const { // look for _Right beginning at or after _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast(_Traits_find<_Traits>( @@ -3902,27 +4126,29 @@ public: } #endif // _HAS_CXX17 - _NODISCARD size_type find(const basic_string& _Right, const size_type _Off = 0) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find( + const basic_string& _Right, const size_type _Off = 0) const noexcept { // look for _Right beginning at or after _Off return static_cast(_Traits_find<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize)); } - _NODISCARD size_type find(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, + _NODISCARD _CONSTEXPR20_CONTAINER size_type find(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for [_Ptr, _Ptr + _Count) beginning at or after _Off return static_cast( _Traits_find<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count)); } - _NODISCARD size_type find(_In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find( + _In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept /* strengthened */ { // look for [_Ptr, ) beginning at or after _Off return static_cast(_Traits_find<_Traits>( _Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr))); } - _NODISCARD size_type find(const _Elem _Ch, const size_type _Off = 0) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find(const _Elem _Ch, const size_type _Off = 0) const noexcept + /* strengthened */ { // look for _Ch at or after _Off return static_cast( _Traits_find_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch)); @@ -3930,7 +4156,7 @@ public: #if _HAS_CXX17 template = 0> - _NODISCARD size_type rfind(const _StringViewIsh& _Right, const size_type _Off = npos) const { + _NODISCARD _CONSTEXPR20_CONTAINER size_type rfind(const _StringViewIsh& _Right, const size_type _Off = npos) const { // look for _Right beginning before _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast(_Traits_rfind<_Traits>( @@ -3938,27 +4164,29 @@ public: } #endif // _HAS_CXX17 - _NODISCARD size_type rfind(const basic_string& _Right, const size_type _Off = npos) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type rfind( + const basic_string& _Right, const size_type _Off = npos) const noexcept { // look for _Right beginning before _Off return static_cast(_Traits_rfind<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize)); } - _NODISCARD size_type rfind(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, + _NODISCARD _CONSTEXPR20_CONTAINER size_type rfind(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for [_Ptr, _Ptr + _Count) beginning before _Off return static_cast( _Traits_rfind<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count)); } - _NODISCARD size_type rfind(_In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type rfind( + _In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ { // look for [_Ptr, ) beginning before _Off return static_cast(_Traits_rfind<_Traits>( _Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr))); } - _NODISCARD size_type rfind(const _Elem _Ch, const size_type _Off = npos) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type rfind(const _Elem _Ch, const size_type _Off = npos) const noexcept + /* strengthened */ { // look for _Ch before _Off return static_cast( _Traits_rfind_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch)); @@ -3966,7 +4194,8 @@ public: #if _HAS_CXX17 template = 0> - _NODISCARD size_type find_first_of(const _StringViewIsh& _Right, const size_type _Off = 0) const { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_of( + const _StringViewIsh& _Right, const size_type _Off = 0) const { // look for one of _Right at or after _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, @@ -3974,28 +4203,30 @@ public: } #endif // _HAS_CXX17 - _NODISCARD size_type find_first_of(const basic_string& _Right, const size_type _Off = 0) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_of( + const basic_string& _Right, const size_type _Off = 0) const noexcept { // look for one of _Right at or after _Off return static_cast(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_first_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, - const size_type _Count) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_of(_In_reads_(_Count) const _Elem* const _Ptr, + const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for one of [_Ptr, _Ptr + _Count) at or after _Off return static_cast(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_first_of(_In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_of( + _In_z_ const _Elem* const _Ptr, const size_type _Off = 0) const noexcept /* strengthened */ { // look for one of [_Ptr, ) at or after _Off return static_cast(_Traits_find_first_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr), _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_first_of(const _Elem _Ch, const size_type _Off = 0) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_of(const _Elem _Ch, const size_type _Off = 0) const noexcept + /* strengthened */ { // look for _Ch at or after _Off return static_cast( _Traits_find_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch)); @@ -4003,7 +4234,8 @@ public: #if _HAS_CXX17 template = 0> - _NODISCARD size_type find_last_of(const _StringViewIsh& _Right, const size_type _Off = npos) const { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_of( + const _StringViewIsh& _Right, const size_type _Off = npos) const { // look for one of _Right before _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, @@ -4011,28 +4243,30 @@ public: } #endif // _HAS_CXX17 - _NODISCARD size_type find_last_of(const basic_string& _Right, size_type _Off = npos) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_of( + const basic_string& _Right, size_type _Off = npos) const noexcept { // look for one of _Right before _Off return static_cast(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_last_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, - const size_type _Count) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_of(_In_reads_(_Count) const _Elem* const _Ptr, + const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for one of [_Ptr, _Ptr + _Count) before _Off return static_cast(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_last_of(_In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_of( + _In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ { // look for one of [_Ptr, ) before _Off return static_cast(_Traits_find_last_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr), _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_last_of(const _Elem _Ch, const size_type _Off = npos) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_of( + const _Elem _Ch, const size_type _Off = npos) const noexcept /* strengthened */ { // look for _Ch before _Off return static_cast( _Traits_rfind_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch)); @@ -4040,7 +4274,8 @@ public: #if _HAS_CXX17 template = 0> - _NODISCARD size_type find_first_not_of(const _StringViewIsh& _Right, const size_type _Off = 0) const { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_not_of( + const _StringViewIsh& _Right, const size_type _Off = 0) const { // look for none of _Right at or after _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast( @@ -4049,29 +4284,30 @@ public: } #endif // _HAS_CXX17 - _NODISCARD size_type find_first_not_of(const basic_string& _Right, const size_type _Off = 0) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_not_of( + const basic_string& _Right, const size_type _Off = 0) const noexcept { // look for none of _Right at or after _Off return static_cast(_Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_first_not_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, - const size_type _Count) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_not_of(_In_reads_(_Count) const _Elem* const _Ptr, + const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for none of [_Ptr, _Ptr + _Count) at or after _Off return static_cast(_Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_first_not_of(_In_z_ const _Elem* const _Ptr, size_type _Off = 0) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_not_of( + _In_z_ const _Elem* const _Ptr, size_type _Off = 0) const noexcept /* strengthened */ { // look for one of [_Ptr, ) at or after _Off return static_cast(_Traits_find_first_not_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr), _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_first_not_of(const _Elem _Ch, const size_type _Off = 0) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_first_not_of( + const _Elem _Ch, const size_type _Off = 0) const noexcept /* strengthened */ { // look for non-_Ch at or after _Off return static_cast( _Traits_find_not_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch)); @@ -4079,7 +4315,8 @@ public: #if _HAS_CXX17 template = 0> - _NODISCARD size_type find_last_not_of(const _StringViewIsh& _Right, const size_type _Off = npos) const { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_not_of( + const _StringViewIsh& _Right, const size_type _Off = npos) const { // look for none of _Right before _Off basic_string_view<_Elem, _Traits> _As_view = _Right; return static_cast( @@ -4088,29 +4325,30 @@ public: } #endif // _HAS_CXX17 - _NODISCARD size_type find_last_not_of(const basic_string& _Right, const size_type _Off = npos) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_not_of( + const basic_string& _Right, const size_type _Off = npos) const noexcept { // look for none of _Right before _Off return static_cast(_Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_last_not_of(_In_reads_(_Count) const _Elem* const _Ptr, const size_type _Off, - const size_type _Count) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_not_of(_In_reads_(_Count) const _Elem* const _Ptr, + const size_type _Off, const size_type _Count) const noexcept /* strengthened */ { // look for none of [_Ptr, _Ptr + _Count) before _Off return static_cast(_Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Count, _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_last_not_of(_In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_not_of( + _In_z_ const _Elem* const _Ptr, const size_type _Off = npos) const noexcept /* strengthened */ { // look for none of [_Ptr, ) before _Off return static_cast(_Traits_find_last_not_of<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ptr, _Traits::length(_Ptr), _Is_specialization<_Traits, char_traits>{})); } - _NODISCARD size_type find_last_not_of(const _Elem _Ch, const size_type _Off = npos) const noexcept - /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER size_type find_last_not_of( + const _Elem _Ch, const size_type _Off = npos) const noexcept /* strengthened */ { // look for non-_Ch before _Off return static_cast( _Traits_rfind_not_ch<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Off, _Ch)); @@ -4118,34 +4356,40 @@ public: #if _HAS_CXX17 _NODISCARD bool _Starts_with(const basic_string_view<_Elem, _Traits> _Right) const noexcept { + // Used exclusively by filesystem return basic_string_view<_Elem, _Traits>(*this)._Starts_with(_Right); } #endif // _HAS_CXX17 - _NODISCARD basic_string substr(const size_type _Off = 0, const size_type _Count = npos) const { + _NODISCARD _CONSTEXPR20_CONTAINER basic_string substr( + const size_type _Off = 0, const size_type _Count = npos) const { // return [_Off, _Off + _Count) as new string return basic_string(*this, _Off, _Count, get_allocator()); } - bool _Equal(const basic_string& _Right) const noexcept { // compare [0, size()) with _Right for equality + _CONSTEXPR20_CONTAINER bool _Equal(const basic_string& _Right) const noexcept { + // compare [0, size()) with _Right for equality return _Traits_equal<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } - bool _Equal(_In_z_ const _Elem* const _Ptr) const noexcept { // compare [0, size()) with _Ptr for equality + _CONSTEXPR20_CONTAINER bool _Equal(_In_z_ const _Elem* const _Ptr) const noexcept { + // compare [0, size()) with _Ptr for equality return _Traits_equal<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Ptr, _Traits::length(_Ptr)); } #if _HAS_CXX17 template = 0> - _NODISCARD int compare(const _StringViewIsh& _Right) const { // compare [0, size()) with _Right + _NODISCARD _CONSTEXPR20_CONTAINER int compare(const _StringViewIsh& _Right) const { + // compare [0, size()) with _Right basic_string_view<_Elem, _Traits> _As_view = _Right; return _Traits_compare<_Traits>( _Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _As_view.data(), _As_view.size()); } template = 0> - _NODISCARD int compare(const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right) const { + _NODISCARD _CONSTEXPR20_CONTAINER int compare( + const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right) const { // compare [_Off, _Off + _Nx) with _Right basic_string_view<_Elem, _Traits> _As_view = _Right; _Mypair._Myval2._Check_offset(_Off); @@ -4154,8 +4398,8 @@ public: } template = 0> - _NODISCARD int compare(const size_type _Off, const size_type _Nx, const _StringViewIsh& _Right, - const size_type _Roff, const size_type _Count = npos) const { + _NODISCARD _CONSTEXPR20_CONTAINER int compare(const size_type _Off, const size_type _Nx, + const _StringViewIsh& _Right, const size_type _Roff, const size_type _Count = npos) const { // compare [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count) basic_string_view<_Elem, _Traits> _As_view = _Right; _Mypair._Myval2._Check_offset(_Off); @@ -4165,20 +4409,21 @@ public: } #endif // _HAS_CXX17 - _NODISCARD int compare(const basic_string& _Right) const noexcept { // compare [0, size()) with _Right + _NODISCARD _CONSTEXPR20_CONTAINER int compare(const basic_string& _Right) const noexcept { + // compare [0, size()) with _Right return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } - _NODISCARD int compare(size_type _Off, size_type _Nx, const basic_string& _Right) const { + _NODISCARD _CONSTEXPR20_CONTAINER int compare(size_type _Off, size_type _Nx, const basic_string& _Right) const { // compare [_Off, _Off + _Nx) with _Right _Mypair._Myval2._Check_offset(_Off); return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr() + _Off, _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx), _Right._Mypair._Myval2._Myptr(), _Right._Mypair._Myval2._Mysize); } - _NODISCARD int compare(const size_type _Off, const size_type _Nx, const basic_string& _Right, const size_type _Roff, - const size_type _Count = npos) const { + _NODISCARD _CONSTEXPR20_CONTAINER int compare(const size_type _Off, const size_type _Nx, const basic_string& _Right, + const size_type _Roff, const size_type _Count = npos) const { // compare [_Off, _Off + _Nx) with _Right [_Roff, _Roff + _Count) _Mypair._Myval2._Check_offset(_Off); _Right._Mypair._Myval2._Check_offset(_Roff); @@ -4186,57 +4431,59 @@ public: _Right._Mypair._Myval2._Myptr() + _Roff, _Right._Mypair._Myval2._Clamp_suffix_size(_Roff, _Count)); } - _NODISCARD int compare(_In_z_ const _Elem* const _Ptr) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER int compare(_In_z_ const _Elem* const _Ptr) const noexcept /* strengthened */ { // compare [0, size()) with [_Ptr, ) return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize, _Ptr, _Traits::length(_Ptr)); } - _NODISCARD int compare(const size_type _Off, const size_type _Nx, _In_z_ const _Elem* const _Ptr) const { + _NODISCARD _CONSTEXPR20_CONTAINER int compare( + const size_type _Off, const size_type _Nx, _In_z_ const _Elem* const _Ptr) const { // compare [_Off, _Off + _Nx) with [_Ptr, ) _Mypair._Myval2._Check_offset(_Off); return _Traits_compare<_Traits>(_Mypair._Myval2._Myptr() + _Off, _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx), _Ptr, _Traits::length(_Ptr)); } - _NODISCARD int compare(const size_type _Off, const size_type _Nx, _In_reads_(_Count) const _Elem* const _Ptr, - const size_type _Count) const { // compare [_Off, _Off + _Nx) with [_Ptr, _Ptr + _Count) + _NODISCARD _CONSTEXPR20_CONTAINER int compare(const size_type _Off, const size_type _Nx, + _In_reads_(_Count) const _Elem* const _Ptr, const size_type _Count) const { + // compare [_Off, _Off + _Nx) with [_Ptr, _Ptr + _Count) _Mypair._Myval2._Check_offset(_Off); return _Traits_compare<_Traits>( _Mypair._Myval2._Myptr() + _Off, _Mypair._Myval2._Clamp_suffix_size(_Off, _Nx), _Ptr, _Count); } #if _HAS_CXX20 - _NODISCARD bool starts_with(const basic_string_view<_Elem, _Traits> _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool starts_with(const basic_string_view<_Elem, _Traits> _Right) const noexcept { return basic_string_view<_Elem, _Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize).starts_with(_Right); } - _NODISCARD bool starts_with(const _Elem _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool starts_with(const _Elem _Right) const noexcept { return basic_string_view<_Elem, _Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize).starts_with(_Right); } - _NODISCARD bool starts_with(const _Elem* const _Right) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER bool starts_with(const _Elem* const _Right) const noexcept /* strengthened */ { return basic_string_view<_Elem, _Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize).starts_with(_Right); } - _NODISCARD bool ends_with(const basic_string_view<_Elem, _Traits> _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool ends_with(const basic_string_view<_Elem, _Traits> _Right) const noexcept { return basic_string_view<_Elem, _Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize).ends_with(_Right); } - _NODISCARD bool ends_with(const _Elem _Right) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER bool ends_with(const _Elem _Right) const noexcept { return basic_string_view<_Elem, _Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize).ends_with(_Right); } - _NODISCARD bool ends_with(const _Elem* const _Right) const noexcept /* strengthened */ { + _NODISCARD _CONSTEXPR20_CONTAINER bool ends_with(const _Elem* const _Right) const noexcept /* strengthened */ { return basic_string_view<_Elem, _Traits>(_Mypair._Myval2._Myptr(), _Mypair._Myval2._Mysize).ends_with(_Right); } #endif // _HAS_CXX20 - _NODISCARD allocator_type get_allocator() const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER allocator_type get_allocator() const noexcept { return static_cast(_Getal()); } private: - _NODISCARD static size_type _Calculate_growth( + _NODISCARD static _CONSTEXPR20_CONTAINER size_type _Calculate_growth( const size_type _Requested, const size_type _Old, const size_type _Max) noexcept { const size_type _Masked = _Requested | _ALLOC_MASK; if (_Masked > _Max) { // the mask overflows, settle for max_size() @@ -4250,12 +4497,12 @@ private: return (_STD max)(_Masked, _Old + _Old / 2); } - _NODISCARD size_type _Calculate_growth(const size_type _Requested) const noexcept { + _NODISCARD _CONSTEXPR20_CONTAINER size_type _Calculate_growth(const size_type _Requested) const noexcept { return _Calculate_growth(_Requested, _Mypair._Myval2._Myres, max_size()); } template - basic_string& _Reallocate_for(const size_type _New_size, _Fty _Fn, _ArgTys... _Args) { + _CONSTEXPR20_CONTAINER basic_string& _Reallocate_for(const size_type _New_size, _Fty _Fn, _ArgTys... _Args) { // reallocate to store exactly _New_size elements, new buffer prepared by // _Fn(_New_ptr, _New_size, _Args...) if (_New_size > max_size()) { @@ -4266,6 +4513,12 @@ private: const size_type _New_capacity = _Calculate_growth(_New_size); auto& _Al = _Getal(); const pointer _New_ptr = _Al.allocate(_New_capacity + 1); // throws + +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Unfancy(_New_ptr), _New_capacity + 1, _Elem()); + } +#endif // __cpp_lib_constexpr_string _Mypair._Myval2._Orphan_all(); _Mypair._Myval2._Mysize = _New_size; _Mypair._Myval2._Myres = _New_capacity; @@ -4281,7 +4534,8 @@ private: } template - basic_string& _Reallocate_grow_by(const size_type _Size_increase, _Fty _Fn, _ArgTys... _Args) { + _CONSTEXPR20_CONTAINER basic_string& _Reallocate_grow_by( + const size_type _Size_increase, _Fty _Fn, _ArgTys... _Args) { // reallocate to increase size by _Size_increase elements, new buffer prepared by // _Fn(_New_ptr, _Old_ptr, _Old_size, _Args...) auto& _My_data = _Mypair._Myval2; @@ -4295,6 +4549,12 @@ private: const size_type _New_capacity = _Calculate_growth(_New_size); auto& _Al = _Getal(); const pointer _New_ptr = _Al.allocate(_New_capacity + 1); // throws + +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { // Begin the lifetimes of the objects before copying to avoid UB + _Traits::assign(_Unfancy(_New_ptr), _New_capacity + 1, _Elem()); + } +#endif // __cpp_lib_constexpr_string _My_data._Orphan_all(); _My_data._Mysize = _New_size; _My_data._Myres = _New_capacity; @@ -4316,6 +4576,8 @@ private: // release any held storage and return to small string mode // pre: *this is in large string mode // pre: this is small enough to return to small string mode + // (not _CONSTEXPR20_CONTAINER; SSO should be disabled in a constexpr context) + _Mypair._Myval2._Orphan_all(); const pointer _Ptr = _Mypair._Myval2._Bx._Ptr; auto& _Al = _Getal(); @@ -4325,18 +4587,33 @@ private: _Mypair._Myval2._Myres = _BUF_SIZE - 1; } - void _Eos(const size_type _Newsize) { // set new length and null terminator + _CONSTEXPR20_CONTAINER void _Eos(const size_type _Newsize) { // set new length and null terminator _Traits::assign(_Mypair._Myval2._Myptr()[_Mypair._Myval2._Mysize = _Newsize], _Elem()); } - void _Tidy_init() noexcept { // initialize basic_string data members - _Mypair._Myval2._Mysize = 0; - _Mypair._Myval2._Myres = _BUF_SIZE - 1; - // the _Traits::assign is last so the codegen doesn't think the char write can alias this - _Traits::assign(_Mypair._Myval2._Bx._Buf[0], _Elem()); + _CONSTEXPR20_CONTAINER void _Tidy_init() noexcept { // initialize basic_string data members + auto& _My_data = _Mypair._Myval2; + _My_data._Mysize = 0; + +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { + _My_data._Myres = _BUF_SIZE; // SSO disabled in constexpr context + auto& _Al = _Getal(); + const pointer _New_ptr = _Al.allocate(_BUF_SIZE + 1); // throws + _My_data._Bx._Ptr = _New_ptr; + + _Elem* const _Raw_new = _Unfancy(_New_ptr); + _Traits::assign(_Raw_new, _BUF_SIZE + 1, _Elem()); + } else +#endif // __cpp_lib_constexpr_string + { + _My_data._Myres = _BUF_SIZE - 1; + // the _Traits::assign is last so the codegen doesn't think the char write can alias this + _Traits::assign(_My_data._Bx._Buf[0], _Elem()); + } } - void _Tidy_deallocate() noexcept { // initialize buffer, deallocating any storage + _CONSTEXPR20_CONTAINER void _Tidy_deallocate() noexcept { // initialize buffer, deallocating any storage _Mypair._Myval2._Orphan_all(); if (_Mypair._Myval2._Large_string_engaged()) { const pointer _Ptr = _Mypair._Myval2._Bx._Ptr; @@ -4345,27 +4622,36 @@ private: _Al.deallocate(_Ptr, _Mypair._Myval2._Myres + 1); } - _Mypair._Myval2._Mysize = 0; - _Mypair._Myval2._Myres = _BUF_SIZE - 1; - // the _Traits::assign is last so the codegen doesn't think the char write can alias this - _Traits::assign(_Mypair._Myval2._Bx._Buf[0], _Elem()); +#ifdef __cpp_lib_constexpr_string + if (_STD is_constant_evaluated()) { + _Mypair._Myval2._Bx._Ptr = nullptr; + _Mypair._Myval2._Mysize = 0; + _Mypair._Myval2._Myres = 0; + } else +#endif // __cpp_lib_constexpr_string + { + _Mypair._Myval2._Mysize = 0; + _Mypair._Myval2._Myres = _BUF_SIZE - 1; + // the _Traits::assign is last so the codegen doesn't think the char write can alias this + _Traits::assign(_Mypair._Myval2._Bx._Buf[0], _Elem()); + } } public: - void _Orphan_all() noexcept { // used by filesystem::path + _CONSTEXPR20_CONTAINER void _Orphan_all() noexcept { // used by filesystem::path _Mypair._Myval2._Orphan_all(); } private: - void _Swap_proxy_and_iterators(basic_string& _Right) { + _CONSTEXPR20_CONTAINER void _Swap_proxy_and_iterators(basic_string& _Right) { _Mypair._Myval2._Swap_proxy_and_iterators(_Right._Mypair._Myval2); } - _Alty& _Getal() noexcept { + _CONSTEXPR20_CONTAINER _Alty& _Getal() noexcept { return _Mypair._Get_first(); } - const _Alty& _Getal() const noexcept { + _CONSTEXPR20_CONTAINER const _Alty& _Getal() const noexcept { return _Mypair._Get_first(); } @@ -4390,13 +4676,13 @@ basic_string(basic_string_view<_Elem, _Traits>, _Guide_size_type_t<_Alloc>, _Gui #endif // _HAS_CXX17 template -void swap(basic_string<_Elem, _Traits, _Alloc>& _Left, basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept -/* strengthened */ { +_CONSTEXPR20_CONTAINER void swap(basic_string<_Elem, _Traits, _Alloc>& _Left, + basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept /* strengthened */ { _Left.swap(_Right); } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { const auto _Left_size = _Left.size(); const auto _Right_size = _Right.size(); @@ -4408,7 +4694,7 @@ _NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( _In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { using _Size_type = typename basic_string<_Elem, _Traits, _Alloc>::size_type; const auto _Left_size = _Convert_size<_Size_type>(_Traits::length(_Left)); @@ -4421,7 +4707,7 @@ _NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( const _Elem _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { const auto _Right_size = _Right.size(); if (_Right_size == _Right.max_size()) { @@ -4432,7 +4718,7 @@ _NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { using _Size_type = typename basic_string<_Elem, _Traits, _Alloc>::size_type; const auto _Left_size = _Left.size(); @@ -4445,7 +4731,7 @@ _NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( const basic_string<_Elem, _Traits, _Alloc>& _Left, const _Elem _Right) { const auto _Left_size = _Left.size(); if (_Left_size == _Left.max_size()) { @@ -4456,19 +4742,19 @@ _NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( const basic_string<_Elem, _Traits, _Alloc>& _Left, basic_string<_Elem, _Traits, _Alloc>&& _Right) { return _STD move(_Right.insert(0, _Left)); } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( basic_string<_Elem, _Traits, _Alloc>&& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { return _STD move(_Left.append(_Right)); } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( basic_string<_Elem, _Traits, _Alloc>&& _Left, basic_string<_Elem, _Traits, _Alloc>&& _Right) { #if _ITERATOR_DEBUG_LEVEL == 2 _STL_VERIFY(_STD addressof(_Left) != _STD addressof(_Right), @@ -4481,44 +4767,58 @@ _NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( _In_z_ const _Elem* const _Left, basic_string<_Elem, _Traits, _Alloc>&& _Right) { return _STD move(_Right.insert(0, _Left)); } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( const _Elem _Left, basic_string<_Elem, _Traits, _Alloc>&& _Right) { return _STD move(_Right.insert(0, 1, _Left)); } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( basic_string<_Elem, _Traits, _Alloc>&& _Left, _In_z_ const _Elem* const _Right) { return _STD move(_Left.append(_Right)); } template -_NODISCARD basic_string<_Elem, _Traits, _Alloc> operator+( +_NODISCARD _CONSTEXPR20_CONTAINER basic_string<_Elem, _Traits, _Alloc> operator+( basic_string<_Elem, _Traits, _Alloc>&& _Left, const _Elem _Right) { _Left.push_back(_Right); return _STD move(_Left); } template -_NODISCARD bool operator==( +_NODISCARD _CONSTEXPR20_CONTAINER bool operator==( const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept { return _Left._Equal(_Right); } template -_NODISCARD bool operator==(_In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { - return _Right._Equal(_Left); +_NODISCARD _CONSTEXPR20_CONTAINER bool operator==( + const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { + return _Left._Equal(_Right); } +#if _HAS_CXX20 template -_NODISCARD bool operator==(const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { - return _Left._Equal(_Right); +_NODISCARD _CONSTEXPR20_CONTAINER _Get_comparison_category_t<_Traits> operator<=>( + const basic_string<_Elem, _Traits, _Alloc>& _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) noexcept { + return static_cast<_Get_comparison_category_t<_Traits>>(_Left.compare(_Right) <=> 0); +} + +template +_NODISCARD _CONSTEXPR20_CONTAINER _Get_comparison_category_t<_Traits> operator<=>( + const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { + return static_cast<_Get_comparison_category_t<_Traits>>(_Left.compare(_Right) <=> 0); +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +template +_NODISCARD bool operator==(_In_z_ const _Elem* const _Left, const basic_string<_Elem, _Traits, _Alloc>& _Right) { + return _Right._Equal(_Left); } template @@ -4600,6 +4900,7 @@ template _NODISCARD bool operator>=(const basic_string<_Elem, _Traits, _Alloc>& _Left, _In_z_ const _Elem* const _Right) { return !(_Left < _Right); } +#endif // ^^^ !_HAS_CXX20 ^^^ using string = basic_string, allocator>; using wstring = basic_string, allocator>; @@ -4685,39 +4986,49 @@ basic_ostream<_Elem, _Traits>& operator<<( // basic_string LITERALS inline namespace literals { inline namespace string_literals { - _NODISCARD inline string operator"" s(const char* _Str, size_t _Len) { + +#ifdef __EDG__ // TRANSITION, VSO-1273381 +#define _CONSTEXPR20_STRING_LITERALS inline +#else // ^^^ workaround / no workaround vvv +#define _CONSTEXPR20_STRING_LITERALS _CONSTEXPR20_CONTAINER +#endif // ^^^ no workaround ^^^ + + _NODISCARD _CONSTEXPR20_STRING_LITERALS string operator"" s(const char* _Str, size_t _Len) { return string(_Str, _Len); } - _NODISCARD inline wstring operator"" s(const wchar_t* _Str, size_t _Len) { + _NODISCARD _CONSTEXPR20_STRING_LITERALS wstring operator"" s(const wchar_t* _Str, size_t _Len) { return wstring(_Str, _Len); } #ifdef __cpp_char8_t - _NODISCARD inline basic_string operator"" s(const char8_t* _Str, size_t _Len) { + _NODISCARD _CONSTEXPR20_STRING_LITERALS basic_string operator"" s(const char8_t* _Str, size_t _Len) { return basic_string(_Str, _Len); } #endif // __cpp_char8_t - _NODISCARD inline u16string operator"" s(const char16_t* _Str, size_t _Len) { + _NODISCARD _CONSTEXPR20_STRING_LITERALS u16string operator"" s(const char16_t* _Str, size_t _Len) { return u16string(_Str, _Len); } - _NODISCARD inline u32string operator"" s(const char32_t* _Str, size_t _Len) { + _NODISCARD _CONSTEXPR20_STRING_LITERALS u32string operator"" s(const char32_t* _Str, size_t _Len) { return u32string(_Str, _Len); } + +#undef _CONSTEXPR20_STRING_LITERALS // TRANSITION, VSO-1273381 + } // namespace string_literals } // namespace literals #if _HAS_CXX20 template -typename basic_string<_Elem, _Traits, _Alloc>::size_type erase( +_CONSTEXPR20_CONTAINER typename basic_string<_Elem, _Traits, _Alloc>::size_type erase( basic_string<_Elem, _Traits, _Alloc>& _Cont, const _Uty& _Val) { return _Erase_remove(_Cont, _Val); } template -typename basic_string<_Elem, _Traits, _Alloc>::size_type erase_if( +_CONSTEXPR20_CONTAINER typename basic_string<_Elem, _Traits, _Alloc>::size_type erase_if( basic_string<_Elem, _Traits, _Alloc>& _Cont, _Pr _Pred) { return _Erase_remove_if(_Cont, _Pass_fn(_Pred)); } diff --git a/stl/inc/xtree b/stl/inc/xtree index aba7f46baee..3d9e6786ee0 100644 --- a/stl/inc/xtree +++ b/stl/inc/xtree @@ -98,18 +98,22 @@ public: return _Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _Tree_unchecked_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 _NODISCARD bool operator==(_Default_sentinel) const noexcept { return !!_Ptr->_Isnil; // TRANSITION, avoid warning C4800: // "Implicit conversion from 'char' to bool. Possible information loss" (/Wall) } +#if !_HAS_CXX20 _NODISCARD bool operator!=(_Default_sentinel) const noexcept { return !_Ptr->_Isnil; } +#endif // !_HAS_CXX20 _Nodeptr _Ptr; // pointer to node }; @@ -232,9 +236,11 @@ public: return this->_Ptr == _Right._Ptr; } +#if !_HAS_CXX20 _NODISCARD bool operator!=(const _Tree_const_iterator& _Right) const noexcept { return !(*this == _Right); } +#endif // !_HAS_CXX20 #if _ITERATOR_DEBUG_LEVEL == 2 friend void _Verify_range(const _Tree_const_iterator& _First, const _Tree_const_iterator& _Last) noexcept { diff --git a/stl/inc/xutility b/stl/inc/xutility index 66e6cbd227b..2179e1251fd 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -774,11 +774,11 @@ concept indirectly_writable = requires(_It&& __i, _Ty&& __t) { }; // CONCEPT _Integer_like -// clang-format off template -concept _Integer_like = _Is_nonbool_integral<_Ty>; +concept _Integer_like = _Is_nonbool_integral>; // CONCEPT _Signed_integer_like +// clang-format off template concept _Signed_integer_like = _Integer_like<_Ty> && static_cast<_Ty>(-1) < static_cast<_Ty>(0); // clang-format on @@ -787,10 +787,20 @@ concept _Signed_integer_like = _Integer_like<_Ty> && static_cast<_Ty>(-1) < stat template using _Make_unsigned_like_t = make_unsigned_t<_Ty>; +template <_Integer_like _Ty> +_NODISCARD constexpr auto _To_unsigned_like(const _Ty _Value) noexcept { + return static_cast<_Make_unsigned_like_t<_Ty>>(_Value); +} + // ALIAS TEMPLATE _Make_signed_like_t template using _Make_signed_like_t = make_signed_t<_Ty>; +template <_Integer_like _Ty> +_NODISCARD constexpr auto _To_signed_like(const _Ty _Value) noexcept { + return static_cast<_Make_signed_like_t<_Ty>>(_Value); +} + // CONCEPT weakly_incrementable // clang-format off template @@ -3757,7 +3767,7 @@ public: #endif // __cpp_lib_concepts } - _NODISCARD _CXX20_DEPRECATE_MOVE_ITERATOR_ARROW _CONSTEXPR17 pointer operator->() const { + _CXX20_DEPRECATE_MOVE_ITERATOR_ARROW _NODISCARD _CONSTEXPR17 pointer operator->() const { return _Current; } diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 988fb6bcfdd..a4d0d1a1aaf 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -150,6 +150,7 @@ // P0458R2 contains() For Ordered And Unordered Associative Containers // P0463R1 endian // P0466R5 Layout-Compatibility And Pointer-Interconvertibility Traits +// P0475R1 Guaranteed Copy Elision For Piecewise Construction // P0476R2 bit_cast // P0482R6 Library Support For char8_t // (mbrtoc8 and c8rtomb not yet implemented) @@ -159,6 +160,7 @@ // P0553R4 Rotating And Counting Functions // P0556R3 Integral Power-Of-2 Operations (renamed by P1956R1) // P0586R2 Integer Comparison Functions +// P0591R4 Utility Functions For Uses-Allocator Construction // P0595R2 is_constant_evaluated() // P0608R3 Improving variant's Converting Constructor/Assignment // P0616R0 Using move() In @@ -183,6 +185,7 @@ // P0912R5 Library Support For Coroutines // P0919R3 Heterogeneous Lookup For Unordered Containers // P0966R1 string::reserve() Should Not Shrink +// P0980R1 constexpr std::string // P1001R2 execution::unseq // P1004R2 constexpr std::vector // P1006R1 constexpr For pointer_traits::pointer_to() @@ -199,6 +202,7 @@ // P1135R6 The C++20 Synchronization Library // P1207R4 Movability Of Single-Pass Iterators // (partially implemented) +// P1208R6 // P1209R0 erase_if(), erase() // P1227R2 Signed std::ssize(), Unsigned span::size() // P1243R4 Rangify New Algorithms @@ -211,6 +215,7 @@ // P1456R1 Move-Only Views // P1474R1 Helpful Pointers For contiguous_iterator // P1612R1 Relocating endian To +// P1614R2 Adding Spaceship <=> To The Library // P1645R1 constexpr For Algorithms // P1651R0 bind_front() Should Not Unwrap reference_wrapper // P1690R1 Refining Heterogeneous Lookup For Unordered Containers @@ -510,7 +515,7 @@ #define _CPPLIB_VER 650 #define _MSVC_STL_VERSION 142 -#define _MSVC_STL_UPDATE 202102L +#define _MSVC_STL_UPDATE 202103L #ifndef _ALLOW_COMPILER_AND_STL_VERSION_MISMATCH #ifdef __CUDACC__ @@ -526,8 +531,8 @@ #error STL1000: Unexpected compiler version, expected Clang 11.0.0 or newer. #endif // ^^^ old Clang ^^^ #elif defined(_MSC_VER) -#if _MSC_VER < 1928 // Coarse-grained, not inspecting _MSC_FULL_VER -#error STL1001: Unexpected compiler version, expected MSVC 19.28 or newer. +#if _MSC_VER < 1929 // Coarse-grained, not inspecting _MSC_FULL_VER +#error STL1001: Unexpected compiler version, expected MSVC 19.29 or newer. #endif // ^^^ old MSVC ^^^ #else // vvv other compilers vvv // not attempting to detect other compilers @@ -1198,10 +1203,15 @@ #define __cpp_lib_constexpr_dynamic_alloc 201907L #endif // __cpp_constexpr_dynamic_alloc -#define __cpp_lib_constexpr_functional 201907L -#define __cpp_lib_constexpr_iterator 201811L -#define __cpp_lib_constexpr_memory 201811L -#define __cpp_lib_constexpr_numeric 201911L +#define __cpp_lib_constexpr_functional 201907L +#define __cpp_lib_constexpr_iterator 201811L +#define __cpp_lib_constexpr_memory 201811L +#define __cpp_lib_constexpr_numeric 201911L + +#if defined(__cpp_constexpr_dynamic_alloc) && !defined(__clang__) // TRANSITION, LLVM-48606 +#define __cpp_lib_constexpr_string 201907L +#endif // defined(__cpp_constexpr_dynamic_alloc) && !defined(__clang__) + #define __cpp_lib_constexpr_string_view 201811L #define __cpp_lib_constexpr_tuple 201811L #define __cpp_lib_constexpr_utility 201811L @@ -1246,13 +1256,18 @@ #define __cpp_lib_semaphore 201907L #define __cpp_lib_shift 201806L #define __cpp_lib_smart_ptr_for_overwrite 202002L -#define __cpp_lib_span 202002L -#define __cpp_lib_ssize 201902L -#define __cpp_lib_starts_ends_with 201711L -#define __cpp_lib_syncbuf 201803L + +#ifdef __cpp_consteval +#define __cpp_lib_source_location 201907L +#endif // __cpp_consteval + +#define __cpp_lib_span 202002L +#define __cpp_lib_ssize 201902L +#define __cpp_lib_starts_ends_with 201711L +#define __cpp_lib_syncbuf 201803L #ifdef __cpp_lib_concepts // TRANSITION, GH-395 -#define __cpp_lib_three_way_comparison 201711L +#define __cpp_lib_three_way_comparison 201907L #endif // __cpp_lib_concepts #define __cpp_lib_to_address 201711L diff --git a/stl/msbuild/stl_base/stl.files.settings.targets b/stl/msbuild/stl_base/stl.files.settings.targets index b83c74b1e4f..d14a8bc528d 100644 --- a/stl/msbuild/stl_base/stl.files.settings.targets +++ b/stl/msbuild/stl_base/stl.files.settings.targets @@ -57,6 +57,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception $(CrtRoot)\github\stl\src\cthread.cpp; $(CrtRoot)\github\stl\src\mutex.cpp; $(CrtRoot)\github\stl\src\pplerror.cpp; + $(CrtRoot)\github\stl\src\ppltasks.cpp; $(CrtRoot)\github\stl\src\taskscheduler.cpp; $(CrtRoot)\github\stl\src\xnotify.cpp; $(CrtRoot)\github\stl\src\xtime.cpp; @@ -148,14 +149,6 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception nativecpp - - nativecpp - - true - - diff --git a/stl/src/memory_resource.cpp b/stl/src/memory_resource.cpp index 862b1e91f09..911ca06a2fd 100644 --- a/stl/src/memory_resource.cpp +++ b/stl/src/memory_resource.cpp @@ -59,7 +59,7 @@ namespace pmr { } // FUNCTION null_memory_resource - extern "C" _CRT_SATELLITE_1 _NODISCARD memory_resource* __cdecl null_memory_resource() noexcept { + extern "C" _NODISCARD _CRT_SATELLITE_1 memory_resource* __cdecl null_memory_resource() noexcept { class _Null_resource final : public _Identity_equal_resource { virtual void* do_allocate(size_t, size_t) override { // Sorry, OOM! _Xbad_alloc(); diff --git a/stl/src/special_math.cpp b/stl/src/special_math.cpp index 8a4dd5e0836..425e5da2c48 100644 --- a/stl/src/special_math.cpp +++ b/stl/src/special_math.cpp @@ -66,7 +66,7 @@ namespace { } // unnamed namespace _EXTERN_C -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_assoc_laguerre( +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_assoc_laguerre( const unsigned int _Pn, const unsigned int _Pm, const double _Px) noexcept { if (_STD isnan(_Px)) { return _Px; @@ -75,7 +75,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_assoc_laguerre( return _Boost_call([=] { return ::boost::math::laguerre(_Pn, _Pm, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_assoc_laguerref( +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_assoc_laguerref( const unsigned int _Pn, const unsigned int _Pm, const float _Px) noexcept { if (_STD isnan(_Px)) { return _Px; @@ -84,7 +84,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_assoc_laguerref( return _Boost_call([=] { return ::boost::math::laguerre(_Pn, _Pm, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_assoc_legendre( +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_assoc_legendre( const unsigned int _Pl, const unsigned int _Pm, const double _Px) noexcept { if (_STD isnan(_Px)) { return _Px; @@ -100,7 +100,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_assoc_legendre( }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_assoc_legendref( +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_assoc_legendref( const unsigned int _Pl, const unsigned int _Pm, const float _Px) noexcept { if (_STD isnan(_Px)) { return _Px; @@ -116,23 +116,23 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_assoc_legendref( }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_beta(const double _Px, const double _Py) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_beta(const double _Px, const double _Py) noexcept { return _Boost_call([=] { return ::boost::math::beta(_Px, _Py); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_betaf(const float _Px, const float _Py) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_betaf(const float _Px, const float _Py) noexcept { return _Boost_call([=] { return ::boost::math::beta(_Px, _Py); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_comp_ellint_1(const double _Pk) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_comp_ellint_1(const double _Pk) noexcept { return _Boost_call([=] { return ::boost::math::ellint_1(_Pk); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_comp_ellint_1f(const float _Pk) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_comp_ellint_1f(const float _Pk) noexcept { return _Boost_call([=] { return ::boost::math::ellint_1(_Pk); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_comp_ellint_2(const double _Pk) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_comp_ellint_2(const double _Pk) noexcept { if (_STD isnan(_Pk)) { return _Pk; } @@ -140,7 +140,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_comp_ellint_2(const doubl return _Boost_call([=] { return ::boost::math::ellint_2(_Pk); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_comp_ellint_2f(const float _Pk) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_comp_ellint_2f(const float _Pk) noexcept { if (_STD isnan(_Pk)) { return _Pk; } @@ -148,7 +148,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_comp_ellint_2f(const float return _Boost_call([=] { return ::boost::math::ellint_2(_Pk); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_comp_ellint_3(const double _Pk, const double _Pnu) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_comp_ellint_3(const double _Pk, const double _Pnu) noexcept { if (_STD isnan(_Pk)) { return _Pk; } @@ -160,7 +160,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_comp_ellint_3(const doubl return _Boost_call([=] { return ::boost::math::ellint_3(_Pk, _Pnu); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_comp_ellint_3f(const float _Pk, const float _Pnu) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_comp_ellint_3f(const float _Pk, const float _Pnu) noexcept { if (_STD isnan(_Pk)) { return _Pk; } @@ -172,7 +172,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_comp_ellint_3f(const float return _Boost_call([=] { return ::boost::math::ellint_3(_Pk, _Pnu); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_bessel_i(const double _Pnu, const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_cyl_bessel_i(const double _Pnu, const double _Px) noexcept { if (_STD isnan(_Pnu)) { return _Pnu; } @@ -184,7 +184,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_bessel_i(const double return _Boost_call([=] { return ::boost::math::cyl_bessel_i(_Pnu, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_bessel_if(const float _Pnu, const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_cyl_bessel_if(const float _Pnu, const float _Px) noexcept { if (_STD isnan(_Pnu)) { return _Pnu; } @@ -196,7 +196,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_bessel_if(const float return _Boost_call([=] { return ::boost::math::cyl_bessel_i(_Pnu, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_bessel_j(const double _Pnu, const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_cyl_bessel_j(const double _Pnu, const double _Px) noexcept { if (_STD isnan(_Pnu)) { return _Pnu; } @@ -208,7 +208,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_bessel_j(const double return _Boost_call([=] { return ::boost::math::cyl_bessel_j(_Pnu, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_bessel_jf(const float _Pnu, const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_cyl_bessel_jf(const float _Pnu, const float _Px) noexcept { if (_STD isnan(_Pnu)) { return _Pnu; } @@ -220,7 +220,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_bessel_jf(const float return _Boost_call([=] { return ::boost::math::cyl_bessel_j(_Pnu, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_bessel_k(const double _Pnu, const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_cyl_bessel_k(const double _Pnu, const double _Px) noexcept { if (_STD isnan(_Pnu)) { return _Pnu; } @@ -232,7 +232,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_bessel_k(const double return _Boost_call([=] { return ::boost::math::cyl_bessel_k(_Pnu, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_bessel_kf(const float _Pnu, const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_cyl_bessel_kf(const float _Pnu, const float _Px) noexcept { if (_STD isnan(_Pnu)) { return _Pnu; } @@ -244,7 +244,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_bessel_kf(const float return _Boost_call([=] { return ::boost::math::cyl_bessel_k(_Pnu, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_neumann(const double _Pnu, const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_cyl_neumann(const double _Pnu, const double _Px) noexcept { if (_STD isnan(_Pnu)) { return _Pnu; } @@ -256,7 +256,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_cyl_neumann(const double return _Boost_call([=] { return ::boost::math::cyl_neumann(_Pnu, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_neumannf(const float _Pnu, const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_cyl_neumannf(const float _Pnu, const float _Px) noexcept { if (_STD isnan(_Pnu)) { return _Pnu; } @@ -268,7 +268,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_cyl_neumannf(const float _ return _Boost_call([=] { return ::boost::math::cyl_neumann(_Pnu, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_ellint_1(const double _Pk, const double _Pphi) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_ellint_1(const double _Pk, const double _Pphi) noexcept { if (_STD isnan(_Pk)) { return _Pk; } @@ -280,7 +280,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_ellint_1(const double _Pk return _Boost_call([=] { return ::boost::math::ellint_1(_Pk, _Pphi); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_ellint_1f(const float _Pk, const float _Pphi) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_ellint_1f(const float _Pk, const float _Pphi) noexcept { if (_STD isnan(_Pk)) { return _Pk; } @@ -292,7 +292,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_ellint_1f(const float _Pk, return _Boost_call([=] { return ::boost::math::ellint_1(_Pk, _Pphi); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_ellint_2(const double _Pk, const double _Pphi) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_ellint_2(const double _Pk, const double _Pphi) noexcept { if (_STD isnan(_Pk)) { return _Pk; } @@ -304,7 +304,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_ellint_2(const double _Pk return _Boost_call([=] { return ::boost::math::ellint_2(_Pk, _Pphi); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_ellint_2f(const float _Pk, const float _Pphi) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_ellint_2f(const float _Pk, const float _Pphi) noexcept { if (_STD isnan(_Pk)) { return _Pk; } @@ -316,7 +316,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_ellint_2f(const float _Pk, return _Boost_call([=] { return ::boost::math::ellint_2(_Pk, _Pphi); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_ellint_3( +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_ellint_3( const double _Pk, const double _Pnu, const double _Pphi) noexcept { if (_STD isnan(_Pk)) { return _Pk; @@ -333,7 +333,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_ellint_3( return _Boost_call([=] { return ::boost::math::ellint_3(_Pk, _Pnu, _Pphi); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_ellint_3f( +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_ellint_3f( const float _Pk, const float _Pnu, const float _Pphi) noexcept { if (_STD isnan(_Pk)) { return _Pk; @@ -350,7 +350,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_ellint_3f( return _Boost_call([=] { return ::boost::math::ellint_3(_Pk, _Pnu, _Pphi); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_expint(const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_expint(const double _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -358,7 +358,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_expint(const double _Px) return _Boost_call([=] { return ::boost::math::expint(_Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_expintf(const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_expintf(const float _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -366,7 +366,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_expintf(const float _Px) n return _Boost_call([=] { return ::boost::math::expint(_Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_hermite(const unsigned int _Pn, const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_hermite(const unsigned int _Pn, const double _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -374,7 +374,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_hermite(const unsigned in return _Boost_call([=] { return ::boost::math::hermite(_Pn, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_hermitef(const unsigned int _Pn, const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_hermitef(const unsigned int _Pn, const float _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -382,7 +382,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_hermitef(const unsigned in return _Boost_call([=] { return ::boost::math::hermite(_Pn, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_laguerre(const unsigned int _Pn, const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_laguerre(const unsigned int _Pn, const double _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -390,7 +390,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_laguerre(const unsigned i return _Boost_call([=] { return ::boost::math::laguerre(_Pn, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_laguerref(const unsigned int _Pn, const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_laguerref(const unsigned int _Pn, const float _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -398,7 +398,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_laguerref(const unsigned i return _Boost_call([=] { return ::boost::math::laguerre(_Pn, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_legendre(const unsigned int _Pl, const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_legendre(const unsigned int _Pl, const double _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -406,7 +406,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_legendre(const unsigned i return _Boost_call([=] { return ::boost::math::legendre_p(_Pl, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_legendref(const unsigned int _Pl, const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_legendref(const unsigned int _Pl, const float _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -414,7 +414,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_legendref(const unsigned i return _Boost_call([=] { return ::boost::math::legendre_p(_Pl, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_riemann_zeta(const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_riemann_zeta(const double _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -422,7 +422,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_riemann_zeta(const double return _Boost_call([=] { return ::boost::math::zeta(_Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_riemann_zetaf(const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_riemann_zetaf(const float _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -430,7 +430,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_riemann_zetaf(const float return _Boost_call([=] { return ::boost::math::zeta(_Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_sph_bessel(const unsigned int _Pn, const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_sph_bessel(const unsigned int _Pn, const double _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -438,7 +438,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_sph_bessel(const unsigned return _Boost_call([=] { return ::boost::math::sph_bessel(_Pn, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_sph_besself(const unsigned int _Pn, const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_sph_besself(const unsigned int _Pn, const float _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -446,7 +446,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_sph_besself(const unsigned return _Boost_call([=] { return ::boost::math::sph_bessel(_Pn, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_sph_legendre( +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_sph_legendre( const unsigned int _Pl, const unsigned int _Pm, const double _Ptheta) noexcept { if (_STD isnan(_Ptheta)) { return _Ptheta; @@ -455,7 +455,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_sph_legendre( return _Boost_call([=] { return ::boost::math::spherical_harmonic_r(_Pl, _Pm, _Ptheta, 0.0); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_sph_legendref( +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_sph_legendref( const unsigned int _Pl, const unsigned int _Pm, const float _Ptheta) noexcept { if (_STD isnan(_Ptheta)) { return _Ptheta; @@ -464,7 +464,7 @@ _CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_sph_legendref( return _Boost_call([=] { return ::boost::math::spherical_harmonic_r(_Pl, _Pm, _Ptheta, 0.0f); }); } -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_sph_neumann(const unsigned int _Pn, const double _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_sph_neumann(const unsigned int _Pn, const double _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -472,7 +472,7 @@ _CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_sph_neumann(const unsigne return _Boost_call([=] { return ::boost::math::sph_neumann(_Pn, _Px); }); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_sph_neumannf(const unsigned int _Pn, const float _Px) noexcept { +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_sph_neumannf(const unsigned int _Pn, const float _Px) noexcept { if (_STD isnan(_Px)) { return _Px; } @@ -515,12 +515,12 @@ namespace { } // unnamed namespace _EXTERN_C -_CRT_SATELLITE_2 _NODISCARD double __stdcall __std_smf_hypot3( +_NODISCARD _CRT_SATELLITE_2 double __stdcall __std_smf_hypot3( const double _Dx, const double _Dy, const double _Dz) noexcept { return _Hypot3(_Dx, _Dy, _Dz); } -_CRT_SATELLITE_2 _NODISCARD float __stdcall __std_smf_hypot3f( +_NODISCARD _CRT_SATELLITE_2 float __stdcall __std_smf_hypot3f( const float _Dx, const float _Dy, const float _Dz) noexcept { return _Hypot3(_Dx, _Dy, _Dz); } diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index a2496d683f3..229c4d2141e 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -320,9 +320,6 @@ std/language.support/support.limits/support.limits.general/functional.version.pa std/language.support/support.limits/support.limits.general/iterator.version.pass.cpp FAIL std/language.support/support.limits/support.limits.general/memory.version.pass.cpp FAIL -# C++20 P1614R2 "Adding Spaceship <=> To The Library" -std/language.support/support.limits/support.limits.general/compare.version.pass.cpp FAIL - # C++23 P1048R1 "is_scoped_enum" std/utilities/meta/meta.unary/meta.unary.prop/is_scoped_enum.pass.cpp FAIL @@ -370,16 +367,6 @@ std/utilities/utility/pairs/pairs.pair/assign_rv_pair_U_V.pass.cpp:0 FAIL 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 -# VSO-1275530 "[constexpr] Temporaries created for functions do not have a ctor called (and have a redundant dtor)." -std/numerics/numeric.ops/exclusive.scan/exclusive_scan.pass.cpp:0 FAIL -std/numerics/numeric.ops/exclusive.scan/exclusive_scan_init_op.pass.cpp:0 FAIL -std/numerics/numeric.ops/inclusive.scan/inclusive_scan.pass.cpp:0 FAIL -std/numerics/numeric.ops/inclusive.scan/inclusive_scan_op.pass.cpp:0 FAIL -std/numerics/numeric.ops/inclusive.scan/inclusive_scan_op_init.pass.cpp:0 FAIL -std/numerics/numeric.ops/transform.exclusive.scan/transform_exclusive_scan_init_bop_uop.pass.cpp:0 FAIL -std/numerics/numeric.ops/transform.inclusive.scan/transform_inclusive_scan_bop_uop.pass.cpp:0 FAIL -std/numerics/numeric.ops/transform.inclusive.scan/transform_inclusive_scan_bop_uop_init.pass.cpp:0 FAIL - # *** CLANG COMPILER BUGS *** # LLVM-33230 "Clang on Windows should define __STDCPP_THREADS__ to be 1" @@ -671,6 +658,18 @@ std/utilities/memory/util.smartptr/util.smartptr.shared/libcxx.control_block_lay # Non-Standard assumption that std::filesystem::file_time_type::duration::period is std::nano std/input.output/filesystems/fs.filesystem.synopsis/file_time_type_resolution.compile.pass.cpp FAIL +# P1614R2 "Adding Spaceship <=> To The Library" makes `std::operator!=(i1, i3)` an error +std/iterators/stream.iterators/istream.iterator/istream.iterator.ops/equal.pass.cpp FAIL + +# Uses-allocator class constructor wants non-const allocator ref and mismatched piecewise_construct's value category +std/utilities/allocator.adaptor/allocator.adaptor.members/construct.pass.cpp FAIL +std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair.pass.cpp FAIL +std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_const_lvalue_pair.pass.cpp FAIL +std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_piecewise.pass.cpp FAIL +std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_rvalue.pass.cpp FAIL +std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_values.pass.cpp FAIL +std/utilities/allocator.adaptor/allocator.adaptor.members/construct_type.pass.cpp FAIL + # *** LIKELY STL BUGS *** # Not yet analyzed, likely STL bugs. Assertions and other runtime failures. diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index 2760e3e0b3c..a8fe00a48f7 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -320,9 +320,6 @@ language.support\support.limits\support.limits.general\functional.version.pass.c language.support\support.limits\support.limits.general\iterator.version.pass.cpp language.support\support.limits\support.limits.general\memory.version.pass.cpp -# C++20 P1614R2 "Adding Spaceship <=> To The Library" -language.support\support.limits\support.limits.general\compare.version.pass.cpp - # C++23 P1048R1 "is_scoped_enum" utilities\meta\meta.unary\meta.unary.prop\is_scoped_enum.pass.cpp @@ -370,16 +367,6 @@ utilities\utility\pairs\pairs.pair\assign_rv_pair_U_V.pass.cpp algorithms\alg.sorting\alg.sort\partial.sort\partial_sort.pass.cpp algorithms\alg.sorting\alg.sort\partial.sort\partial_sort_comp.pass.cpp -# VSO-1275530 "[constexpr] Temporaries created for functions do not have a ctor called (and have a redundant dtor)." -numerics\numeric.ops\exclusive.scan\exclusive_scan.pass.cpp -numerics\numeric.ops\exclusive.scan\exclusive_scan_init_op.pass.cpp -numerics\numeric.ops\inclusive.scan\inclusive_scan.pass.cpp -numerics\numeric.ops\inclusive.scan\inclusive_scan_op.pass.cpp -numerics\numeric.ops\inclusive.scan\inclusive_scan_op_init.pass.cpp -numerics\numeric.ops\transform.exclusive.scan\transform_exclusive_scan_init_bop_uop.pass.cpp -numerics\numeric.ops\transform.inclusive.scan\transform_inclusive_scan_bop_uop.pass.cpp -numerics\numeric.ops\transform.inclusive.scan\transform_inclusive_scan_bop_uop_init.pass.cpp - # *** CLANG COMPILER BUGS *** # LLVM-33230 "Clang on Windows should define __STDCPP_THREADS__ to be 1" @@ -671,6 +658,18 @@ utilities\memory\util.smartptr\util.smartptr.shared\libcxx.control_block_layout. # Non-Standard assumption that std::filesystem::file_time_type::duration::period is std::nano input.output\filesystems\fs.filesystem.synopsis\file_time_type_resolution.compile.pass.cpp +# P1614R2 "Adding Spaceship <=> To The Library" makes `std::operator!=(i1, i3)` an error +iterators\stream.iterators\istream.iterator\istream.iterator.ops\equal.pass.cpp + +# Uses-allocator class constructor wants non-const allocator ref and mismatched piecewise_construct's value category +utilities\allocator.adaptor\allocator.adaptor.members\construct.pass.cpp +utilities\allocator.adaptor\allocator.adaptor.members\construct_pair.pass.cpp +utilities\allocator.adaptor\allocator.adaptor.members\construct_pair_const_lvalue_pair.pass.cpp +utilities\allocator.adaptor\allocator.adaptor.members\construct_pair_piecewise.pass.cpp +utilities\allocator.adaptor\allocator.adaptor.members\construct_pair_rvalue.pass.cpp +utilities\allocator.adaptor\allocator.adaptor.members\construct_pair_values.pass.cpp +utilities\allocator.adaptor\allocator.adaptor.members\construct_type.pass.cpp + # *** LIKELY STL BUGS *** # Not yet analyzed, likely STL bugs. Assertions and other runtime failures. diff --git a/tests/std/include/test_death.hpp b/tests/std/include/test_death.hpp index 943b0f28a02..296a0f69012 100644 --- a/tests/std/include/test_death.hpp +++ b/tests/std/include/test_death.hpp @@ -79,9 +79,16 @@ namespace std_testing { // buffer was not big enough const size_t str_max_size = result.max_size(); const size_t result_max_size = str_max_size - str_max_size / 2; +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif // __clang__ if (result_size >= result_max_size) { api_unexpected("GetModuleFileNameW"); } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif // __clang__ result.resize(result_size + result_size / 2); } else if (result_size == 0) { diff --git a/tests/std/test.lst b/tests/std/test.lst index ca60f536b2f..e5866c93af1 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -99,7 +99,7 @@ tests\Dev11_0000000_user_defined_literals tests\Dev11_0019127_singular_iterators tests\Dev11_0091392_string_erase_resize_perf tests\Dev11_0133625_locale0_implib_cpp -tests\Dev11_0135139_vector_bool_equality_perf +tests\Dev11_0135139_vector_bool_comparisons tests\Dev11_0235721_async_and_packaged_task tests\Dev11_0253803_debug_pointer tests\Dev11_0272959_make_signed @@ -243,6 +243,7 @@ tests\P0415R1_constexpr_complex tests\P0426R1_constexpr_char_traits tests\P0433R2_deduction_guides tests\P0466R5_layout_compatibility_and_pointer_interconvertibility_traits +tests\P0475R1_P0591R4_uses_allocator_construction tests\P0476R2_bit_cast tests\P0487R1_fixing_operator_shl_basic_istream_char_pointer tests\P0513R0_poisoning_the_hash @@ -373,6 +374,7 @@ tests\P0896R4_views_elements tests\P0896R4_views_empty tests\P0896R4_views_filter tests\P0896R4_views_filter_death +tests\P0896R4_views_iota tests\P0896R4_views_reverse tests\P0896R4_views_single tests\P0896R4_views_take @@ -385,6 +387,7 @@ tests\P0898R3_identity tests\P0912R5_coroutine tests\P0919R3_heterogeneous_unordered_lookup tests\P0966R1_string_reserve_should_not_shrink +tests\P0980R1_constexpr_strings tests\P1004R2_constexpr_vector tests\P1004R2_constexpr_vector_bool tests\P1007R3_assume_aligned @@ -398,6 +401,7 @@ tests\P1135R6_barrier tests\P1135R6_latch tests\P1135R6_semaphore tests\P1165R1_consistently_propagating_stateful_allocators +tests\P1208R6_source_location tests\P1423R3_char8_t_remediation tests\P1502R1_standard_library_header_units tests\P1614R2_spaceship diff --git a/tests/std/tests/Dev11_0135139_vector_bool_equality_perf/env.lst b/tests/std/tests/Dev11_0135139_vector_bool_comparisons/env.lst similarity index 100% rename from tests/std/tests/Dev11_0135139_vector_bool_equality_perf/env.lst rename to tests/std/tests/Dev11_0135139_vector_bool_comparisons/env.lst diff --git a/tests/std/tests/Dev11_0135139_vector_bool_comparisons/test.cpp b/tests/std/tests/Dev11_0135139_vector_bool_comparisons/test.cpp new file mode 100644 index 00000000000..43dfd67165b --- /dev/null +++ b/tests/std/tests/Dev11_0135139_vector_bool_comparisons/test.cpp @@ -0,0 +1,162 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +using namespace std; + +vector vb_from_str(const char* str) { + vector vb; + + for (; *str != '\0'; ++str) { + assert(*str == '0' || *str == '1'); + + vb.push_back(*str == '1'); + } + + return vb; +} + +enum class Ordering : int { Less = -1, Equal = 0, Greater = 1 }; + +constexpr Ordering Lt = Ordering::Less; +constexpr Ordering Eq = Ordering::Equal; +constexpr Ordering Gt = Ordering::Greater; + +void test_comparison(const char* const left_str, const char* const right_str, const Ordering order) { + const auto left = vb_from_str(left_str); + const auto right = vb_from_str(right_str); + +#ifdef __cpp_lib_concepts + assert((left <=> right) == (static_cast(order) <=> 0)); + assert((right <=> left) == (0 <=> static_cast(order))); +#endif // __cpp_lib_concepts + + switch (order) { + case Lt: + assert(!(left == right)); + assert(!(right == left)); + assert(left < right); + assert(!(right < left)); + break; + case Eq: + assert(left == right); + assert(right == left); + assert(!(left < right)); + assert(!(right < left)); + break; + case Gt: + assert(!(left == right)); + assert(!(right == left)); + assert(!(left < right)); + assert(right < left); + break; + default: + assert(false); + break; + } +} + +int main() { + + { + mt19937 eng(1729); + + uniform_int_distribution dist(0, 1); + + const size_t N = 137; + + vector x(N); + vector y(N); + + for (size_t i = 0; i < N; ++i) { + const bool b = dist(eng) != 0; + + x[i] = b; + y[i] = b; + } + + assert(x == y); + + y.push_back(0); + + assert(x != y); + + y.pop_back(); + + assert(x == y); + + y.push_back(1); + + assert(x != y); + + y.pop_back(); + + assert(x == y); + + x.back().flip(); + + assert(x != y); + + y.back().flip(); + + assert(x == y); + } + + { + // Also test DevDiv-850453 ": Missing emplace methods in std::vector container". + + vector v(47, allocator()); + + v.emplace_back(make_shared(123)); + v.emplace_back(shared_ptr()); + + v.emplace(v.cbegin(), make_shared(3.14)); + v.emplace(v.cbegin(), make_unique(456)); + v.emplace(v.cbegin(), shared_ptr()); + v.emplace(v.cbegin(), unique_ptr()); + v.emplace(v.cbegin(), unique_ptr()); + + + vector correct; + + correct.insert(correct.cend(), 3, false); + correct.insert(correct.cend(), 2, true); + correct.insert(correct.cend(), 47, false); + correct.insert(correct.cend(), 1, true); + correct.insert(correct.cend(), 1, false); + + assert(v == correct); + } + + // Also test GH-1046 optimizing vector spaceship and less-than comparisons. + test_comparison("", "", Eq); // both empty + test_comparison("", "00", Lt); // empty vs. partial word + test_comparison("", "01", Lt); + test_comparison("", "00000000000000000000000000000000", Lt); // empty vs. full word + test_comparison("", "01000000000000000000000000000000", Lt); + test_comparison("", "0000000000000000000000000000000000", Lt); // empty vs. full and partial words + test_comparison("", "0100000000000000000000000000000001", Lt); + + test_comparison("010111010", "010111010", Eq); + test_comparison("010111010", "011110010", Lt); // test that bits are compared in the correct direction + + // same test, after an initial matching word + test_comparison("00001111000011110000111100001111010111010", "00001111000011110000111100001111010111010", Eq); + test_comparison("00001111000011110000111100001111010111010", "00001111000011110000111100001111011110010", Lt); + + test_comparison("00001111", "00001111", Eq); + test_comparison("00001111", "000011110", Lt); // matching prefixes, test size comparison + test_comparison("00001111", "000011111", Lt); + test_comparison("00001111", "00001111000000000000000000000000", Lt); // full word + test_comparison("00001111", "00001111000000000000000000000001", Lt); + test_comparison("00001111", "0000111100000000000000000000000000", Lt); // full and partial words + test_comparison("00001111", "0000111100000000000000000000000001", Lt); + + test_comparison("10", "01111", Gt); // shorter but greater + test_comparison("10", "01111111111111111111111111111111", Gt); // full word + test_comparison("10", "0111111111111111111111111111111111", Gt); // full and partial words +} diff --git a/tests/std/tests/Dev11_0135139_vector_bool_equality_perf/test.cpp b/tests/std/tests/Dev11_0135139_vector_bool_equality_perf/test.cpp deleted file mode 100644 index 2aa72a7adaf..00000000000 --- a/tests/std/tests/Dev11_0135139_vector_bool_equality_perf/test.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include -#include -#include -#include - -using namespace std; - -int main() { - - { - mt19937 eng(1729); - - uniform_int_distribution dist(0, 1); - - const size_t N = 137; - - vector x(N); - vector y(N); - - for (size_t i = 0; i < N; ++i) { - const bool b = dist(eng) != 0; - - x[i] = b; - y[i] = b; - } - - assert(x == y); - - y.push_back(0); - - assert(x != y); - - y.pop_back(); - - assert(x == y); - - y.push_back(1); - - assert(x != y); - - y.pop_back(); - - assert(x == y); - - x.back().flip(); - - assert(x != y); - - y.back().flip(); - - assert(x == y); - } - - { - // Also test DevDiv-850453 ": Missing emplace methods in std::vector container". - - vector v(47, allocator()); - - v.emplace_back(make_shared(123)); - v.emplace_back(shared_ptr()); - - v.emplace(v.cbegin(), make_shared(3.14)); - v.emplace(v.cbegin(), make_unique(456)); - v.emplace(v.cbegin(), shared_ptr()); - v.emplace(v.cbegin(), unique_ptr()); - v.emplace(v.cbegin(), unique_ptr()); - - - vector correct; - - correct.insert(correct.cend(), 3, false); - correct.insert(correct.cend(), 2, true); - correct.insert(correct.cend(), 47, false); - correct.insert(correct.cend(), 1, true); - correct.insert(correct.cend(), 1, false); - - assert(v == correct); - } -} diff --git a/tests/std/tests/Dev11_0836436_get_time/test.cpp b/tests/std/tests/Dev11_0836436_get_time/test.cpp index 2d704e204dd..d28890d0014 100644 --- a/tests/std/tests/Dev11_0836436_get_time/test.cpp +++ b/tests/std/tests/Dev11_0836436_get_time/test.cpp @@ -494,6 +494,64 @@ void test_990695() { assert(t.tm_min == 8); assert(t.tm_sec == 0); } + + { + // Should fail if EOF while not parsing specifier (N4878 [locale.time.get.members]/8.3). + tm t{}; + istringstream iss("4"); + iss >> get_time(&t, "42"); + assert(iss.rdstate() == (ios_base::eofbit | ios_base::failbit)); + } + + { + // Trailing % should not be treated as a literal (N4878 [locale.time.get.members]/8.4). + tm t{}; + istringstream iss("%"); + iss >> get_time(&t, "%"); + assert(iss.fail()); + } + + { + // % with modifier but no specifier is also incomplete. + tm t{}; + istringstream iss("%E"); + iss >> get_time(&t, "%E"); + assert(iss.fail()); + } + + { + // Literal match is case-insensitive (N4878 [locale.time.get.members]/8.6). + tm t{}; + istringstream iss("aBc"); + iss >> get_time(&t, "AbC"); + assert(iss); + } + + { + // GH-1606: reads too many leading zeros + istringstream iss("19700405T000006"); + tm t{}; + iss >> get_time(&t, "%Y%m%dT%H%M%S"); + assert(iss); + + printf("Expected hour 0, min 0, sec 6\n"); + printf(" Got hour %d, min %d, sec %d\n", t.tm_hour, t.tm_min, t.tm_sec); + + assert(t.tm_year == 70); + assert(t.tm_mon == 3); + assert(t.tm_mday == 5); + assert(t.tm_hour == 0); + assert(t.tm_min == 0); + assert(t.tm_sec == 6); + } + + { + // strptime specification: "leading zeros are permitted but not required" + tm t{}; + istringstream{" 7 4"} >> get_time(&t, "%m%d"); + assert(t.tm_mon == 6); + assert(t.tm_mday == 4); + } } } diff --git a/tests/std/tests/P0067R5_charconv/test.cpp b/tests/std/tests/P0067R5_charconv/test.cpp index 466a8d72d7c..4201ccf9856 100644 --- a/tests/std/tests/P0067R5_charconv/test.cpp +++ b/tests/std/tests/P0067R5_charconv/test.cpp @@ -101,7 +101,8 @@ void initialize_randomness(mt19937_64& mt64, const int argc, char** const argv) puts("Successfully seeded mt64. First three values:"); for (int i = 0; i < 3; ++i) { - printf("0x%016llX\n", mt64()); + // libc++ uses long for 64-bit values. + printf("0x%016llX\n", static_cast(mt64())); } } @@ -575,7 +576,8 @@ void assert_message_bits(const bool b, const char* const msg, const uint32_t bit void assert_message_bits(const bool b, const char* const msg, const uint64_t bits) { if (!b) { - fprintf(stderr, "%s failed for 0x%016llX\n", msg, bits); + // libc++ uses long for 64-bit values. + fprintf(stderr, "%s failed for 0x%016llX\n", msg, static_cast(bits)); fprintf(stderr, "This is a randomized test.\n"); fprintf(stderr, "DO NOT IGNORE/RERUN THIS FAILURE.\n"); fprintf(stderr, "You must report it to the STL maintainers.\n"); @@ -1098,8 +1100,8 @@ int main(int argc, char** argv) { printf("Total time: %lld ms\n", ms); if (ms < 3'000) { - puts("That was fast. Consider retuning PrefixesToTest and FractionBits."); + puts("That was fast. Consider tuning PrefixesToTest and FractionBits to test more cases."); } else if (ms > 30'000) { - puts("That was slow. Consider retuning PrefixesToTest and FractionBits."); + puts("That was slow. Consider tuning PrefixesToTest and FractionBits to test fewer cases."); } } diff --git a/tests/std/tests/P0218R1_filesystem/test.cpp b/tests/std/tests/P0218R1_filesystem/test.cpp index 610e1003b8b..c1ac9a46ccf 100644 --- a/tests/std/tests/P0218R1_filesystem/test.cpp +++ b/tests/std/tests/P0218R1_filesystem/test.cpp @@ -3924,7 +3924,7 @@ int wmain(int argc, wchar_t* argv[]) { if (argc > 1) { run_interactive_tests(argc, argv); - return 0; // not a PM_ constant because the caller isn't run.pl here + return 0; } for (const auto& testCase : decompTestCases) { diff --git a/tests/std/tests/P0220R1_searchers/test.cpp b/tests/std/tests/P0220R1_searchers/test.cpp index 0551e3d6477..a0cd15d29b7 100644 --- a/tests/std/tests/P0220R1_searchers/test.cpp +++ b/tests/std/tests/P0220R1_searchers/test.cpp @@ -433,7 +433,7 @@ void test_case_randomized_cases() { if (elapsed > 10s) { cout << "test_case_randomized_cases() took " << duration_cast(elapsed).count() << " ms.\n"; - cout << "Consider retuning Needles and Haystacks.\n"; + cout << "Consider tuning Needles and Haystacks to test fewer cases.\n"; } } diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_dates/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_dates/test.cpp index 4bb31c8a863..6a59c863a07 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_dates/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_dates/test.cpp @@ -967,6 +967,7 @@ constexpr void year_month_day_last_test() { if (is_constant_evaluated()) { static_assert((2020y / 1 / last).ok()); static_assert(!(2020y / 13 / last).ok()); + static_assert(!(2020y / 0 / last).day().ok()); // implementation-specific assumption, see GH-1647 } else { for (int iy = y_min; iy <= y_max; ++iy) { for (auto m = 0u; m <= 255u; ++m) { @@ -976,6 +977,7 @@ constexpr void year_month_day_last_test() { assert((y / mdl).ok()); } else { assert(!(y / mdl).ok()); + assert((y / mdl).day().ok() || (y / mdl).day() == day{255}); // implementation-specific assumption } } } diff --git a/tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/env.lst b/tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/test.cpp b/tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/test.cpp new file mode 100644 index 00000000000..8c5924c5412 --- /dev/null +++ b/tests/std/tests/P0475R1_P0591R4_uses_allocator_construction/test.cpp @@ -0,0 +1,181 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +using namespace std; + +void test_P0475R1() { + struct DoNotCopy { + DoNotCopy() = default; + DoNotCopy(const DoNotCopy&) { + assert(false); + } + }; + + struct X { + using allocator_type = allocator; + X(DoNotCopy&&, const allocator_type&) {} + }; + + scoped_allocator_adaptor>> alloc; + auto ptr = alloc.allocate(1); + alloc.construct(ptr, piecewise_construct, tuple{}, make_tuple(1)); + alloc.destroy(ptr); + alloc.deallocate(ptr, 1); +} + +constexpr bool test_P0591R4() { + allocator alloc; + int i = 5; + pair p(i, i); + + struct AllocatorArgConstructible { + using allocator_type = allocator; + + constexpr AllocatorArgConstructible(allocator_arg_t, const allocator&, int y) : x(y) {} + + int x; + }; + + struct AllocatorConstructible { + using allocator_type = allocator; + + constexpr AllocatorConstructible(int y, const allocator&) : x(y) {} + + int x; + }; + + struct OnlyAllocatorArgConstructible { + using allocator_type = allocator; + + constexpr OnlyAllocatorArgConstructible(allocator_arg_t, const allocator&) {} + }; + + struct OnlyAllocatorConstructible { + using allocator_type = allocator; + + constexpr OnlyAllocatorConstructible(const allocator&) {} + }; + + struct DefaultConstructible { + constexpr DefaultConstructible() {} + }; + + using AllocatorArgConstructArgs = tuple&, int&>; + using AllocatorConstructArgs = tuple&>; + using ConstAllocatorArgConstructArgs = tuple&, const int&>; + using ConstAllocatorConstructArgs = tuple&>; + using MovedAllocatorArgConstructArgs = tuple&, int&&>; + using MovedAllocatorConstructArgs = tuple&>; + using OnlyAllocatorArgConstructArgs = tuple&>; + using OnlyAllocatorConstructArgs = tuple&>; + using DefaultConstructArgs = tuple<>; + + { // non-pair overload + auto tuple1 = uses_allocator_construction_args(alloc, i); + static_assert(is_same_v>); + + auto tuple2 = uses_allocator_construction_args(alloc, i); + static_assert(is_same_v); + + auto tuple3 = uses_allocator_construction_args(alloc, i); + static_assert(is_same_v); + } + + { // pair(piecewise_construct_t, tuple, tuple) overload + auto tuple4 = uses_allocator_construction_args>( + alloc, piecewise_construct, forward_as_tuple(i), forward_as_tuple()); + static_assert( + is_same_v, OnlyAllocatorArgConstructArgs>>); + + auto tuple5 = uses_allocator_construction_args>( + alloc, piecewise_construct, forward_as_tuple(i), forward_as_tuple()); + static_assert( + is_same_v>); + } + + { // pair() overload + auto tuple6 = + uses_allocator_construction_args>(alloc); + static_assert(is_same_v>); + + auto tuple7 = uses_allocator_construction_args>(alloc); + static_assert(is_same_v>); + } + + { // pair(first, second) overload + auto tuple8 = uses_allocator_construction_args>(alloc, i, i); + static_assert( + is_same_v, AllocatorArgConstructArgs>>); + + auto tuple9 = uses_allocator_construction_args>(alloc, i, i); + static_assert(is_same_v>>); + } + + { // pair(const pair&) overload + auto tuple10 = uses_allocator_construction_args>(alloc, p); + static_assert(is_same_v, ConstAllocatorArgConstructArgs>>); + + auto tuple11 = uses_allocator_construction_args>(alloc, p); + static_assert( + is_same_v>>); + } + + { // pair(pair&&) overload + auto tuple12 = uses_allocator_construction_args>(alloc, move(p)); + static_assert( + is_same_v, MovedAllocatorArgConstructArgs>>); + + auto tuple13 = uses_allocator_construction_args>(alloc, move(p)); + static_assert( + is_same_v>>); + } + + { + auto obj1 = make_obj_using_allocator(alloc, i); + static_assert(is_same_v); + assert(obj1.x == i); + + auto obj2 = make_obj_using_allocator(alloc, i); + static_assert(is_same_v); + assert(obj2.x == i); + } + + { + allocator alloc2; + auto ptr2 = alloc2.allocate(1); + + uninitialized_construct_using_allocator(ptr2, alloc, i); + assert(ptr2->x == i); + destroy_at(ptr2); + + alloc2.deallocate(ptr2, 1); + + allocator alloc3; + auto ptr3 = alloc3.allocate(1); + + uninitialized_construct_using_allocator(ptr3, alloc, i); + assert(ptr3->x == i); + destroy_at(ptr3); + + alloc3.deallocate(ptr3, 1); + } + + return true; +} + +int main() { + test_P0475R1(); + + assert(test_P0591R4()); + static_assert(test_P0591R4()); +} diff --git a/tests/std/tests/P0784R7_library_machinery/test.cpp b/tests/std/tests/P0784R7_library_machinery/test.cpp index 80293a7a9ca..3fde728f335 100644 --- a/tests/std/tests/P0784R7_library_machinery/test.cpp +++ b/tests/std/tests/P0784R7_library_machinery/test.cpp @@ -127,7 +127,7 @@ _CONSTEXPR20_DYNALLOC bool test() { } #endif // __cpp_lib_concepts -#if defined(__cpp_constexpr_dynamic_alloc) && (defined(__clang__) || defined(__EDG__)) // TRANSITION, DevCom-1333853 +#if defined(__cpp_constexpr_dynamic_alloc) { // _Uninitialized_move_unchecked int_wrapper_move input[] = {1, 2, 3, 4}; int_wrapper_move output[4]; @@ -140,7 +140,7 @@ _CONSTEXPR20_DYNALLOC bool test() { assert(equal(begin(input), end(input), begin(expected_after_move), end(expected_after_move))); } } -#endif // defined(__cpp_constexpr_dynamic_alloc) && (defined(__clang__) || defined(__EDG__)) +#endif // defined(__cpp_constexpr_dynamic_alloc) return true; } diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy/test.cpp index 38c0d687d54..0199745aa45 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy/test.cpp @@ -203,7 +203,7 @@ struct memcpy_test { static constexpr int expected_input_long[] = {13, 55, 12345, 42}; static void call() { - { // Validate only range overload + { // Validate matching ranges int input[] = {13, 55, 12345}; int output[] = {-1, -1, -1}; @@ -235,6 +235,30 @@ struct memcpy_test { assert(ranges::equal(input, expected_input_long)); assert(ranges::equal(output, expected_output)); } + + { // Validate non-common input range + int input[] = {13, 55, 12345}; + int output[] = {-1, -1, -1}; + + const auto result = + ranges::uninitialized_copy(begin(input), unreachable_sentinel, begin(output), end(output)); + assert(result.in == end(input)); + assert(result.out == end(output)); + assert(ranges::equal(input, expected_input)); + assert(ranges::equal(output, expected_output)); + } + + { // Validate non-common output range + int input[] = {13, 55, 12345}; + int output[] = {-1, -1, -1}; + + const auto result = + ranges::uninitialized_copy(begin(input), end(input), begin(output), unreachable_sentinel); + assert(result.in == end(input)); + assert(result.out == end(output)); + assert(ranges::equal(input, expected_input)); + assert(ranges::equal(output, expected_output)); + } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill/test.cpp index 2c7801a5348..a04fe76cdb9 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill/test.cpp @@ -141,6 +141,20 @@ struct throwing_test { } }; +struct memset_test { + static constexpr unsigned char expected[] = {42, 42, 42}; + + static void call() { + { // Validate only range overload + unsigned char input[3]; + + const auto result = ranges::uninitialized_fill(input, static_cast(42)); + assert(result == end(input)); + assert(ranges::equal(input, expected)); + } + } +}; + using test_range = test::range; @@ -150,4 +164,5 @@ int main() { instantiator::call(); throwing_test::call(); + memset_test::call(); } diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill_n/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill_n/test.cpp index 4d0be9f186c..87faa500d0e 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill_n/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_fill_n/test.cpp @@ -120,6 +120,20 @@ struct throwing_test { } }; +struct memset_test { + static constexpr unsigned char expected[] = {42, 42, 42}; + + static void call() { + { // Validate only range overload + unsigned char input[3]; + + const auto result = ranges::uninitialized_fill_n(input, 3, static_cast(42)); + assert(result == end(input)); + assert(ranges::equal(input, expected)); + } + } +}; + using test_range = test::range; @@ -129,4 +143,5 @@ int main() { instantiator::call(); throwing_test::call(); + memset_test::call(); } diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_move/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_move/test.cpp index 865dcffcccf..45adaeecf7e 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_move/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_move/test.cpp @@ -204,11 +204,13 @@ struct memcpy_test { static constexpr int expected_input_long[] = {13, 55, 12345, 42}; static void call() { - { // Validate range + { // Validate matching ranges int input[] = {13, 55, 12345}; int output[] = {-1, -1, -1}; - ranges::uninitialized_move(input, output); + const auto result = ranges::uninitialized_move(input, output); + assert(result.in == end(input)); + assert(result.out == end(output)); assert(ranges::equal(input, expected_input)); assert(ranges::equal(output, expected_output)); } @@ -234,6 +236,30 @@ struct memcpy_test { assert(ranges::equal(input, expected_input_long)); assert(ranges::equal(output, expected_output)); } + + { // Validate non-common input range + int input[] = {13, 55, 12345}; + int output[] = {-1, -1, -1}; + + const auto result = + ranges::uninitialized_move(begin(input), unreachable_sentinel, begin(output), end(output)); + assert(result.in == end(input)); + assert(result.out == end(output)); + assert(ranges::equal(input, expected_input)); + assert(ranges::equal(output, expected_output)); + } + + { // Validate non-common output range + int input[] = {13, 55, 12345}; + int output[] = {-1, -1, -1}; + + const auto result = + ranges::uninitialized_move(begin(input), end(input), begin(output), unreachable_sentinel); + assert(result.in == end(input)); + assert(result.out == end(output)); + assert(ranges::equal(input, expected_input)); + assert(ranges::equal(output, expected_output)); + } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct/test.cpp index a17ede367c5..f5c27485556 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct/test.cpp @@ -121,6 +121,20 @@ struct throwing_test { } }; +struct memset_test { + static constexpr int expected[] = {0, 0, 0}; + + static void call() { + { // Validate only range overload + int input[3]; + + const auto result = ranges::uninitialized_value_construct(input); + assert(result == end(input)); + assert(ranges::equal(input, expected)); + } + } +}; + using test_range = test::range; @@ -130,4 +144,5 @@ int main() { instantiator::call(); throwing_test::call(); + memset_test::call(); } diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct_n/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct_n/test.cpp index 6a9f08449d8..2797150ec44 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct_n/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_value_construct_n/test.cpp @@ -100,6 +100,20 @@ struct throwing_test { } }; +struct memset_test { + static constexpr int expected[] = {0, 0, 0}; + + static void call() { + { // Validate only range overload + int input[3]; + + const auto result = ranges::uninitialized_value_construct_n(begin(input), 3); + assert(result == end(input)); + assert(ranges::equal(input, expected)); + } + } +}; + using test_range = test::range; @@ -109,4 +123,5 @@ int main() { instantiator::call(); throwing_test::call(); + memset_test::call(); } diff --git a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp index 576c4340517..2e4698779e3 100644 --- a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp @@ -104,6 +104,7 @@ STATIC_ASSERT(test_cpo(ranges::views::drop)); STATIC_ASSERT(test_cpo(ranges::views::drop_while)); STATIC_ASSERT(test_cpo(ranges::views::elements<42>)); STATIC_ASSERT(test_cpo(ranges::views::filter)); +STATIC_ASSERT(test_cpo(ranges::views::iota)); STATIC_ASSERT(test_cpo(ranges::views::keys)); STATIC_ASSERT(test_cpo(ranges::views::reverse)); STATIC_ASSERT(test_cpo(ranges::views::single)); @@ -1499,9 +1500,7 @@ namespace borrowed_range_testing { STATIC_ASSERT(test_borrowed_range, std::span::iterator>()); STATIC_ASSERT(test_borrowed_range, int*>()); STATIC_ASSERT(test_borrowed_range, int*>()); -#if 0 // TRANSITION, future - STATIC_ASSERT(test_borrowed_range, ...>()); -#endif // TRANSITION, future + STATIC_ASSERT(test_borrowed_range, ranges::iterator_t>>()); struct simple_borrowed_range { int* begin() const { diff --git a/tests/std/tests/P0896R4_views_drop/test.cpp b/tests/std/tests/P0896R4_views_drop/test.cpp index f8d503138de..2e154446815 100644 --- a/tests/std/tests/P0896R4_views_drop/test.cpp +++ b/tests/std/tests/P0896R4_views_drop/test.cpp @@ -20,41 +20,58 @@ using namespace std; constexpr auto pipeline = views::drop(1) | views::drop(1) | views::drop(1) | views::drop(1); template -inline constexpr bool is_empty_view = false; -template -inline constexpr bool is_empty_view> = true; - -template -inline constexpr bool is_dynamic_span = false; -template -inline constexpr bool is_dynamic_span> = true; - -template -inline constexpr bool is_string_view = false; -template -inline constexpr bool is_string_view> = true; +inline constexpr bool is_span = false; +template +inline constexpr bool is_span> = true; template inline constexpr bool is_subrange = false; template inline constexpr bool is_subrange> = true; +template +struct mapped { + template + using apply = ranges::drop_view>; +}; +template +struct mapped> { + template + using apply = ranges::empty_view; +}; +template +struct mapped> { + template + using apply = span; +}; +template +struct mapped> { + template + using apply = basic_string_view; +}; +// clang-format off +template + requires ranges::random_access_range> + && ranges::sized_range> +struct mapped> { + // clang-format on + template + using apply = ranges::iota_view; +}; // clang-format off -template -concept reconstructible = ranges::random_access_range - && ranges::sized_range - && (is_empty_view - || is_dynamic_span - || is_string_view - // || is_iota_view // TRANSITION, iota_view - || is_subrange); -// clang-format on - -template -using mapped_t = conditional_t, V, ranges::drop_view>; +template + requires random_access_iterator +struct mapped> { + // clang-format on + template + using apply = ranges::subrange; +}; template -using pipeline_t = mapped_t>>>>; +using mapped_t = typename mapped>::template apply; + +template +using pipeline_t = mapped_t>>>; template concept CanViewDrop = requires(Rng&& r) { @@ -71,7 +88,7 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { constexpr bool is_view = ranges::view>; using V = views::all_t; - using M = mapped_t; + using M = mapped_t; STATIC_ASSERT(ranges::view); STATIC_ASSERT(common_range == common_range); STATIC_ASSERT(input_range == input_range); @@ -81,7 +98,7 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(contiguous_range == contiguous_range); // Validate range adaptor object and range adaptor closure - constexpr auto drop_four = views::drop(4); + constexpr auto closure = views::drop(4); // ... with lvalue argument STATIC_ASSERT(CanViewDrop == (!is_view || copyable) ); @@ -91,8 +108,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(views::drop(rng, 4)) == is_noexcept); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(rng | drop_four) == is_noexcept); + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(rng | closure) == is_noexcept); STATIC_ASSERT(same_as>); STATIC_ASSERT(noexcept(rng | pipeline) == is_noexcept); @@ -100,26 +117,26 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { // ... with const lvalue argument STATIC_ASSERT(CanViewDrop&> == (!is_view || copyable) ); - if constexpr (is_view && copyable) { + if constexpr (is_span> || (is_view && copyable) ) { constexpr bool is_noexcept = (is_nothrow_copy_constructible_v && !is_subrange); STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(views::drop(as_const(rng), 4)) == is_noexcept); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(as_const(rng) | drop_four) == is_noexcept); + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | closure) == is_noexcept); STATIC_ASSERT(same_as&>>); STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); } else if constexpr (!is_view) { - using RC = mapped_t&>>; + using RC = mapped_t&>; constexpr bool is_noexcept = is_nothrow_constructible_v&, int>; STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(views::drop(as_const(rng), 4)) == is_noexcept); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(as_const(rng) | drop_four) == is_noexcept); + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | closure) == is_noexcept); STATIC_ASSERT(same_as&>>); STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); @@ -127,13 +144,13 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { // ... with rvalue argument STATIC_ASSERT(CanViewDrop> == is_view || enable_borrowed_range>); - if constexpr (is_view) { + if constexpr (is_span> || is_view) { constexpr bool is_noexcept = is_nothrow_move_constructible_v && !is_subrange; STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(views::drop(move(rng), 4)) == is_noexcept); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(move(rng) | drop_four) == is_noexcept); + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | closure) == is_noexcept); STATIC_ASSERT(same_as>>); STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); @@ -145,8 +162,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(views::drop(move(rng), 4)) == is_noexcept); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(move(rng) | drop_four) == is_noexcept); + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | closure) == is_noexcept); STATIC_ASSERT(same_as>>>); STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); @@ -155,14 +172,14 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { // ... with const rvalue argument STATIC_ASSERT(CanViewDrop> == (is_view && copyable) || (!is_view && enable_borrowed_range>) ); - if constexpr (is_view && copyable) { + if constexpr (is_span> || (is_view && copyable) ) { constexpr bool is_noexcept = is_nothrow_copy_constructible_v && !is_subrange; STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(views::drop(move(as_const(rng)), 4)) == is_noexcept); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(move(as_const(rng)) | drop_four) == is_noexcept); + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(rng)) | closure) == is_noexcept); STATIC_ASSERT(same_as>>); STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); @@ -174,8 +191,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(views::drop(move(as_const(rng)), 4)) == is_noexcept); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(move(as_const(rng)) | drop_four) == is_noexcept); + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(rng)) | closure) == is_noexcept); STATIC_ASSERT(same_as>>>); STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); @@ -455,9 +472,13 @@ int main() { // Validate views { // ... copyable // Test all of the "reconstructible range" types: span, empty_view, subrange, basic_string_view, iota_view - constexpr span s{some_ints}; - STATIC_ASSERT(test_one(s, only_four_ints)); - test_one(s, only_four_ints); + constexpr span s0{some_ints}; + STATIC_ASSERT(test_one(s0, only_four_ints)); + test_one(s0, only_four_ints); + + constexpr span s1{some_ints}; + STATIC_ASSERT(test_one(s1, only_four_ints)); + test_one(s1, only_four_ints); STATIC_ASSERT(test_one(ranges::subrange{some_ints}, only_four_ints)); test_one(ranges::subrange{some_ints}, only_four_ints); @@ -468,9 +489,8 @@ int main() { STATIC_ASSERT(test_one(basic_string_view{ranges::begin(some_ints), ranges::end(some_ints)}, only_four_ints)); test_one(basic_string_view{ranges::begin(some_ints), ranges::end(some_ints)}, only_four_ints); - // TRANSITION, iota_view - // STATIC_ASSERT(test_one(ranges::iota_view{0, 8}, only_four_ints)); - // test_one(ranges::iota_view{0, 8}, only_four_ints); + STATIC_ASSERT(test_one(ranges::iota_view{0, 8}, only_four_ints)); + test_one(ranges::iota_view{0, 8}, only_four_ints); } // ... move-only STATIC_ASSERT((move_only_test(), true)); @@ -490,13 +510,6 @@ int main() { test_one(lst, only_four_ints); } - // Validate a non-view borrowed range - { - constexpr span s{some_ints}; - STATIC_ASSERT(test_one(s, only_four_ints)); - test_one(s, only_four_ints); - } - // Validate an output range STATIC_ASSERT((output_range_test(), true)); output_range_test(); diff --git a/tests/std/tests/P0896R4_views_iota/env.lst b/tests/std/tests/P0896R4_views_iota/env.lst new file mode 100644 index 00000000000..62a24024479 --- /dev/null +++ b/tests/std/tests/P0896R4_views_iota/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_views_iota/test.cpp b/tests/std/tests/P0896R4_views_iota/test.cpp new file mode 100644 index 00000000000..ed990295ba3 --- /dev/null +++ b/tests/std/tests/P0896R4_views_iota/test.cpp @@ -0,0 +1,299 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +using namespace std; + +template +concept CanViewIota = requires(W w, B b) { + views::iota(w, b); +}; + +template +concept CanSize = requires(R& r) { + ranges::size(r); +}; + +struct empty_type {}; + +template +constexpr void test_integral() { + constexpr T low = 0; + constexpr T high = 8; + constexpr T expected[] = {0, 1, 2, 3, 4, 5, 6, 7}; + + { + // Validate bounded (both upper and lower bounds exist) iota_view + using R = ranges::iota_view; + + // Validate type properties + static_assert(same_as, T>); + static_assert(same_as, T>); + + static_assert(ranges::random_access_range); + static_assert(!ranges::contiguous_range); + static_assert(ranges::common_range); + + static_assert(ranges::view); + static_assert(semiregular); + static_assert(is_nothrow_copy_constructible_v); + static_assert(is_nothrow_copy_assignable_v); + static_assert(is_nothrow_move_constructible_v); + static_assert(is_nothrow_move_assignable_v); + + if constexpr (sizeof(T) < sizeof(int)) { + static_assert(same_as, int>); + } else { + static_assert(same_as, long long>); + } + + // iota_view is always a simple-view, i.e., const and non-const are always valid ranges with the same iterators: + static_assert(ranges::common_range); + static_assert( + same_as>, ranges::iterator_t>>); + + static_assert(same_as, T>); + static_assert(same_as, ranges::range_difference_t>); + + const same_as auto rng = views::iota(low, high); + static_assert(noexcept(views::iota(low, high))); // strengthened + + assert(ranges::equal(rng, expected)); + static_assert(noexcept(rng.begin())); // strengthened + static_assert(noexcept(rng.end())); // strengthened + + assert(rng.size() == 8u); + static_assert(noexcept(rng.size() == 8u)); // strengthened + + using I = ranges::iterator_t; + static_assert(same_as); + static_assert(same_as::iterator_category, input_iterator_tag>); + + assert(I{} == I{T{0}}); + static_assert(is_nothrow_default_constructible_v); + assert(I{} == I{}); + assert(!(I{} != I{})); + assert(!(I{} < I{})); + assert(!(I{} > I{})); + assert(I{} <= I{}); + assert(I{} >= I{}); + assert(I{} <=> I{} == 0); + assert(I{} - I{} == 0); + + const I first = rng.begin(); + const I second = ranges::next(first); + + assert(*first == T{0}); + assert(*second == T{1}); + static_assert(noexcept(*first)); + + assert(first == I{T{0}}); + assert(second == I{T{1}}); + static_assert(noexcept(noexcept(I{T{0}}))); // strengthened + + { + I tmp = first; + static_assert(is_nothrow_copy_constructible_v); + + I i = move(tmp); + static_assert(is_nothrow_move_constructible_v); + + tmp = i; + static_assert(is_nothrow_copy_assignable_v); + + i = move(tmp); + static_assert(is_nothrow_move_assignable_v); + } + + assert(!(first == second)); + assert(!(second == first)); + static_assert(noexcept(first == second)); // strengthened + + assert(first != second); + assert(second != first); + static_assert(noexcept(first != second)); // strengthened + + assert(first < second); + assert(!(second < first)); + static_assert(noexcept(first < second)); // strengthened + + assert(!(first > second)); + assert(second > first); + static_assert(noexcept(first > second)); // strengthened + + assert(first <= second); + assert(!(second <= first)); + static_assert(noexcept(first <= second)); // strengthened + + assert(!(first >= second)); + assert(second >= first); + static_assert(noexcept(first >= second)); // strengthened + + assert(first <=> second < 0); + assert(second <=> first > 0); + static_assert(noexcept(first <=> second)); // strengthened + + { + I i = first; + + assert(&++i == &i); + assert(i == second); + static_assert(noexcept(++i)); // strengthened + + i = first; + + assert(i++ == first); + assert(i == second); + static_assert(noexcept(i++)); // strengthened + + assert(&--i == &i); + assert(i == first); + static_assert(noexcept(--i)); // strengthened + + i = second; + + assert(i-- == second); + assert(i == first); + static_assert(noexcept(i--)); // strengthened + + assert(i + 1 == second); + static_assert(noexcept(i + 1)); // strengthened + + assert(1 + i == second); + static_assert(noexcept(1 + i)); // strengthened + + assert(&(i += 1) == &i); + assert(i == second); + static_assert(noexcept(i += 1)); // strengthened + assert(i - 1 == first); + static_assert(noexcept(i - 1)); // strengthened + + assert(&(i -= 1) == &i); + assert(i == first); + static_assert(noexcept(i -= 1)); // strengthened + + assert(second - first == 1); + static_assert(noexcept(second - first)); // strengthened + + assert(first[1] == *second); + assert(second[-1] == *first); + static_assert(noexcept(first[1])); // strengthened + } + + const same_as> auto last = rng.end(); + assert(!(first == last)); + static_assert(noexcept(first == last)); // strengthened + assert(first != last); + static_assert(noexcept(first != last)); // strengthened + assert(last - first == 8); + static_assert(noexcept(last - first)); // strengthened + } + + { + // Validate unbounded (upper bound is unreachable) iota_view + using R = ranges::iota_view; + static_assert(same_as, T>); + static_assert(same_as, T>); + + static_assert(ranges::random_access_range); + static_assert(!ranges::contiguous_range); + static_assert(!ranges::common_range); + static_assert(same_as>, unreachable_sentinel_t>); + + // Bounded and unbounded iota_view have the same iterator type, however: + static_assert(same_as>, ranges::iterator_t>>); + + if constexpr (sizeof(T) < sizeof(int)) { + static_assert(same_as, int>); + } else { + static_assert(same_as, long long>); + } + + { + const same_as auto rng = views::iota(low); + + auto i = low; + for (const auto& e : rng) { + assert(e == i); + if (++i == high) { + break; + } + } + } + + static_assert(!CanSize>); + } +} + +int main() { + // Validate standard signed integer types + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + + // Validate standard unsigned integer types + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + + // Validate other integer types +#ifndef __clang__ // TRANSITION, LLVM-48173 + static_assert(!CanViewIota); +#endif // TRANSITION, LLVM-48173 + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); +#ifdef __cpp_char8_t + static_assert((test_integral(), true)); + test_integral(); +#endif // __cpp_char8_t + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + + // Some non-integer coverage: + { + // Pointers + empty_type objects[] = {{}, {}, {}}; + const auto address = [](auto& x) { return &x; }; + assert(ranges::equal( + views::iota(begin(as_const(objects)), end(objects)), objects, ranges::equal_to{}, identity{}, address)); + assert(ranges::equal( + views::iota(begin(objects), end(as_const(objects))), objects, ranges::equal_to{}, identity{}, address)); + } + { + // Iterator and sentinel of a non-common range + const int some_ints[] = {0, 1, 2, 3, 4, 5, 6, 7}; + const int even_ints[] = {0, 2, 4, 6}; + + const auto even = [](int x) { return x % 2 == 0; }; + const auto deref = [](auto x) -> decltype(auto) { return *x; }; + + auto f = some_ints | views::filter(even); + auto r = views::iota(ranges::begin(f), ranges::end(f)); + + assert(ranges::equal(r, even_ints, ranges::equal_to{}, deref)); + } +} diff --git a/tests/std/tests/P0896R4_views_take/test.cpp b/tests/std/tests/P0896R4_views_take/test.cpp index 27ccb1b7cc3..34fb9e3a305 100644 --- a/tests/std/tests/P0896R4_views_take/test.cpp +++ b/tests/std/tests/P0896R4_views_take/test.cpp @@ -20,41 +20,58 @@ using namespace std; constexpr auto pipeline = views::take(7) | views::take(6) | views::take(5) | views::take(4); template -inline constexpr bool is_empty_view = false; -template -inline constexpr bool is_empty_view> = true; - -template -inline constexpr bool is_dynamic_span = false; -template -inline constexpr bool is_dynamic_span> = true; - -template -inline constexpr bool is_string_view = false; -template -inline constexpr bool is_string_view> = true; +inline constexpr bool is_span = false; +template +inline constexpr bool is_span> = true; template inline constexpr bool is_subrange = false; template inline constexpr bool is_subrange> = true; +template +struct mapped { + template + using apply = ranges::take_view>; +}; +template +struct mapped> { + template + using apply = ranges::empty_view; +}; +template +struct mapped> { + template + using apply = span; +}; +template +struct mapped> { + template + using apply = basic_string_view; +}; +// clang-format off +template + requires ranges::random_access_range> + && ranges::sized_range> +struct mapped> { + // clang-format on + template + using apply = ranges::iota_view; +}; // clang-format off -template -concept reconstructible = ranges::random_access_range - && ranges::sized_range - && (is_empty_view - || is_dynamic_span - || is_string_view - // || is_iota_view // TRANSITION, iota_view - || is_subrange); -// clang-format on - -template -using mapped_t = conditional_t, V, ranges::take_view>; +template + requires random_access_iterator +struct mapped> { + // clang-format on + template + using apply = ranges::subrange; +}; + +template +using mapped_t = typename mapped>::template apply; template -using pipeline_t = mapped_t>>>>; +using pipeline_t = mapped_t>>>; template concept CanViewTake = requires(Rng&& r) { @@ -71,7 +88,7 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { constexpr bool is_view = ranges::view>; using V = views::all_t; - using M = mapped_t; + using M = mapped_t; STATIC_ASSERT(ranges::view); STATIC_ASSERT(input_range == input_range); STATIC_ASSERT(forward_range == forward_range); @@ -80,7 +97,7 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(contiguous_range == contiguous_range); // Validate range adaptor object and range adaptor closure - constexpr auto take_four = views::take(4); + constexpr auto closure = views::take(4); // ... with lvalue argument STATIC_ASSERT(CanViewTake == (!is_view || copyable) ); @@ -90,8 +107,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(views::take(rng, 4)) == is_noexcept); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(rng | take_four) == is_noexcept); + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(rng | closure) == is_noexcept); STATIC_ASSERT(same_as>); STATIC_ASSERT(noexcept(rng | pipeline) == is_noexcept); @@ -99,40 +116,40 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { // ... with const lvalue argument STATIC_ASSERT(CanViewTake&> == (!is_view || copyable) ); - if constexpr (is_view && copyable) { - constexpr bool is_noexcept = (is_nothrow_copy_constructible_v && !is_subrange); + if constexpr (is_span> || (is_view && copyable) ) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v && !is_subrange; STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(views::take(as_const(rng), 4)) == is_noexcept); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(as_const(rng) | take_four) == is_noexcept); + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | closure) == is_noexcept); STATIC_ASSERT(same_as&>>); STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); } else if constexpr (!is_view) { - using RC = mapped_t&>>; + using RC = mapped_t&>; constexpr bool is_noexcept = is_nothrow_constructible_v&, int>; STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(views::take(as_const(rng), 4)) == is_noexcept); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(as_const(rng) | take_four) == is_noexcept); + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | closure) == is_noexcept); STATIC_ASSERT(same_as&>>); STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); } // ... with rvalue argument - STATIC_ASSERT(CanViewTake> == is_view || enable_borrowed_range>); - if constexpr (is_view) { + STATIC_ASSERT(CanViewTake> == (is_view || enable_borrowed_range>) ); + if constexpr (is_span> || is_view) { constexpr bool is_noexcept = is_nothrow_move_constructible_v && !is_subrange; STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(views::take(move(rng), 4)) == is_noexcept); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(move(rng) | take_four) == is_noexcept); + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | closure) == is_noexcept); STATIC_ASSERT(same_as>>); STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); @@ -144,8 +161,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(views::take(move(rng), 4)) == is_noexcept); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(move(rng) | take_four) == is_noexcept); + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | closure) == is_noexcept); STATIC_ASSERT(same_as>>>); STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); @@ -154,14 +171,14 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { // ... with const rvalue argument STATIC_ASSERT(CanViewTake> == (is_view && copyable) || (!is_view && enable_borrowed_range>) ); - if constexpr (is_view && copyable) { + if constexpr (is_span> || (is_view && copyable) ) { constexpr bool is_noexcept = is_nothrow_copy_constructible_v && !is_subrange; STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(views::take(move(as_const(rng)), 4)) == is_noexcept); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(move(as_const(rng)) | take_four) == is_noexcept); + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(rng)) | closure) == is_noexcept); STATIC_ASSERT(same_as>>); STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); @@ -173,8 +190,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(same_as); STATIC_ASSERT(noexcept(views::take(move(as_const(rng)), 4)) == is_noexcept); - STATIC_ASSERT(same_as); - STATIC_ASSERT(noexcept(move(as_const(rng)) | take_four) == is_noexcept); + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(rng)) | closure) == is_noexcept); STATIC_ASSERT(same_as>>>); STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); @@ -496,10 +513,14 @@ constexpr void output_range_test() { int main() { // Validate views { // ... copyable - // Test all of the "reconstructible range" types: span, empty_view, subrange, basic_string_view, iota_view - constexpr span s{some_ints}; - STATIC_ASSERT(test_one(s, only_four_ints)); - test_one(s, only_four_ints); + // Test all of the "reconstructible range" types: span, empty_view, subrange, basic_string_view, iota_view + constexpr span s0{some_ints}; + STATIC_ASSERT(test_one(s0, only_four_ints)); + test_one(s0, only_four_ints); + + constexpr span s1{some_ints}; + STATIC_ASSERT(test_one(s1, only_four_ints)); + test_one(s1, only_four_ints); STATIC_ASSERT(test_one(ranges::subrange{some_ints}, only_four_ints)); test_one(ranges::subrange{some_ints}, only_four_ints); @@ -510,9 +531,8 @@ int main() { STATIC_ASSERT(test_one(basic_string_view{ranges::begin(some_ints), ranges::end(some_ints)}, only_four_ints)); test_one(basic_string_view{ranges::begin(some_ints), ranges::end(some_ints)}, only_four_ints); - // TRANSITION, iota_view - // STATIC_ASSERT(test_one(ranges::iota_view{0, 8}, only_four_ints)); - // test_one(ranges::iota_view{0, 8}, only_four_ints); + STATIC_ASSERT(test_one(ranges::iota_view{0, 8}, only_four_ints)); + test_one(ranges::iota_view{0, 8}, only_four_ints); } // ... move-only STATIC_ASSERT((move_only_test(), true)); @@ -532,13 +552,6 @@ int main() { test_one(lst, only_four_ints); } - // Validate a non-view borrowed range - { - constexpr span s{some_ints}; - STATIC_ASSERT(test_one(s, only_four_ints)); - test_one(s, only_four_ints); - } - // Validate an output range STATIC_ASSERT((output_range_test(), true)); output_range_test(); diff --git a/tests/std/tests/P0980R1_constexpr_strings/env.lst b/tests/std/tests/P0980R1_constexpr_strings/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0980R1_constexpr_strings/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P0980R1_constexpr_strings/test.cpp b/tests/std/tests/P0980R1_constexpr_strings/test.cpp new file mode 100644 index 00000000000..495abb7d558 --- /dev/null +++ b/tests/std/tests/P0980R1_constexpr_strings/test.cpp @@ -0,0 +1,1799 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma warning(disable : 4389) // signed/unsigned mismatch in arithmetic + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wsign-compare" +#endif // __clang__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +constexpr auto literal_input = "Hello fluffy kittens"; +constexpr auto literal_input_u16 = u"Hello fluffy kittens"; +constexpr auto literal_input_u32 = U"Hello fluffy kittens"; +constexpr auto literal_input_w = L"Hello fluffy kittens"; + +constexpr auto view_input = "Hello fluffy kittens"sv; +constexpr auto view_input_u16 = u"Hello fluffy kittens"sv; +constexpr auto view_input_u32 = U"Hello fluffy kittens"sv; +constexpr auto view_input_w = L"Hello fluffy kittens"sv; + +#ifdef __cpp_char8_t +constexpr auto literal_input_u8 = u8"Hello fluffy kittens"; +constexpr auto view_input_u8 = u8"Hello fluffy kittens"sv; +#endif // __cpp_char8_t + +template +constexpr auto get_literal_input() { + if constexpr (is_same_v) { + return literal_input; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return literal_input_u8; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return literal_input_u16; + } else if constexpr (is_same_v) { + return literal_input_u32; + } else { + return literal_input_w; + } +} + +template +constexpr auto get_view_input() { + if constexpr (is_same_v) { + return view_input; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return view_input_u8; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return view_input_u16; + } else if constexpr (is_same_v) { + return view_input_u32; + } else { + return view_input_w; + } +} + +template +constexpr auto get_cat() { + if constexpr (is_same_v) { + return "kitten"; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return u8"kitten"; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return u"kitten"; + } else if constexpr (is_same_v) { + return U"kitten"; + } else { + return L"kitten"; + } +} + +template +constexpr auto get_dog() { + if constexpr (is_same_v) { + return "dog"; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return u8"dog"; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return u"dog"; + } else if constexpr (is_same_v) { + return U"dog"; + } else { + return L"dog"; + } +} + +template +constexpr auto get_no_needle() { + if constexpr (is_same_v) { + return "vxz"; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return u8"vxz"; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return u"vxz"; + } else if constexpr (is_same_v) { + return U"vxz"; + } else { + return L"vxz"; + } +} + +template +constexpr auto get_cute_and_scratchy() { + if constexpr (is_same_v) { + return "cute and scratchy "; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return u8"cute and scratchy "; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return u"cute and scratchy "; + } else if constexpr (is_same_v) { + return U"cute and scratchy "; + } else { + return L"cute and scratchy "; + } +} + +template +struct string_view_convertible { + _CONSTEXPR20_CONTAINER operator basic_string_view() const { + if constexpr (is_same_v) { + return view_input; +#ifdef __cpp_char8_t + } else if constexpr (is_same_v) { + return view_input_u8; +#endif // __cpp_char8_t + } else if constexpr (is_same_v) { + return view_input_u16; + } else if constexpr (is_same_v) { + return view_input_u32; + } else { + return view_input_w; + } + } +}; + +// TRANSITION, EDG concepts support +template +constexpr bool equalRanges(const Range1& range1, const Range2& range2) noexcept { +#ifdef __cpp_lib_concepts + return ranges::equal(range1, range2); +#else // ^^^ __cpp_lib_concepts ^^^ / vvv !__cpp_lib_concepts vvv + return equal(begin(range1), end(range1), begin(range2), end(range2)); +#endif // !__cpp_lib_concepts +} + +template +_CONSTEXPR20_CONTAINER bool test_interface() { +#ifndef __EDG__ // TRANSITION, VSO-1273296 + using str = basic_string; + + { // constructors + // range constructors + str literal_constructed{get_literal_input()}; + assert(equalRanges(literal_constructed, get_view_input())); + + str view_constructed(get_view_input()); + assert(equalRanges(view_constructed, literal_constructed)); + + str initializer_list_constructed({CharType{'m'}, CharType{'e'}, CharType{'o'}, CharType{'w'}}); + assert(equalRanges(initializer_list_constructed, "meow"sv)); + + // special member functions + str default_constructed; + assert(default_constructed.empty()); + + str copy_constructed(literal_constructed); + assert(equalRanges(copy_constructed, literal_constructed)); + + str move_constructed(move(copy_constructed)); + assert(equalRanges(move_constructed, literal_constructed)); + assert(copy_constructed.empty()); + + str copy_assigned(get_dog()); + copy_assigned = literal_constructed; + assert(equalRanges(copy_assigned, literal_constructed)); + + str move_assigned(get_dog()); + move_assigned = move(copy_assigned); + assert(equalRanges(move_assigned, literal_constructed)); + assert(copy_assigned.empty()); + + // Other constructors + str size_value_constructed(5, CharType{'a'}); + assert(equalRanges(size_value_constructed, "aaaaa"sv)); + + str copy_start_constructed(literal_constructed, 2); + assert(equalRanges(copy_start_constructed, "llo fluffy kittens"sv)); + + str copy_start_length_constructed(literal_constructed, 2, 3); + assert(equalRanges(copy_start_length_constructed, "llo"sv)); + + str ptr_size_constructed(get_literal_input(), 2); + assert(equalRanges(ptr_size_constructed, "He"sv)); +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) // TRANSITION, VSO-1270433 + str iterator_constructed(literal_constructed.begin(), literal_constructed.end()); + assert(equalRanges(iterator_constructed, literal_constructed)); +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) + const string_view_convertible convertible; + str conversion_constructed(convertible); + assert(equalRanges(conversion_constructed, literal_constructed)); + + str conversion_start_length_constructed(convertible, 2, 3); + assert(equalRanges(conversion_start_length_constructed, "llo"sv)); + } + + { // allocator constructors + allocator alloc; + + // range constructors + str literal_constructed{get_literal_input(), alloc}; + assert(equalRanges(literal_constructed, get_view_input())); + + str view_constructed{get_view_input(), alloc}; + assert(equalRanges(view_constructed, literal_constructed)); + + str initializer_list_constructed({CharType{'m'}, CharType{'e'}, CharType{'o'}, CharType{'w'}}, alloc); + assert(equalRanges(initializer_list_constructed, "meow"sv)); + + // special member functions + str default_constructed{alloc}; + assert(default_constructed.empty()); + + str copy_constructed{literal_constructed, alloc}; + assert(equalRanges(copy_constructed, literal_constructed)); + + str move_constructed{move(copy_constructed), alloc}; + assert(equalRanges(move_constructed, literal_constructed)); + assert(copy_constructed.empty()); + + // Other constructors + str size_value_constructed(5, CharType{'a'}, alloc); + assert(equalRanges(size_value_constructed, "aaaaa"sv)); + + str copy_start_constructed(literal_constructed, 2, alloc); + assert(equalRanges(copy_start_constructed, "llo fluffy kittens"sv)); + + str copy_start_length_constructed(literal_constructed, 2, 3, alloc); + assert(equalRanges(copy_start_length_constructed, "llo"sv)); + + str ptr_size_constructed(get_literal_input(), 2, alloc); + assert(equalRanges(ptr_size_constructed, "He"sv)); +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) // TRANSITION, VSO-1270433 + str iterator_constructed(literal_constructed.begin(), literal_constructed.end(), alloc); + assert(equalRanges(iterator_constructed, literal_constructed)); +#endif // defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) + const string_view_convertible convertible; + str conversion_constructed(convertible, alloc); + assert(equalRanges(conversion_constructed, literal_constructed)); + + str conversion_start_length_constructed(convertible, 2, 3, alloc); + assert(equalRanges(conversion_start_length_constructed, "llo"sv)); + } + + { // assignment operator + str literal_constructed = get_literal_input(); + + str copy_assigned; + copy_assigned = literal_constructed; + assert(equalRanges(copy_assigned, literal_constructed)); + + str move_assigned; + move_assigned = move(copy_assigned); + assert(equalRanges(move_assigned, literal_constructed)); + assert(copy_assigned.empty()); + + str literal_assigned; + literal_assigned = get_literal_input(); + assert(equalRanges(literal_assigned, literal_constructed)); + + str char_assigned; + char_assigned = CharType{'!'}; + assert(equalRanges(char_assigned, "!"sv)); + + str initializer_list_assigned; + initializer_list_assigned = {CharType{'m'}, CharType{'e'}, CharType{'o'}, CharType{'w'}}; + assert(equalRanges(initializer_list_assigned, "meow"sv)); + + const string_view_convertible convertible; + str conversion_assigned; + conversion_assigned = convertible; + assert(equalRanges(conversion_assigned, literal_constructed)); + } + + { // assign + str literal_constructed = get_literal_input(); + + str assign_size_char; + assign_size_char.assign(5, CharType{'a'}); + assert(equalRanges(assign_size_char, "aaaaa"sv)); + + str assign_str; + assign_str.assign(literal_constructed); + assert(equalRanges(assign_str, literal_constructed)); + + str assign_str_pos; + assign_str_pos.assign(literal_constructed, 2); + assert(equalRanges(assign_str_pos, "llo fluffy kittens"sv)); + + str assign_str_pos_len; + assign_str_pos_len.assign(literal_constructed, 2, 3); + assert(equalRanges(assign_str_pos_len, "llo"sv)); + + str assign_moved_str; + assign_moved_str.assign(move(assign_str_pos_len)); + assert(equalRanges(assign_moved_str, "llo"sv)); + assert(assign_str_pos_len.empty()); + + str assign_literal; + assign_literal.assign(get_literal_input()); + assert(equalRanges(assign_literal, literal_constructed)); + + str assign_literal_count; + assign_literal_count.assign(get_literal_input(), 2); + assert(equalRanges(assign_literal_count, "He"sv)); + + str assign_iterator; + assign_iterator.assign(begin(get_view_input()), end(get_view_input())); + assert(equalRanges(assign_iterator, get_view_input())); + + str assign_initializer_list; + assign_initializer_list.assign({CharType{'m'}, CharType{'e'}, CharType{'o'}, CharType{'w'}}); + assert(equalRanges(assign_initializer_list, "meow"sv)); + + const string_view_convertible convertible; + str assign_conversion; + assign_conversion.assign(convertible); + assert(equalRanges(assign_conversion, literal_constructed)); + + str assign_conversion_start_length; + assign_conversion_start_length.assign(convertible, 2, 3); + assert(equalRanges(assign_conversion_start_length, "llo"sv)); + } + + { // allocator + str default_constructed; + [[maybe_unused]] const auto alloc = default_constructed.get_allocator(); + static_assert(is_same_v, allocator>); + } + + { // access + str literal_constructed = get_literal_input(); + const str const_literal_constructed = get_literal_input(); + + const auto at = literal_constructed.at(2); + static_assert(is_same_v, CharType>); + assert(at == CharType{'l'}); + + literal_constructed.at(2) = CharType{'v'}; + const auto at2 = literal_constructed.at(2); + static_assert(is_same_v, CharType>); + assert(at2 == CharType{'v'}); + + const auto cat = const_literal_constructed.at(2); + static_assert(is_same_v, CharType>); + assert(cat == CharType{'l'}); + + const auto op = literal_constructed[3]; + static_assert(is_same_v, CharType>); + assert(op == CharType{'l'}); + + literal_constructed[3] = CharType{'u'}; + const auto op2 = literal_constructed[3]; + static_assert(is_same_v, CharType>); + assert(op2 == CharType{'u'}); + + const auto cop = const_literal_constructed[3]; + static_assert(is_same_v, CharType>); + assert(cop == CharType{'l'}); + + const auto f = literal_constructed.front(); + static_assert(is_same_v, CharType>); + assert(f == CharType{'H'}); + + const auto cf = const_literal_constructed.front(); + static_assert(is_same_v, CharType>); + assert(cf == CharType{'H'}); + + const auto b = literal_constructed.back(); + static_assert(is_same_v, CharType>); + assert(b == CharType{'s'}); + + const auto cb = const_literal_constructed.back(); + static_assert(is_same_v, CharType>); + assert(cb == CharType{'s'}); + + const auto d = literal_constructed.data(); + static_assert(is_same_v); + assert(*d == CharType{'H'}); + + const auto cd = const_literal_constructed.data(); + static_assert(is_same_v); + assert(*cd == CharType{'H'}); + + const auto cs = literal_constructed.c_str(); + static_assert(is_same_v); + assert(cs == literal_constructed.data()); + assert(char_traits::length(cs) == literal_constructed.size()); + } + + { // iterators + str literal_constructed = get_literal_input(); + const str const_literal_constructed = get_literal_input(); + + const auto b = literal_constructed.begin(); + static_assert(is_same_v, typename str::iterator>); + assert(*b == CharType{'H'}); + + const auto cb = literal_constructed.cbegin(); + static_assert(is_same_v, typename str::const_iterator>); + assert(*cb == CharType{'H'}); + + const auto cb2 = const_literal_constructed.begin(); + static_assert(is_same_v, typename str::const_iterator>); + assert(*cb2 == CharType{'H'}); + + const auto e = literal_constructed.end(); + static_assert(is_same_v, typename str::iterator>); + assert(*prev(e) == CharType{'s'}); + + const auto ce = literal_constructed.cend(); + static_assert(is_same_v, typename str::const_iterator>); + assert(*prev(ce) == CharType{'s'}); + + const auto ce2 = const_literal_constructed.end(); + static_assert(is_same_v, typename str::const_iterator>); + assert(*prev(ce2) == CharType{'s'}); + + const auto rb = literal_constructed.rbegin(); + static_assert(is_same_v, reverse_iterator>); + assert(*rb == CharType{'s'}); + + const auto crb = literal_constructed.crbegin(); + static_assert(is_same_v, reverse_iterator>); + assert(*crb == CharType{'s'}); + + const auto crb2 = const_literal_constructed.rbegin(); + static_assert(is_same_v, reverse_iterator>); + assert(*crb2 == CharType{'s'}); + + const auto re = literal_constructed.rend(); + static_assert(is_same_v, reverse_iterator>); + assert(*prev(re) == CharType{'H'}); + + const auto cre = literal_constructed.crend(); + static_assert(is_same_v, reverse_iterator>); + assert(*prev(cre) == CharType{'H'}); + + const auto cre2 = const_literal_constructed.rend(); + static_assert(is_same_v, reverse_iterator>); + assert(*prev(cre2) == CharType{'H'}); + } + + { // capacity + str literal_constructed = get_literal_input(); + + const auto e = literal_constructed.empty(); + static_assert(is_same_v, bool>); + assert(!e); + + const auto s = literal_constructed.size(); + static_assert(is_same_v, size_t>); + assert(s == size(get_view_input())); + + const auto l = literal_constructed.length(); + static_assert(is_same_v, size_t>); + assert(l == s); + + const auto ms = literal_constructed.max_size(); + static_assert(is_same_v, size_t>); + if constexpr (is_same_v || is_same_v || is_same_v) { + assert(ms == static_cast(-1) / sizeof(CharType) - 1); + } else { + assert(ms == static_cast(-1) / 2); + } + + literal_constructed.reserve(20); + + const auto c = literal_constructed.capacity(); + static_assert(is_same_v, size_t>); + if constexpr (is_same_v || is_same_v || is_same_v) { + assert(c == 23); + } else { + assert(c == 31); + } + + // make reserve actually do work + literal_constructed.reserve(35); + const auto c2 = literal_constructed.capacity(); + if constexpr (is_same_v || is_same_v) { + assert(c2 == 39); + } else if constexpr (is_same_v) { + assert(c2 == 35); + } else { + assert(c2 == 47); + } + + // shrink back to previous size + literal_constructed.shrink_to_fit(); + + const auto c3 = literal_constructed.capacity(); + if constexpr (is_same_v || is_same_v || is_same_v) { + assert(c3 == 23); + } else { + assert(c3 == 31); + } + + literal_constructed.erase(3); + literal_constructed.shrink_to_fit(); + + const auto c4 = literal_constructed.capacity(); + if (is_constant_evaluated()) { // check minimum allocation of _BUF_SIZE when constant evaluated + assert(c4 == 16 / sizeof(CharType)); + } else { + if constexpr (is_same_v || is_same_v) { + assert(c4 == 7); + } else if constexpr (is_same_v) { + assert(c4 == 3); + } else { + assert(c4 == 15); + } + } + } + + { // clear + str cleared = get_literal_input(); + cleared.clear(); + assert(cleared.empty()); + assert(cleared.capacity() == str{get_literal_input()}.capacity()); + } + + { // insert + str insert_char = get_literal_input(); + const CharType to_be_inserted = CharType{','}; + insert_char.insert(insert_char.begin() + 5, to_be_inserted); + assert(equalRanges(insert_char, "Hello, fluffy kittens"sv)); + + str insert_const_char = get_literal_input(); + insert_const_char.insert(insert_const_char.cbegin() + 5, to_be_inserted); + assert(equalRanges(insert_const_char, "Hello, fluffy kittens"sv)); + + str insert_char_rvalue = get_literal_input(); + insert_char_rvalue.insert(insert_char_rvalue.begin() + 5, CharType{','}); + assert(equalRanges(insert_char_rvalue, "Hello, fluffy kittens"sv)); + + str insert_const_char_rvalue = get_literal_input(); + insert_const_char_rvalue.insert(insert_const_char_rvalue.cbegin() + 5, CharType{','}); + assert(equalRanges(insert_const_char_rvalue, "Hello, fluffy kittens"sv)); + + str insert_range(2, CharType{'b'}); + const auto it = insert_range.insert( + insert_range.begin() + 1, begin(get_view_input()), end(get_view_input())); + assert(it == insert_range.begin() + 1); + assert(equalRanges(insert_range, "bHello fluffy kittensb"sv)); + + str insert_const_range(2, CharType{'b'}); + const auto cit = insert_const_range.insert( + insert_const_range.cbegin() + 1, begin(get_view_input()), end(get_view_input())); + assert(cit == insert_const_range.cbegin() + 1); + assert(equalRanges(insert_const_range, "bHello fluffy kittensb"sv)); + + str insert_initializer_list = get_literal_input(); + const auto it_ilist = insert_initializer_list.insert(insert_initializer_list.begin() + 6, + {CharType{'c'}, CharType{'u'}, CharType{'t'}, CharType{'e'}, CharType{' '}}); + assert(it_ilist == insert_initializer_list.begin() + 6); + assert(equalRanges(insert_initializer_list, "Hello cute fluffy kittens"sv)); + + str insert_const_initializer_list = get_literal_input(); + const auto cit_ilist = insert_const_initializer_list.insert(insert_const_initializer_list.cbegin() + 6, + {CharType{'c'}, CharType{'u'}, CharType{'t'}, CharType{'e'}, CharType{' '}}); + assert(cit_ilist == insert_const_initializer_list.cbegin() + 6); + assert(equalRanges(insert_const_initializer_list, "Hello cute fluffy kittens"sv)); + + str insert_pos_str = get_literal_input(); + const str to_insert = get_cute_and_scratchy(); + insert_pos_str.insert(6, to_insert); + assert(equalRanges(insert_pos_str, "Hello cute and scratchy fluffy kittens"sv)); + + str insert_pos_substr = get_literal_input(); + insert_pos_substr.insert(6, to_insert, 0, 5); + assert(equalRanges(insert_pos_substr, "Hello cute fluffy kittens"sv)); + + const string_view_convertible convertible; + str insert_pos_conversion = get_literal_input(); + insert_pos_conversion.insert(6, convertible); + assert(equalRanges(insert_pos_conversion, "Hello Hello fluffy kittensfluffy kittens"sv)); + + str insert_pos_conversion_substr = get_literal_input(); + insert_pos_conversion_substr.insert(6, convertible, 6, 7); + assert(equalRanges(insert_pos_conversion_substr, "Hello fluffy fluffy kittens"sv)); + + str insert_pos_literal = get_literal_input(); + insert_pos_literal.insert(6, get_literal_input()); + assert(equalRanges(insert_pos_literal, "Hello Hello fluffy kittensfluffy kittens"sv)); + + str insert_pos_literal_substr = get_literal_input(); + insert_pos_literal_substr.insert(6, get_literal_input(), 6); + assert(equalRanges(insert_pos_literal_substr, "Hello Hello fluffy kittens"sv)); + + str insert_pos_count_char = get_literal_input(); + insert_pos_count_char.insert(6, 3, CharType{'b'}); + assert(equalRanges(insert_pos_count_char, "Hello bbbfluffy kittens"sv)); + + str insert_iter_count_char = get_literal_input(); + insert_iter_count_char.insert(begin(insert_iter_count_char) + 5, 4, CharType{'o'}); + assert(equalRanges(insert_iter_count_char, "Hellooooo fluffy kittens"sv)); + } + + { // erase + str erase_pos_count = get_literal_input(); + erase_pos_count.erase(0, 6); + assert(equalRanges(erase_pos_count, "fluffy kittens"sv)); + + str erase_iter = get_literal_input(); + erase_iter.erase(erase_iter.begin()); + assert(equalRanges(erase_iter, "ello fluffy kittens"sv)); + + str erase_const_iter = get_literal_input(); + erase_const_iter.erase(erase_const_iter.cbegin()); + assert(equalRanges(erase_const_iter, "ello fluffy kittens"sv)); + + str erase_iter_iter = get_literal_input(); + erase_iter_iter.erase(erase_iter_iter.begin(), erase_iter_iter.begin() + 6); + assert(equalRanges(erase_iter_iter, "fluffy kittens"sv)); + + str erase_const_iter_iter = get_literal_input(); + erase_const_iter_iter.erase(erase_const_iter_iter.cbegin(), erase_const_iter_iter.cbegin() + 6); + assert(equalRanges(erase_const_iter_iter, "fluffy kittens"sv)); + + str erased_free = get_literal_input(); + erase(erased_free, CharType{'l'}); + assert(equalRanges(erased_free, "Heo fuffy kittens"sv)); + + str erased_free_if = get_literal_input(); + erase_if(erased_free_if, [](const CharType val) { return val == CharType{'t'}; }); + assert(equalRanges(erased_free_if, "Hello fluffy kiens"sv)); + } + + { // push_back / pop_back + str pushed; + pushed.push_back(CharType{'y'}); + assert(pushed.size() == 1); + assert(pushed.back() == CharType{'y'}); + + const CharType to_be_pushed = CharType{'z'}; + pushed.push_back(to_be_pushed); + assert(pushed.size() == 2); + assert(pushed.back() == CharType{'z'}); + + pushed.pop_back(); + assert(pushed.size() == 1); + assert(pushed.back() == CharType{'y'}); + } + + { // append + const str literal_constructed = get_literal_input(); + + str append_size_char(2, CharType{'b'}); + append_size_char.append(5, CharType{'a'}); + assert(equalRanges(append_size_char, "bbaaaaa"sv)); + + str append_str(2, CharType{'b'}); + append_str.append(literal_constructed); + assert(equalRanges(append_str, "bbHello fluffy kittens"sv)); + + str append_str_pos(2, CharType{'b'}); + append_str_pos.append(literal_constructed, 2); + assert(equalRanges(append_str_pos, "bbllo fluffy kittens"sv)); + + str append_str_pos_len(2, CharType{'b'}); + append_str_pos_len.append(literal_constructed, 2, 3); + assert(equalRanges(append_str_pos_len, "bbllo"sv)); + + str append_literal(2, CharType{'b'}); + append_literal.append(get_literal_input()); + assert(equalRanges(append_literal, "bbHello fluffy kittens"sv)); + + str append_literal_count(2, CharType{'b'}); + append_literal_count.append(get_literal_input(), 2); + assert(equalRanges(append_literal_count, "bbHe"sv)); + + str append_iterator(2, CharType{'b'}); + append_iterator.append(begin(get_view_input()), end(get_view_input())); + assert(equalRanges(append_iterator, "bbHello fluffy kittens"sv)); + + str append_initializer_list(2, CharType{'b'}); + append_initializer_list.append({CharType{'m'}, CharType{'e'}, CharType{'o'}, CharType{'w'}}); + assert(equalRanges(append_initializer_list, "bbmeow"sv)); + + const string_view_convertible convertible; + str append_conversion(2, CharType{'b'}); + append_conversion.append(convertible); + assert(equalRanges(append_conversion, "bbHello fluffy kittens"sv)); + + str append_conversion_start_length(2, CharType{'b'}); + append_conversion_start_length.append(convertible, 2, 3); + assert(equalRanges(append_conversion_start_length, "bbllo"sv)); + } + + { // operator+= + str literal_constructed = get_literal_input(); + + str plus_string(2, CharType{'b'}); + plus_string += literal_constructed; + assert(equalRanges(plus_string, "bbHello fluffy kittens"sv)); + + str plus_character(2, CharType{'b'}); + plus_character += CharType{'a'}; + assert(equalRanges(plus_character, "bba"sv)); + + str plus_literal(2, CharType{'b'}); + plus_literal += get_literal_input(); + assert(equalRanges(plus_literal, "bbHello fluffy kittens"sv)); + + str plus_initializer_list(2, CharType{'b'}); + plus_initializer_list += {CharType{'m'}, CharType{'e'}, CharType{'o'}, CharType{'w'}}; + assert(equalRanges(plus_initializer_list, "bbmeow"sv)); + + const string_view_convertible convertible; + str plus_conversion(2, CharType{'b'}); + plus_conversion += convertible; + assert(equalRanges(plus_conversion, "bbHello fluffy kittens"sv)); + } + + { // compare + const str first = get_literal_input(); + const str second = get_cat(); + + const int comp_str_eq = first.compare(first); + assert(comp_str_eq == 0); + + const int comp_str_less = first.compare(second); + assert(comp_str_less == -1); + + const int comp_str_greater = second.compare(first); + assert(comp_str_greater == 1); + + const int comp_pos_count_str_eq = first.compare(3, 7, first.substr(3, 7)); + assert(comp_pos_count_str_eq == 0); + + const int comp_pos_count_str_less = first.compare(0, 2, second); + assert(comp_pos_count_str_less == -1); + + const int comp_pos_count_str_greater = second.compare(0, 2, first); + assert(comp_pos_count_str_greater == 1); + + const int comp_pos_count_str_pos_eq = first.compare(3, 20, first, 3); + assert(comp_pos_count_str_pos_eq == 0); + + const int comp_pos_count_str_pos_less = first.compare(0, 2, second, 2); + assert(comp_pos_count_str_pos_less == -1); + + const int comp_pos_count_str_pos_greater = second.compare(0, 2, first, 6); + assert(comp_pos_count_str_pos_greater == 1); + + const int comp_pos_count_str_pos_count_eq = first.compare(3, 5, first, 3, 5); + assert(comp_pos_count_str_pos_count_eq == 0); + + const int comp_pos_count_str_pos_count_less = first.compare(0, 2, second, 2, 3); + assert(comp_pos_count_str_pos_count_less == -1); + + const int comp_pos_count_str_pos_count_greater = second.compare(0, 2, first, 6, 4); + assert(comp_pos_count_str_pos_count_greater == 1); + + const int comp_literal_eq = first.compare(get_literal_input()); + assert(comp_literal_eq == 0); + + const int comp_literal_less = first.compare(get_cat()); + assert(comp_literal_less == -1); + + const int comp_literal_greater = second.compare(get_literal_input()); + assert(comp_literal_greater == 1); + + const int comp_pos_count_literal_eq = first.compare(13, 6, get_cat()); + assert(comp_pos_count_literal_eq == 0); + + const int comp_pos_count_literal_less = first.compare(0, 2, get_cat()); + assert(comp_pos_count_literal_less == -1); + + const int comp_pos_count_literal_greater = second.compare(0, 2, get_literal_input()); + assert(comp_pos_count_literal_greater == 1); + + const int comp_pos_count_literal_count_eq = first.compare(13, 5, get_cat(), 5); + assert(comp_pos_count_literal_count_eq == 0); + + const int comp_pos_count_literal_count_less = first.compare(0, 2, get_cat(), 2); + assert(comp_pos_count_literal_count_less == -1); + + const int comp_pos_count_literal_count_greater = second.compare(0, 2, get_literal_input(), 6); + assert(comp_pos_count_literal_count_greater == 1); + + const int comp_pos_count_literal_pos_count_eq = first.compare(3, 5, get_literal_input(), 3, 5); + assert(comp_pos_count_literal_pos_count_eq == 0); + + const int comp_pos_count_literal_pos_count_less = first.compare(0, 2, get_cat(), 2, 3); + assert(comp_pos_count_literal_pos_count_less == -1); + + const int comp_pos_count_literal_pos_count_greater = second.compare(0, 2, get_literal_input(), 6, 4); + assert(comp_pos_count_literal_pos_count_greater == 1); + + const string_view_convertible convertible; + const int comp_conversion_eq = first.compare(convertible); + assert(comp_conversion_eq == 0); + + const int comp_conversion_less = first.compare(second); + assert(comp_conversion_less == -1); + + const int comp_conversion_greater = second.compare(convertible); + assert(comp_conversion_greater == 1); + + const int comp_pos_count_conversion_eq = first.compare(0, 20, convertible); + assert(comp_pos_count_conversion_eq == 0); + + const int comp_pos_count_conversion_less = first.compare(5, 4, convertible); + assert(comp_pos_count_conversion_less == -1); + + const int comp_pos_count_conversion_greater = second.compare(0, 2, convertible); + assert(comp_pos_count_conversion_greater == 1); + + const int comp_pos_count_conversion_pos_eq = first.compare(3, 20, convertible, 3); + assert(comp_pos_count_conversion_pos_eq == 0); + + const int comp_pos_count_conversion_pos_less = first.compare(0, 2, second, 2); + assert(comp_pos_count_conversion_pos_less == -1); + + const int comp_pos_count_conversion_pos_greater = second.compare(0, 2, convertible, 6); + assert(comp_pos_count_conversion_pos_greater == 1); + + const int comp_pos_count_conversion_pos_count_eq = first.compare(3, 5, convertible, 3, 5); + assert(comp_pos_count_conversion_pos_count_eq == 0); + + const int comp_pos_count_conversion_pos_count_less = first.compare(0, 2, second, 2, 3); + assert(comp_pos_count_conversion_pos_count_less == -1); + + const int comp_pos_count_conversion_pos_count_greater = second.compare(0, 2, convertible, 6, 4); + assert(comp_pos_count_conversion_pos_count_greater == 1); + } + + { // starts_with + const str starts = get_literal_input(); + const str input_string_true = starts.substr(0, 5); + assert(starts.starts_with(input_string_true)); + + const str input_string_false = get_cat(); + assert(!starts.starts_with(input_string_false)); + + assert(starts.starts_with(CharType{'H'})); + assert(!starts.starts_with(CharType{'h'})); + + assert(starts.starts_with(get_literal_input())); + assert(!input_string_false.starts_with(get_literal_input())); + } + + { // ends_with + const str ends = get_literal_input(); + const str input_string_true = ends.substr(5); + assert(ends.ends_with(input_string_true)); + + const str input_string_false = get_cat(); + assert(!ends.ends_with(input_string_false)); + + assert(ends.ends_with(CharType{'s'})); + assert(!ends.ends_with(CharType{'S'})); + + assert(ends.ends_with(get_literal_input())); + assert(!input_string_false.ends_with(get_literal_input())); + } + + { // replace + const str input = get_dog(); + + str replaced_pos_count_str = get_literal_input(); + replaced_pos_count_str.replace(13, 7, input); + assert(equalRanges(replaced_pos_count_str, "Hello fluffy dog"sv)); + + str replaced_pos_count_str_shift = get_literal_input(); + replaced_pos_count_str_shift.replace(13, 2, input); + assert(equalRanges(replaced_pos_count_str_shift, "Hello fluffy dogttens"sv)); + + str replaced_iter_str = get_literal_input(); + replaced_iter_str.replace(replaced_iter_str.cbegin() + 13, replaced_iter_str.cend(), input); + assert(equalRanges(replaced_iter_str, "Hello fluffy dog"sv)); + + str replaced_iter_str_shift = get_literal_input(); + replaced_iter_str_shift.replace( + replaced_iter_str_shift.cbegin() + 13, replaced_iter_str_shift.cbegin() + 15, input); + assert(equalRanges(replaced_iter_str_shift, "Hello fluffy dogttens"sv)); + + str replaced_pos_count_str_pos_count = get_literal_input(); + replaced_pos_count_str_pos_count.replace(13, 7, input, 1); + assert(equalRanges(replaced_pos_count_str_pos_count, "Hello fluffy og"sv)); + + str replaced_pos_count_str_pos_count_less = get_literal_input(); + replaced_pos_count_str_pos_count_less.replace(13, 2, input, 1, 2); + assert(equalRanges(replaced_pos_count_str_pos_count_less, "Hello fluffy ogttens"sv)); + + str replaced_iter_iter = get_literal_input(); + replaced_iter_iter.replace( + replaced_iter_iter.cbegin() + 13, replaced_iter_iter.cend(), input.begin(), input.end()); + assert(equalRanges(replaced_iter_iter, "Hello fluffy dog"sv)); + + str replaced_iter_iter_less = get_literal_input(); + replaced_iter_iter_less.replace(replaced_iter_iter_less.cbegin() + 13, replaced_iter_iter_less.cbegin() + 15, + input.begin() + 1, input.end()); + assert(equalRanges(replaced_iter_iter_less, "Hello fluffy ogttens"sv)); + + str replaced_pos_count_literal = get_literal_input(); + replaced_pos_count_literal.replace(13, 2, get_dog()); + assert(equalRanges(replaced_pos_count_literal, "Hello fluffy dogttens"sv)); + + str replaced_pos_count_literal_count = get_literal_input(); + replaced_pos_count_literal_count.replace(13, 2, get_dog(), 2); + assert(equalRanges(replaced_pos_count_literal_count, "Hello fluffy dottens"sv)); + + str replaced_iter_literal = get_literal_input(); + replaced_iter_literal.replace( + replaced_iter_literal.cbegin() + 13, replaced_iter_literal.cbegin() + 15, get_dog()); + assert(equalRanges(replaced_iter_literal, "Hello fluffy dogttens"sv)); + + str replaced_iter_literal_count = get_literal_input(); + replaced_iter_literal_count.replace(replaced_iter_literal_count.cbegin() + 13, + replaced_iter_literal_count.cbegin() + 15, get_dog(), 2); + assert(equalRanges(replaced_iter_literal_count, "Hello fluffy dottens"sv)); + + str replaced_pos_count_chars = get_literal_input(); + replaced_pos_count_chars.replace(13, 2, 5, CharType{'a'}); + assert(equalRanges(replaced_pos_count_chars, "Hello fluffy aaaaattens"sv)); + + str replaced_iter_chars = get_literal_input(); + replaced_iter_chars.replace( + replaced_iter_chars.cbegin() + 13, replaced_iter_chars.cbegin() + 15, 5, CharType{'a'}); + assert(equalRanges(replaced_iter_chars, "Hello fluffy aaaaattens"sv)); + + str replaced_iter_init = get_literal_input(); + replaced_iter_init.replace(replaced_iter_init.cbegin() + 13, replaced_iter_init.cbegin() + 15, + {CharType{'c'}, CharType{'u'}, CharType{'t'}, CharType{'e'}, CharType{' '}}); + assert(equalRanges(replaced_iter_init, "Hello fluffy cute ttens"sv)); + + const string_view_convertible convertible; + str replaced_pos_count_conversion = get_dog(); + replaced_pos_count_conversion.replace(1, 5, convertible); + assert(equalRanges(replaced_pos_count_conversion, "dHello fluffy kittens"sv)); + + str replaced_iter_conversion = get_dog(); + replaced_iter_conversion.replace( + replaced_iter_conversion.cbegin() + 1, replaced_iter_conversion.cbegin() + 2, convertible); + assert(equalRanges(replaced_iter_conversion, "dHello fluffy kittensg"sv)); + + str replaced_pos_count_conversion_pos = get_dog(); + replaced_pos_count_conversion_pos.replace(1, 5, convertible, 6); + assert(equalRanges(replaced_pos_count_conversion_pos, "dfluffy kittens"sv)); + + str replaced_pos_count_conversion_pos_count = get_dog(); + replaced_pos_count_conversion_pos_count.replace(1, 5, convertible, 6, 6); + assert(equalRanges(replaced_pos_count_conversion_pos_count, "dfluffy"sv)); + } + + { // substr + const str input = get_literal_input(); + + const str substr_pos = input.substr(6); + assert(equalRanges(substr_pos, "fluffy kittens"sv)); + + const str substr_pos_count = input.substr(6, 6); + assert(equalRanges(substr_pos_count, "fluffy"sv)); + } + + { // copy + const str input = get_literal_input(); + + CharType copy_count[5]; + input.copy(copy_count, 5); + assert(equalRanges(copy_count, "Hello"sv)); + + CharType copy_count_pos[6]; + input.copy(copy_count_pos, 6, 6); + assert(equalRanges(copy_count_pos, "fluffy"sv)); + } + + { // resize + str resized = get_literal_input(); + resized.resize(3); + assert(equalRanges(resized, "Hel"sv)); + + resized.resize(6, CharType{'a'}); + assert(equalRanges(resized, "Helaaa"sv)); + } + + { // swap + constexpr basic_string_view expected_first = get_dog(); + constexpr basic_string_view expected_second = get_cat(); + str first{get_cat()}; + str second{get_dog()}; + swap(first, second); + + assert(equalRanges(first, expected_first)); + assert(equalRanges(second, expected_second)); + + first.swap(second); + assert(equalRanges(second, expected_first)); + assert(equalRanges(first, expected_second)); + } + + { // find + const str input = get_literal_input(); + const str needle = get_cat(); + const str no_needle = get_no_needle(); + + const auto find_str = input.find(needle); + assert(find_str == 13u); + + const auto find_str_none = input.find(no_needle); + assert(find_str_none == str::npos); + + const auto find_str_pos = input.find(needle, 6); + assert(find_str_pos == 13u); + + const auto find_str_pos_none = input.find(needle, 14); + assert(find_str_pos_none == str::npos); + + const auto find_str_overflow = input.find(needle, 50); + assert(find_str_overflow == str::npos); + + const auto find_literal = input.find(get_cat()); + assert(find_literal == 13u); + + const auto find_literal_none = input.find(get_dog()); + assert(find_literal_none == str::npos); + + const auto find_literal_pos = input.find(get_cat(), 6); + assert(find_literal_pos == 13u); + + const auto find_literal_pos_none = input.find(get_cat(), 14); + assert(find_literal_pos_none == str::npos); + + const auto find_literal_overflow = input.find(get_cat(), 50); + assert(find_literal_overflow == str::npos); + + const auto find_literal_pos_count = input.find(get_cat(), 6, 4); + assert(find_literal_pos_count == 13u); + + const auto find_literal_pos_count_none = input.find(get_dog(), 14, 4); + assert(find_literal_pos_count_none == str::npos); + + const auto find_char = input.find(CharType{'e'}); + assert(find_char == 1u); + + const auto find_char_none = input.find(CharType{'x'}); + assert(find_char_none == str::npos); + + const auto find_char_pos = input.find(CharType{'e'}, 4); + assert(find_char_pos == 17u); + + const string_view_convertible convertible; + const auto find_convertible = input.find(convertible); + assert(find_convertible == 0); + + const auto find_convertible_pos = input.find(convertible, 2); + assert(find_convertible_pos == str::npos); + } + + { // rfind + const str input = get_literal_input(); + const str needle = get_cat(); + const str no_needle = get_no_needle(); + + const auto rfind_str = input.rfind(needle); + assert(rfind_str == 13u); + + const auto rfind_str_none = input.rfind(no_needle); + assert(rfind_str_none == str::npos); + + const auto rfind_str_pos = input.rfind(needle, 15); + assert(rfind_str_pos == 13u); + + const auto rfind_str_pos_none = input.rfind(needle, 6); + assert(rfind_str_pos_none == str::npos); + + const auto rfind_str_overflow = input.rfind(needle, 50); + assert(rfind_str_overflow == 13u); + + const auto rfind_literal = input.rfind(get_cat()); + assert(rfind_literal == 13u); + + const auto rfind_literal_none = input.rfind(get_dog()); + assert(rfind_literal_none == str::npos); + + const auto rfind_literal_pos = input.rfind(get_cat(), 15); + assert(rfind_literal_pos == 13u); + + const auto rfind_literal_pos_none = input.rfind(get_cat(), 6); + assert(rfind_literal_pos_none == str::npos); + + const auto rfind_literal_overflow = input.rfind(get_cat(), 50); + assert(rfind_literal_overflow == 13u); + + const auto rfind_literal_pos_count = input.rfind(get_cat(), 15, 4); + assert(rfind_literal_pos_count == 13u); + + const auto rfind_literal_pos_count_none = input.rfind(get_dog(), 6, 4); + assert(rfind_literal_pos_count_none == str::npos); + + const auto rfind_char = input.rfind(CharType{'e'}); + assert(rfind_char == 17u); + + const auto rfind_char_none = input.rfind(CharType{'x'}); + assert(rfind_char_none == str::npos); + + const auto rfind_char_pos = input.rfind(CharType{'e'}, 4); + assert(rfind_char_pos == 1u); + + const string_view_convertible convertible; + const auto rfind_convertible = input.rfind(convertible); + assert(rfind_convertible == 0); + + const auto rfind_convertible_pos = input.rfind(convertible, 5); + assert(rfind_convertible_pos == 0); + } + + { // find_first_of + const str input = get_literal_input(); + const str needle = get_cat(); + const str no_needle = get_no_needle(); + + const auto find_first_of_str = input.find_first_of(needle); + assert(find_first_of_str == 1u); + + const auto find_first_of_str_none = input.find_first_of(no_needle); + assert(find_first_of_str_none == str::npos); + + const auto find_first_of_str_pos = input.find_first_of(needle, 6); + assert(find_first_of_str_pos == 13u); + + const auto find_first_of_str_pos_none = input.find_first_of(no_needle, 14); + assert(find_first_of_str_pos_none == str::npos); + + const auto find_first_of_str_overflow = input.find_first_of(needle, 50); + assert(find_first_of_str_overflow == str::npos); + + const auto find_first_of_literal = input.find_first_of(get_cat()); + assert(find_first_of_literal == 1u); + + const auto find_first_of_literal_none = input.find_first_of(get_no_needle()); + assert(find_first_of_literal_none == str::npos); + + const auto find_first_of_literal_pos = input.find_first_of(get_cat(), 6); + assert(find_first_of_literal_pos == 13u); + + const auto find_first_of_literal_pos_none = input.find_first_of(get_no_needle(), 14); + assert(find_first_of_literal_pos_none == str::npos); + + const auto find_first_of_literal_overflow = input.find_first_of(get_cat(), 50); + assert(find_first_of_literal_overflow == str::npos); + + const auto find_first_of_literal_pos_count = input.find_first_of(get_cat(), 6, 4); + assert(find_first_of_literal_pos_count == 13u); + + const auto find_first_of_literal_pos_count_none = input.find_first_of(get_no_needle(), 14, 4); + assert(find_first_of_literal_pos_count_none == str::npos); + + const auto find_first_of_char = input.find_first_of(CharType{'e'}); + assert(find_first_of_char == 1u); + + const auto find_first_of_char_none = input.find_first_of(CharType{'x'}); + assert(find_first_of_char_none == str::npos); + + const auto find_first_of_char_pos = input.find_first_of(CharType{'e'}, 4); + assert(find_first_of_char_pos == 17u); + + const string_view_convertible convertible; + const auto find_first_of_convertible = input.find_first_of(convertible); + assert(find_first_of_convertible == 0); + + const auto find_first_of_convertible_pos = input.find_first_of(convertible, 2); + assert(find_first_of_convertible_pos == 2u); + } + + { // find_first_not_of + const str input = get_literal_input(); + const str needle = get_cat(); + const str no_needle = get_no_needle(); + + const auto find_first_not_of_str = input.find_first_not_of(needle); + assert(find_first_not_of_str == 0u); + + const auto find_first_not_of_str_none = input.find_first_not_of(input); + assert(find_first_not_of_str_none == str::npos); + + const auto find_first_not_of_str_pos = input.find_first_not_of(needle, 6); + assert(find_first_not_of_str_pos == 6u); + + const auto find_first_not_of_str_pos_none = input.find_first_not_of(input, 14); + assert(find_first_not_of_str_pos_none == str::npos); + + const auto find_first_not_of_str_overflow = input.find_first_not_of(needle, 50); + assert(find_first_not_of_str_overflow == str::npos); + + const auto find_first_not_of_literal = input.find_first_not_of(get_cat()); + assert(find_first_not_of_literal == 0u); + + const auto find_first_not_of_literal_none = input.find_first_not_of(get_literal_input()); + assert(find_first_not_of_literal_none == str::npos); + + const auto find_first_not_of_literal_pos = input.find_first_not_of(get_cat(), 6); + assert(find_first_not_of_literal_pos == 6u); + + const auto find_first_not_of_literal_pos_none = input.find_first_not_of(get_literal_input(), 2); + assert(find_first_not_of_literal_pos_none == str::npos); + + const auto find_first_not_of_literal_overflow = input.find_first_not_of(get_cat(), 50); + assert(find_first_not_of_literal_overflow == str::npos); + + const auto find_first_not_of_literal_pos_count = input.find_first_not_of(get_cat(), 6, 4); + assert(find_first_not_of_literal_pos_count == 6u); + + const auto find_first_not_of_literal_pos_count_none = + input.find_first_not_of(get_literal_input(), 14, 20); + assert(find_first_not_of_literal_pos_count_none == str::npos); + + const auto find_first_not_of_char = input.find_first_not_of(CharType{'H'}); + assert(find_first_not_of_char == 1u); + + const auto find_first_not_of_char_pos = input.find_first_not_of(CharType{'e'}, 1); + assert(find_first_not_of_char_pos == 2u); + + const string_view_convertible convertible; + const auto find_first_not_of_convertible = input.find_first_not_of(convertible); + assert(find_first_not_of_convertible == str::npos); + + const auto find_first_not_of_convertible_pos = input.find_first_not_of(convertible, 2); + assert(find_first_not_of_convertible_pos == str::npos); + } + + { // find_last_of + const str input = get_literal_input(); + const str needle = get_cat(); + const str no_needle = get_no_needle(); + + const auto find_last_of_str = input.find_last_of(needle); + assert(find_last_of_str == 18u); + + const auto find_last_of_str_none = input.find_last_of(no_needle); + assert(find_last_of_str_none == str::npos); + + const auto find_last_of_str_pos = input.find_last_of(needle, 6); + assert(find_last_of_str_pos == 1u); + + const auto find_last_of_str_pos_none = input.find_last_of(no_needle, 14); + assert(find_last_of_str_pos_none == str::npos); + + const auto find_last_of_str_overflow = input.find_last_of(needle, 50); + assert(find_last_of_str_overflow == 18u); + + const auto find_last_of_literal = input.find_last_of(get_cat()); + assert(find_last_of_literal == 18u); + + const auto find_last_of_literal_none = input.find_last_of(get_no_needle()); + assert(find_last_of_literal_none == str::npos); + + const auto find_last_of_literal_pos = input.find_last_of(get_cat(), 6); + assert(find_last_of_literal_pos == 1u); + + const auto find_last_of_literal_pos_none = input.find_last_of(get_no_needle(), 14); + assert(find_last_of_literal_pos_none == str::npos); + + const auto find_last_of_literal_overflow = input.find_last_of(get_cat(), 50); + assert(find_last_of_literal_overflow == 18u); + + const auto find_last_of_literal_pos_count = input.find_last_of(get_cat(), 6, 7); + assert(find_last_of_literal_pos_count == 1u); + + const auto find_last_of_literal_pos_count_none = input.find_last_of(get_no_needle(), 14, 4); + assert(find_last_of_literal_pos_count_none == str::npos); + + const auto find_last_of_char = input.find_last_of(CharType{'e'}); + assert(find_last_of_char == 17u); + + const auto find_last_of_char_none = input.find_last_of(CharType{'x'}); + assert(find_last_of_char_none == str::npos); + + const auto find_last_of_char_pos = input.find_last_of(CharType{'e'}, 4); + assert(find_last_of_char_pos == 1u); + + const string_view_convertible convertible; + const auto find_last_of_convertible = input.find_last_of(convertible); + assert(find_last_of_convertible == 19u); + + const auto find_last_of_convertible_pos = input.find_last_of(convertible, 4); + assert(find_last_of_convertible_pos == 4u); + } + + { // find_last_not_of + const str input = get_literal_input(); + const str needle = get_cat(); + const str no_needle = get_no_needle(); + + const auto find_last_not_of_str = input.find_last_not_of(needle); + assert(find_last_not_of_str == 19u); + + const auto find_last_not_of_str_none = input.find_last_not_of(input); + assert(find_last_not_of_str_none == str::npos); + + const auto find_last_not_of_str_pos = input.find_last_not_of(needle, 6); + assert(find_last_not_of_str_pos == 6u); + + const auto find_last_not_of_str_pos_none = input.find_last_not_of(input, 14); + assert(find_last_not_of_str_pos_none == str::npos); + + const auto find_last_not_of_str_overflow = input.find_last_not_of(needle, 50); + assert(find_last_not_of_str_overflow == 19u); + + const auto find_last_not_of_literal = input.find_last_not_of(get_cat()); + assert(find_last_not_of_literal == 19u); + + const auto find_last_not_of_literal_none = input.find_last_not_of(get_literal_input()); + assert(find_last_not_of_literal_none == str::npos); + + const auto find_last_not_of_literal_pos = input.find_last_not_of(get_cat(), 6); + assert(find_last_not_of_literal_pos == 6u); + + const auto find_last_not_of_literal_pos_none = input.find_last_not_of(get_literal_input(), 2); + assert(find_last_not_of_literal_pos_none == str::npos); + + const auto find_last_not_of_literal_overflow = input.find_last_not_of(get_cat(), 50); + assert(find_last_not_of_literal_overflow == 19u); + + const auto find_last_not_of_literal_pos_count = input.find_last_not_of(get_cat(), 6, 4); + assert(find_last_not_of_literal_pos_count == 6u); + + const auto find_last_not_of_literal_pos_count_none = + input.find_last_not_of(get_literal_input(), 14, 20); + assert(find_last_not_of_literal_pos_count_none == str::npos); + + const auto find_last_not_of_char = input.find_last_not_of(CharType{'H'}); + assert(find_last_not_of_char == 19u); + + const auto find_last_not_of_char_pos = input.find_last_not_of(CharType{'e'}, 2); + assert(find_last_not_of_char_pos == 2u); + + const string_view_convertible convertible; + const auto find_last_not_of_convertible = input.find_last_not_of(convertible); + assert(find_last_not_of_convertible == str::npos); + + const auto find_last_not_of_convertible_pos = input.find_last_not_of(convertible, 2); + assert(find_last_not_of_convertible_pos == str::npos); + } + + { // operator+ + const str first = get_cat(); + const str second = get_dog(); + + const str op_str_str = first + second; + assert(equalRanges(op_str_str, "kittendog"sv)); + + const str op_str_literal = first + get_dog(); + assert(equalRanges(op_str_literal, "kittendog"sv)); + + const str op_str_char = first + CharType{'!'}; + assert(equalRanges(op_str_char, "kitten!"sv)); + + const str op_literal_str = get_cat() + second; + assert(equalRanges(op_literal_str, "kittendog"sv)); + + const str op_char_str = CharType{'!'} + second; + assert(equalRanges(op_char_str, "!dog"sv)); + + const str op_rstr_rstr = str{get_cat()} + str{get_dog()}; + assert(equalRanges(op_rstr_rstr, "kittendog"sv)); + + const str op_rstr_str = str{get_cat()} + second; + assert(equalRanges(op_rstr_str, "kittendog"sv)); + + const str op_rstr_literal = str{get_cat()} + get_dog(); + assert(equalRanges(op_rstr_literal, "kittendog"sv)); + + const str op_rstr_char = str{get_cat()} + CharType{'!'}; + assert(equalRanges(op_rstr_char, "kitten!"sv)); + + const str op_str_rstr = first + str{get_dog()}; + assert(equalRanges(op_str_rstr, "kittendog"sv)); + + const str op_literal_rstr = get_cat() + str{get_dog()}; + assert(equalRanges(op_literal_rstr, "kittendog"sv)); + + const str op_char_rstr = CharType{'!'} + str{get_dog()}; + assert(equalRanges(op_char_rstr, "!dog"sv)); + } + + { // comparison + str first(get_view_input()); + str second(get_view_input()); + str third{get_cat()}; + + const bool eq_str_str = first == second; + assert(eq_str_str); + + const bool ne_str_str = first != third; + assert(ne_str_str); + + const bool less_str_str = first < third; + assert(less_str_str); + + const bool less_eq_str_str = first <= third; + assert(less_eq_str_str); + + const bool greater_str_str = first > third; + assert(!greater_str_str); + + const bool greater_eq_str_str = first >= third; + assert(!greater_eq_str_str); + + const bool eq_str_literal = first == get_view_input(); + assert(eq_str_literal); + + const bool ne_str_literal = first != get_cat(); + assert(ne_str_literal); + + const bool less_str_literal = first < get_cat(); + assert(less_str_literal); + + const bool less_eq_str_literal = first <= get_cat(); + assert(less_eq_str_literal); + + const bool greater_str_literal = first > get_cat(); + assert(!greater_str_literal); + + const bool greater_eq_str_literal = first >= get_cat(); + assert(!greater_eq_str_literal); + + const bool eq_literal_str = get_view_input() == second; + assert(eq_literal_str); + + const bool ne_literal_str = get_view_input() != third; + assert(ne_literal_str); + + const bool less_literal_str = get_view_input() < third; + assert(less_literal_str); + + const bool less_eq_literal_str = get_view_input() <= third; + assert(less_eq_literal_str); + + const bool greater_literal_str = get_view_input() > third; + assert(!greater_literal_str); + + const bool greater_eq_literal_str = get_view_input() >= third; + assert(!greater_eq_literal_str); + + const strong_ordering spaceship_str_str_eq = first <=> second; + assert(spaceship_str_str_eq == strong_ordering::equal); + + const strong_ordering spaceship_str_str_less = first <=> third; + assert(spaceship_str_str_less == strong_ordering::less); + + const strong_ordering spaceship_str_str_greater = third <=> first; + assert(spaceship_str_str_greater == strong_ordering::greater); + + const strong_ordering spaceship_str_literal_eq = first <=> get_view_input(); + assert(spaceship_str_literal_eq == strong_ordering::equal); + + const strong_ordering spaceship_str_literal_less = first <=> get_cat(); + assert(spaceship_str_literal_less == strong_ordering::less); + + const strong_ordering spaceship_str_literal_greater = third <=> get_dog(); + assert(spaceship_str_literal_greater == strong_ordering::greater); + } + + { // basic_string_view conversion + str s = get_literal_input(); + basic_string_view sv = s; + assert(equalRanges(sv, "Hello fluffy kittens"sv)); + } +#endif // __EDG__ + return true; +} + +_CONSTEXPR20_CONTAINER bool test_udls() { +#ifndef __EDG__ // TRANSITION, VSO-1273296 + assert(equalRanges("purr purr"s, "purr purr"sv)); +#ifdef __cpp_char8_t + assert(equalRanges(u8"purr purr"s, "purr purr"sv)); +#endif // __cpp_char8_t + assert(equalRanges(u"purr purr"s, "purr purr"sv)); + assert(equalRanges(U"purr purr"s, "purr purr"sv)); + assert(equalRanges(L"purr purr"s, "purr purr"sv)); +#endif // __EDG__ + return true; +} + +template +struct CharLikeType { + constexpr CharLikeType() = default; + constexpr CharLikeType(CharType cc) : c(cc) {} + CharType c; +}; + +template +_CONSTEXPR20_CONTAINER bool test_iterators() { +#ifndef __EDG__ // TRANSITION, VSO-1273296 + using str = basic_string; + str literal_constructed = get_literal_input(); + + { // assignment + auto it = literal_constructed.begin(); + auto it2 = literal_constructed.end(); + auto cit = literal_constructed.cbegin(); + auto cit2 = literal_constructed.cend(); + + it = it2; + cit = cit2; + } + + { // op-> + basic_string> bs{CharType{'x'}}; + auto it = bs.begin(); + auto c = it->c; + assert(c == CharType{'x'}); + + auto cit = bs.cbegin(); + auto cc = cit->c; + assert(cc == CharType{'x'}); + } + + { // increment + auto it = literal_constructed.begin(); + assert(*++it == CharType{'e'}); + assert(*it++ == CharType{'e'}); + assert(*it == CharType{'l'}); + + auto cit = literal_constructed.cbegin(); + assert(*++cit == CharType{'e'}); + assert(*cit++ == CharType{'e'}); + assert(*cit == CharType{'l'}); + } + + { // advance + auto it = literal_constructed.begin() + 2; + assert(*it == CharType{'l'}); + it += 2; + assert(*it == CharType{'o'}); + it = 2 + it; + assert(*it == CharType{'f'}); + + auto cit = literal_constructed.cbegin() + 2; + assert(*cit == CharType{'l'}); + cit += 2; + assert(*cit == CharType{'o'}); + cit = 2 + cit; + assert(*cit == CharType{'f'}); + } + + { // decrement + auto it = literal_constructed.end(); + assert(*--it == CharType{'s'}); + assert(*it-- == CharType{'s'}); + assert(*it == CharType{'n'}); + + auto cit = literal_constructed.cend(); + assert(*--cit == CharType{'s'}); + assert(*cit-- == CharType{'s'}); + assert(*cit == CharType{'n'}); + } + + { // advance back + auto it = literal_constructed.end() - 2; + assert(*it == CharType{'n'}); + it -= 2; + assert(*it == CharType{'t'}); + + auto cit = literal_constructed.cend() - 2; + assert(*cit == CharType{'n'}); + cit -= 2; + assert(*cit == CharType{'t'}); + } + + { // difference + const auto it1 = literal_constructed.begin(); + const auto it2 = literal_constructed.end(); + assert(it2 - it1 == ssize(get_view_input())); + + const auto cit1 = literal_constructed.cbegin(); + const auto cit2 = literal_constructed.cend(); + assert(cit2 - cit1 == ssize(get_view_input())); + + assert(it2 - cit1 == ssize(get_view_input())); + assert(cit2 - it1 == ssize(get_view_input())); + } + + { // comparison + const auto it1 = literal_constructed.begin(); + const auto it2 = literal_constructed.begin(); + const auto it3 = literal_constructed.end(); + + assert(it1 == it2); + assert(it1 != it3); + assert(it1 < it3); + assert(it1 <= it3); + assert(it3 > it1); + assert(it3 >= it1); + + assert((it1 <=> it2) == strong_ordering::equal); + assert((it1 <=> it3) == strong_ordering::less); + assert((it3 <=> it1) == strong_ordering::greater); + } + + { // access + const auto it = literal_constructed.begin() + 2; + it[2] = CharType{'l'}; + assert(literal_constructed[4] == CharType{'l'}); + + const auto cit = literal_constructed.cbegin() + 2; + assert(cit[2] == CharType{'l'}); + } +#endif // __EDG__ + return true; +} + +template +_CONSTEXPR20_CONTAINER bool test_growth() { + using str = basic_string; +#ifndef __EDG__ // TRANSITION, VSO-1273296 + { + str v(1007, CharType{'a'}); + + assert(v.size() == 1007); + assert(v.capacity() == 1007); + + v.resize(1008); + + assert(v.size() == 1008); + assert(v.capacity() == 1510); + } + + { + str v(1007, CharType{'a'}); + + assert(v.size() == 1007); + assert(v.capacity() == 1007); + + v.resize(8007); + + assert(v.size() == 8007); + if constexpr (is_same_v || is_same_v || is_same_v) { + assert(v.capacity() == 8007); + } else { + assert(v.capacity() == 8015); + } + } + + { + str v(1007, CharType{'a'}); + + assert(v.size() == 1007); + assert(v.capacity() == 1007); + + v.push_back(CharType{'b'}); + + assert(v.size() == 1008); + assert(v.capacity() == 1510); + } + + { + str v(1007, CharType{'a'}); + + assert(v.size() == 1007); + assert(v.capacity() == 1007); + + str l(3, CharType{'b'}); + + v.insert(v.end(), l.begin(), l.end()); + + assert(v.size() == 1010); + assert(v.capacity() == 1510); + } + + { + str v(1007, CharType{'a'}); + + assert(v.size() == 1007); + assert(v.capacity() == 1007); + + str l(7000, CharType{'b'}); + + v.insert(v.end(), l.begin(), l.end()); + + assert(v.size() == 8007); + if constexpr (is_same_v || is_same_v || is_same_v) { + assert(v.capacity() == 8007); + } else { + assert(v.capacity() == 8015); + } + } + + { + str v(1007, CharType{'a'}); + + assert(v.size() == 1007); + assert(v.capacity() == 1007); + + v.insert(v.end(), 3, CharType{'b'}); + + assert(v.size() == 1010); + assert(v.capacity() == 1510); + } + + { + str v(1007, CharType{'a'}); + + assert(v.size() == 1007); + assert(v.capacity() == 1007); + + v.insert(v.end(), 7000, CharType{'b'}); + + assert(v.size() == 8007); + if constexpr (is_same_v || is_same_v || is_same_v) { + assert(v.capacity() == 8007); + } else { + assert(v.capacity() == 8015); + } + } +#endif // __EDG__ + return true; +} + +int main() { + test_interface(); +#ifdef __cpp_char8_t + test_interface(); +#endif // __cpp_char8_t + test_interface(); + test_interface(); + test_interface(); + + test_udls(); + + test_iterators(); +#ifdef __cpp_char8_t + test_iterators(); +#endif // __cpp_char8_t + test_iterators(); + test_iterators(); + test_iterators(); + + test_growth(); +#ifdef __cpp_char8_t + test_growth(); +#endif // __cpp_char8_t + test_growth(); + test_growth(); + test_growth(); + +#ifdef __cpp_lib_constexpr_string + static_assert(test_interface()); +#ifdef __cpp_char8_t + static_assert(test_interface()); +#endif // __cpp_char8_t + static_assert(test_interface()); + static_assert(test_interface()); + static_assert(test_interface()); + + static_assert(test_udls()); + + static_assert(test_iterators()); +#ifdef __cpp_char8_t + static_assert(test_iterators()); +#endif // __cpp_char8_t + static_assert(test_iterators()); + static_assert(test_iterators()); + static_assert(test_iterators()); + + static_assert(test_growth()); +#ifdef __cpp_char8_t + static_assert(test_growth()); +#endif // __cpp_char8_t + static_assert(test_growth()); + static_assert(test_growth()); + static_assert(test_growth()); +#endif // __cpp_lib_constexpr_string +} diff --git a/tests/std/tests/P1004R2_constexpr_vector/test.cpp b/tests/std/tests/P1004R2_constexpr_vector/test.cpp index f6cc0a4dea6..eaee72eec1f 100644 --- a/tests/std/tests/P1004R2_constexpr_vector/test.cpp +++ b/tests/std/tests/P1004R2_constexpr_vector/test.cpp @@ -12,8 +12,7 @@ using namespace std; -#if defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 \ - || defined(MSVC_INTERNAL_TESTING) // TRANSITION, VSO-1270433, VSO-1275530 +#if defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 || defined(MSVC_INTERNAL_TESTING) // TRANSITION, VSO-1270433 static constexpr int input[] = {0, 1, 2, 3, 4, 5}; #endif // defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 || defined(MSVC_INTERNAL_TESTING) @@ -64,8 +63,7 @@ struct soccc_allocator { using vec = vector>; _CONSTEXPR20_CONTAINER bool test_interface() { -#if defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 \ - || defined(MSVC_INTERNAL_TESTING) // TRANSITION, VSO-1270433, VSO-1275530 +#if defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 || defined(MSVC_INTERNAL_TESTING) // TRANSITION, VSO-1270433 { // constructors // Non allocator constructors @@ -513,8 +511,7 @@ _CONSTEXPR20_CONTAINER bool test_interface() { } _CONSTEXPR20_CONTAINER bool test_iterators() { -#if defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 \ - || defined(MSVC_INTERNAL_TESTING) // TRANSITION, VSO-1270433, VSO-1275530 +#if defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 || defined(MSVC_INTERNAL_TESTING) // TRANSITION, VSO-1270433 vec range_constructed(begin(input), end(input)); #if !defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1273381 @@ -614,8 +611,7 @@ _CONSTEXPR20_CONTAINER bool test_iterators() { } _CONSTEXPR20_CONTAINER bool test_growth() { -#if defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 \ - || defined(MSVC_INTERNAL_TESTING) // TRANSITION, VSO-1270433, VSO-1275530 +#if defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 || defined(MSVC_INTERNAL_TESTING) // TRANSITION, VSO-1270433 { vector v(1000, 1729); diff --git a/tests/std/tests/P1004R2_constexpr_vector_bool/test.cpp b/tests/std/tests/P1004R2_constexpr_vector_bool/test.cpp index c1441f02775..1124ad5e15a 100644 --- a/tests/std/tests/P1004R2_constexpr_vector_bool/test.cpp +++ b/tests/std/tests/P1004R2_constexpr_vector_bool/test.cpp @@ -13,8 +13,7 @@ using namespace std; -#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) \ - || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1270433, VSO-1275530 +#if defined(MSVC_INTERNAL_TESTING) || defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 // TRANSITION, VSO-1270433 #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-const-variable" // TRANSITION, LLVM-48606 @@ -73,8 +72,7 @@ struct soccc_allocator { using vec = vector>; _CONSTEXPR20_CONTAINER bool test_interface() { -#if defined(__EDG__) \ - || _ITERATOR_DEBUG_LEVEL != 2 // || defined(MSVC_INTERNAL_TESTING) // TRANSITION, VSO-1270433, VSO-1275530 +#if defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 || defined(MSVC_INTERNAL_TESTING) // TRANSITION, VSO-1270433 { // constructors // Non allocator constructors @@ -541,8 +539,7 @@ _CONSTEXPR20_CONTAINER bool test_interface() { } _CONSTEXPR20_CONTAINER bool test_iterators() { -#if defined(__EDG__) \ - || _ITERATOR_DEBUG_LEVEL != 2 // || defined(MSVC_INTERNAL_TESTING) // TRANSITION, VSO-1270433, VSO-1275530 +#if defined(__EDG__) || _ITERATOR_DEBUG_LEVEL != 2 || defined(MSVC_INTERNAL_TESTING) // TRANSITION, VSO-1270433 #ifndef __EDG__ // TRANSITION, VSO-1274387 vec range_constructed(begin(input), end(input)); diff --git a/tests/std/tests/P1208R6_source_location/env.lst b/tests/std/tests/P1208R6_source_location/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P1208R6_source_location/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P1208R6_source_location/header.h b/tests/std/tests/P1208R6_source_location/header.h new file mode 100644 index 00000000000..a16d321ee66 --- /dev/null +++ b/tests/std/tests/P1208R6_source_location/header.h @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#include +#include +#include + +constexpr void header_test() { + using namespace std; + const auto x = source_location::current(); + assert(x.line() == __LINE__ - 1); + assert(x.column() == 37); + assert(x.function_name() == "header_test"sv); + assert(string_view{x.file_name()}.ends_with("header.h"sv)); +} diff --git a/tests/std/tests/P1208R6_source_location/test.cpp b/tests/std/tests/P1208R6_source_location/test.cpp new file mode 100644 index 00000000000..547c0b7130a --- /dev/null +++ b/tests/std/tests/P1208R6_source_location/test.cpp @@ -0,0 +1,160 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#if defined(__cpp_consteval) && !defined(__EDG__) // TRANSITION, VSO-1285779 +#include "header.h" +#include +#include +#include +#include +using namespace std; + +static_assert(is_nothrow_default_constructible_v); +static_assert(is_nothrow_move_constructible_v); +static_assert(is_nothrow_move_assignable_v); +static_assert(is_nothrow_swappable_v); + +constexpr auto test_cpp = "test.cpp"sv; + +constexpr auto g = source_location::current(); +static_assert(g.line() == __LINE__ - 1); +static_assert(g.column() == 37); +static_assert(g.function_name() == ""sv); +static_assert(string_view{g.file_name()}.ends_with(test_cpp)); + +constexpr int s_int_line = __LINE__ + 3; +struct s { + constexpr s(const source_location x = source_location::current()) : loc(x) {} + constexpr s(int) {} + source_location loc = source_location::current(); +}; + +constexpr int s2_int_line = __LINE__ + 3; +struct s2 { + constexpr s2(const source_location l = source_location::current()) : x{l} {} + constexpr s2(int) {} + s x = source_location::current(); +}; + +constexpr void copy_test() { + const auto rhs = source_location::current(); + const auto lhs = rhs; + assert(lhs.line() == rhs.line()); + assert(lhs.column() == rhs.column()); + assert(string_view{lhs.function_name()} == string_view{rhs.function_name()}); + assert(string_view{lhs.file_name()} == string_view{rhs.file_name()}); +} + +constexpr void local_test() { + const auto x = source_location::current(); + assert(x.line() == __LINE__ - 1); + assert(x.column() == 37); + assert(x.function_name() == "local_test"sv); + assert(string_view{x.file_name()}.ends_with(test_cpp)); +} + +constexpr void argument_test( + const unsigned int line, const unsigned int column, const source_location x = source_location::current()) { + assert(x.line() == line); + assert(x.column() == column); + assert(x.function_name() == "test"sv); + assert(string_view{x.file_name()}.ends_with(test_cpp)); +} + +constexpr void sloc_constructor_test() { + const s x; + assert(x.loc.line() == __LINE__ - 1); +#ifdef _PREFAST_ + assert(x.loc.column() == 14); +#else // _PREFAST_ + assert(x.loc.column() == 13); +#endif // _PREFAST_ + if (is_constant_evaluated()) { + assert(x.loc.function_name() == "main"sv); // TRANSITION, VSO-1285783 + } else { + assert(x.loc.function_name() == "sloc_constructor_test"sv); + } + assert(string_view{x.loc.file_name()}.ends_with(test_cpp)); +} + +constexpr void different_constructor_test() { + const s x{1}; + assert(x.loc.line() == s_int_line); + assert(x.loc.column() == 5); + assert(x.loc.function_name() == "s"sv); + assert(string_view{x.loc.file_name()}.ends_with(test_cpp)); +} + +constexpr void sub_member_test() { + const s2 s; + assert(s.x.loc.line() == __LINE__ - 1); +#ifdef _PREFAST_ + assert(s.x.loc.column() == 15); +#else // _PREFAST_ + assert(s.x.loc.column() == 14); +#endif // _PREFAST_ + if (is_constant_evaluated()) { + assert(s.x.loc.function_name() == "main"sv); // TRANSITION, VSO-1285783 + } else { + assert(s.x.loc.function_name() == "sub_member_test"sv); + } + assert(string_view{s.x.loc.file_name()}.ends_with(test_cpp)); + + const s2 s_i{1}; + assert(s_i.x.loc.line() == s2_int_line); + assert(s_i.x.loc.column() == 5); + assert(s_i.x.loc.function_name() == "s2"sv); + assert(string_view{s_i.x.loc.file_name()}.ends_with(test_cpp)); +} + +constexpr void lambda_test() { + const auto l = [loc = source_location::current()] { return loc; }; + const auto x = l(); + assert(x.line() == __LINE__ - 2); + assert(x.column() == 51); + assert(x.function_name() == "lambda_test"sv); + assert(string_view{x.file_name()}.ends_with(test_cpp)); +} + +template +constexpr source_location function_template() { + return source_location::current(); +} + +constexpr void function_template_test() { + const auto x1 = function_template(); + assert(x1.line() == __LINE__ - 5); + assert(x1.column() == 29); + assert(x1.function_name() == "function_template"sv); + assert(string_view{x1.file_name()}.ends_with(test_cpp)); + + const auto x2 = function_template(); + assert(x1.line() == x2.line()); + assert(x1.column() == x2.column()); + assert(string_view{x1.function_name()} == string_view{x2.function_name()}); + assert(string_view{x1.file_name()} == string_view{x2.file_name()}); +} + +constexpr bool test() { + copy_test(); + local_test(); + argument_test(__LINE__, 5); + const auto loc = source_location::current(); + argument_test(__LINE__ - 1, 39, loc); + sloc_constructor_test(); + different_constructor_test(); + sub_member_test(); + lambda_test(); + function_template_test(); + header_test(); + return true; +} + +int main() { + test(); + static_assert(test()); + return 0; +} +#else // ^^^ defined(__cpp_consteval) && !defined(__EDG__) / !defined(__cpp_consteval) || defined(__EDG__) vvv +int main() {} +#endif // ^^^ !defined(__cpp_consteval) || defined(__EDG__) ^^^ diff --git a/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py b/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py index ddf75ebbd8d..d86678b8fd0 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py +++ b/tests/std/tests/P1502R1_standard_library_header_units/custom_format.py @@ -63,7 +63,7 @@ def getBuildSteps(self, test, litConfig, shared): 'semaphore', 'set', 'shared_mutex', - # 'source_location', + 'source_location', 'span', 'sstream', 'stack', diff --git a/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl b/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl index 2ccde3fc6e8..37dff666bd4 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl +++ b/tests/std/tests/P1502R1_standard_library_header_units/custombuild.pl @@ -62,7 +62,7 @@ () "semaphore", "set", "shared_mutex", - # "source_location", + "source_location", "span", "sstream", "stack", diff --git a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp index 8b0e829db32..2102a025ea9 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -63,7 +63,7 @@ import ; import ; import ; import ; -// import ; +import ; import ; import ; import ; @@ -94,6 +94,17 @@ import ; #include using namespace std; +constexpr bool test_source_location() { +#ifdef __cpp_lib_source_location + const auto sl = source_location::current(); + assert(sl.line() == __LINE__ - 1); + assert(sl.column() == 1); + assert(sl.function_name() == "test_source_location"sv); + assert(string_view{sl.file_name()}.ends_with("test.cpp"sv)); +#endif // __cpp_lib_source_location + return true; +} + int main() { { puts("Testing ."); @@ -115,7 +126,7 @@ int main() { { puts("Testing ."); -#if 0 // TRANSITION, VSO-1088552 (deduction guides) +#ifdef MSVC_INTERNAL_TESTING // TRANSITION, VSO-1088552 (deduction guides) constexpr array arr{10, 20, 30, 40, 50}; #else // ^^^ no workaround / workaround vvv constexpr array arr{10, 20, 30, 40, 50}; @@ -324,13 +335,9 @@ int main() { puts("Testing ."); promise p{}; future f{p.get_future()}; -#ifdef MSVC_INTERNAL_TESTING // TRANSITION, VSO-1271718 (Standard Library Header Units ICE with C++20 chrono) assert(f.wait_for(chrono::seconds{0}) == future_status::timeout); -#endif // ^^^ no workaround ^^^ p.set_value(1729); -#ifdef MSVC_INTERNAL_TESTING // TRANSITION, VSO-1271718 (Standard Library Header Units ICE with C++20 chrono) assert(f.wait_for(chrono::seconds{0}) == future_status::ready); -#endif // ^^^ no workaround ^^^ assert(f.get() == 1729); } @@ -574,7 +581,7 @@ int main() { { puts("Testing ."); constexpr int arr[]{11, 0, 22, 0, 33, 0, 44, 0, 55}; -#if 0 // TRANSITION, VSO-1088552 (deduction guides) +#ifdef MSVC_INTERNAL_TESTING // TRANSITION, VSO-1088552 (deduction guides) assert(ranges::distance(views::filter(arr, [](int x) { return x == 0; })) == 4); static_assert(ranges::distance(views::filter(arr, [](int x) { return x != 0; })) == 5); #else // ^^^ no workaround / workaround vvv @@ -689,7 +696,8 @@ int main() { { puts("Testing ."); - puts("(TRANSITION, not yet implemented.)"); + assert(test_source_location()); + static_assert(test_source_location()); } { @@ -760,9 +768,7 @@ int main() { } l.count_down(); // tell main() that we're done while (!token.stop_requested()) { -#ifdef MSVC_INTERNAL_TESTING // TRANSITION, VSO-1271718 (Standard Library Header Units ICE with C++20 chrono) this_thread::sleep_for(10ms); // not a timing assumption; avoids spinning furiously -#endif // ^^^ no workaround ^^^ } vec.push_back(-1000); // indicate that token.stop_requested() returned true }}; diff --git a/tests/std/tests/P1614R2_spaceship/test.cpp b/tests/std/tests/P1614R2_spaceship/test.cpp index 37fb868db72..2b2e73691e5 100644 --- a/tests/std/tests/P1614R2_spaceship/test.cpp +++ b/tests/std/tests/P1614R2_spaceship/test.cpp @@ -1,11 +1,9 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// Covers: -// * spaceship for containers - #include #include +#include #include #include #include @@ -14,27 +12,36 @@ #include #include #include +#include #include #include #include +#include +#include +#include +#include #include #include #include #include +#include #include #include #include +#include #include #include +#include #include +#include +#include #include #include #include +#include +#include #include -template -using SpaceshipType = decltype(std::declval() <=> std::declval()); - template concept HasSpaceshipWith = requires { std::declval() <=> std::declval(); @@ -142,7 +149,7 @@ struct dummy_diagnostic : std::error_category { }; template -void spaceship_test(const SmallType& smaller, const EqualType& smaller_equal, const LargeType& larger) { +constexpr bool spaceship_test(const SmallType& smaller, const EqualType& smaller_equal, const LargeType& larger) { assert(smaller == smaller_equal); assert(smaller_equal == smaller); assert(smaller != larger); @@ -160,18 +167,22 @@ void spaceship_test(const SmallType& smaller, const EqualType& smaller_equal, co assert((smaller <=> smaller_equal) == 0); static_assert(std::is_same_v larger), ReturnType>); + + return true; } template -inline constexpr bool is_pair = false; -template -inline constexpr bool is_pair> = true; // TRANSITION, std::pair spaceship not yet implemented +inline constexpr bool has_synth_ordered = false; +template +inline constexpr bool has_synth_ordered> = true; +template <> +inline constexpr bool has_synth_ordered = true; template void ordered_containers_test(const Container& smaller, const Container& smaller_equal, const Container& larger) { using Elem = typename Container::value_type; - if constexpr (is_pair // TRANSITION, std::pair spaceship not yet implemented - || std::is_same_v) { + + if constexpr (has_synth_ordered) { spaceship_test(smaller, smaller_equal, larger); } else { spaceship_test(smaller, smaller_equal, larger); @@ -185,6 +196,27 @@ void unordered_containers_test( assert(something != different); } +template +void ordered_iterator_test(const Iter& smaller, const Iter& smaller_equal, const Iter& larger, + const ConstIter& const_smaller, const ConstIter& const_smaller_equal, const ConstIter& const_larger) { + spaceship_test(smaller, smaller_equal, larger); + spaceship_test(const_smaller, const_smaller_equal, const_larger); + spaceship_test(const_smaller, smaller_equal, larger); +} + +template +void unordered_iterator_test(const Iter& something, const Iter& something_equal, const Iter& different, + const ConstIter& const_something, const ConstIter& const_something_equal, const ConstIter& const_different) { + assert(something == something_equal); + assert(something != different); + + assert(const_something == const_something_equal); + assert(const_something != const_different); + + assert(something == const_something_equal); + assert(something != const_different); +} + template void diagnostics_test() { dummy_diagnostic c_mem[2]; @@ -215,7 +247,773 @@ void diagnostics_test() { } } +template