diff --git a/.vscode/settings.json b/.vscode/settings.json index 5a97ba29cee..5312b7ed5b4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,6 +5,7 @@ "editor.formatOnSave": true, "files.associations": { ".clang-format": "yaml", + "header-units.json": "jsonc", "**/stl/inc/**": "cpp" }, "files.eol": "\r\n", diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e712f36ad8..8b166a3d5a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ if (NOT DEFINED CMAKE_TOOLCHAIN_FILE AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/vcpkg set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_LIST_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake") endif() -cmake_minimum_required(VERSION 3.18) +cmake_minimum_required(VERSION 3.19) set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) project(msvc_standard_libraries LANGUAGES CXX) diff --git a/README.md b/README.md index 570e9cedcf9..40546698950 100644 --- a/README.md +++ b/README.md @@ -143,10 +143,10 @@ 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 2 or later. +1. Install Visual Studio 2019 16.9 Preview 3 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.18 or later, and [Ninja][] 1.8.2 or later. + * Otherwise, install [CMake][] 3.19 or later, and [Ninja][] 1.10.2 or later. 2. Open Visual Studio, and choose the "Clone or check out code" option. Enter the URL of this repository, `https://github.com/microsoft/STL`. 3. Open a terminal in the IDE with `` Ctrl + ` `` (by default) or press on "View" in the top bar, and then "Terminal". @@ -158,10 +158,10 @@ acquire this dependency. # How To Build With A Native Tools Command Prompt -1. Install Visual Studio 2019 16.9 Preview 2 or later. +1. Install Visual Studio 2019 16.9 Preview 3 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.18 or later, and [Ninja][] 1.8.2 or later. + * Otherwise, install [CMake][] 3.19 or later, and [Ninja][] 1.10.2 or later. 2. Open a command prompt. 3. Change directories to a location where you'd like a clone of this STL repository. 4. `git clone https://github.com/microsoft/STL` @@ -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 or newer and have it on the `PATH` (or run it directly using its absolute or relative path). +2. Acquire [Python][] 3.9.1 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. diff --git a/azure-devops/create-vmss.ps1 b/azure-devops/create-vmss.ps1 index 1434ce0ac48..963fb7f874a 100644 --- a/azure-devops/create-vmss.ps1 +++ b/azure-devops/create-vmss.ps1 @@ -17,6 +17,9 @@ or are running from Azure Cloud Shell. $ErrorActionPreference = 'Stop' +# https://aka.ms/azps-changewarnings +$Env:SuppressAzurePowerShellBreakingChangeWarnings = 'true' + $Location = 'westus2' $Prefix = 'StlBuild-' + (Get-Date -Format 'yyyy-MM-dd') $VMSize = 'Standard_D32as_v4' diff --git a/azure-devops/provision-image.ps1 b/azure-devops/provision-image.ps1 index ba9e940c575..8468b850ad6 100644 --- a/azure-devops/provision-image.ps1 +++ b/azure-devops/provision-image.ps1 @@ -50,36 +50,75 @@ Function Get-TempFilePath { return Join-Path $tempPath $tempName } +<# +.SYNOPSIS +Downloads and extracts a ZIP file to a newly created temporary subdirectory. + +.DESCRIPTION +DownloadAndExtractZip returns a path containing the extracted contents. + +.PARAMETER Url +The URL of the ZIP file to download. +#> +Function DownloadAndExtractZip { + Param( + [String]$Url + ) + + if ([String]::IsNullOrWhiteSpace($Url)) { + throw 'Missing Url' + } + + $ZipPath = Get-TempFilePath -Extension 'zip' + & curl.exe -L -o $ZipPath -s -S $Url + $TempSubdirPath = Get-TempFilePath -Extension 'dir' + Expand-Archive -Path $ZipPath -DestinationPath $TempSubdirPath -Force + + return $TempSubdirPath +} + $TranscriptPath = 'C:\provision-image-transcript.txt' if ([string]::IsNullOrEmpty($AdminUserPassword)) { - Start-Transcript -Path $TranscriptPath + Start-Transcript -Path $TranscriptPath -UseMinimalHeader } else { Write-Host 'AdminUser password supplied; switching to AdminUser.' - $PsExecPath = Get-TempFilePath -Extension 'exe' - Write-Host "Downloading psexec to: $PsExecPath" - & curl.exe -L -o $PsExecPath -s -S https://live.sysinternals.com/PsExec64.exe + + # https://docs.microsoft.com/en-us/sysinternals/downloads/psexec + $PsToolsZipUrl = 'https://download.sysinternals.com/files/PSTools.zip' + Write-Host "Downloading: $PsToolsZipUrl" + $ExtractedPsToolsPath = DownloadAndExtractZip -Url $PsToolsZipUrl + $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' + Write-Host "Downloading: $PowerShellZipUrl" + $ExtractedPowerShellPath = DownloadAndExtractZip -Url $PowerShellZipUrl + $PwshPath = Join-Path $ExtractedPowerShellPath 'pwsh.exe' + $PsExecArgs = @( '-u', 'AdminUser', '-p', - $AdminUserPassword, + 'AdminUserPassword_REDACTED', '-accepteula', + '-i', '-h', - 'C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe', + $PwshPath, '-ExecutionPolicy', 'Unrestricted', '-File', $PSCommandPath ) - Write-Host "Executing: $PsExecPath $PsExecArgs" + $PsExecArgs[3] = $AdminUserPassword $proc = Start-Process -FilePath $PsExecPath -ArgumentList $PsExecArgs -Wait -PassThru Write-Host 'Reading transcript...' Get-Content -Path $TranscriptPath Write-Host 'Cleaning up...' - Remove-Item $PsExecPath + Remove-Item -Recurse -Path $ExtractedPsToolsPath + Remove-Item -Recurse -Path $ExtractedPowerShellPath exit $proc.ExitCode } @@ -100,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.0/python-3.9.0-amd64.exe' +$PythonUrl = 'https://www.python.org/ftp/python/3.9.1/python-3.9.1-amd64.exe' # https://docs.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk $WindowsDriverKitUrl = 'https://go.microsoft.com/fwlink/?linkid=2128854' @@ -315,7 +354,7 @@ Function PipInstall { try { Write-Host "Installing or upgrading $Package..." - python.exe -m pip install --upgrade $Package + python.exe -m pip install --progress-bar off --upgrade $Package Write-Host "Done installing or upgrading $Package." } catch { @@ -358,3 +397,9 @@ Write-Host 'Finished updating PATH!' PipInstall pip PipInstall psutil + +# https://docs.microsoft.com/en-us/windows-hardware/drivers/devtest/bcdedit--set#verification-settings +Write-Host 'Enabling test-signed kernel-mode drivers...' +bcdedit /set testsigning on + +Write-Host 'Done!' diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 3fa907668b9..f342d73784b 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -6,7 +6,7 @@ variables: tmpDir: 'D:\Temp' -pool: 'StlBuild-2020-12-08-1' +pool: 'StlBuild-2021-01-20-2' stages: - stage: Code_Format diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index 394f28cd064..6f2686e6de2 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -146,6 +146,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/future ${CMAKE_CURRENT_LIST_DIR}/inc/hash_map ${CMAKE_CURRENT_LIST_DIR}/inc/hash_set + ${CMAKE_CURRENT_LIST_DIR}/inc/header-units.json ${CMAKE_CURRENT_LIST_DIR}/inc/initializer_list ${CMAKE_CURRENT_LIST_DIR}/inc/iomanip ${CMAKE_CURRENT_LIST_DIR}/inc/ios diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 9146dd1077f..8eb7c131ae2 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -4174,6 +4174,7 @@ namespace ranges { for (;; ++_Current) { if (++_First == _Last) { + ++_Current; return {_STD move(_Current), _STD move(_First)}; } diff --git a/stl/inc/future b/stl/inc/future index 2600a3cac8f..d38072b6692 100644 --- a/stl/inc/future +++ b/stl/inc/future @@ -874,6 +874,9 @@ class future : public _State_manager<_Ty> { using _Mybase = _State_manager<_Ty>; public: + static_assert(!is_array_v<_Ty> && is_object_v<_Ty> && is_destructible_v<_Ty>, + "T in future must meet the Cpp17Destructible requirements (N4878 [futures.unique.future]/4)."); + future() noexcept {} future(future&& _Other) noexcept : _Mybase(_STD move(_Other), true) {} @@ -972,6 +975,9 @@ class shared_future : public _State_manager<_Ty> { using _Mybase = _State_manager<_Ty>; public: + static_assert(!is_array_v<_Ty> && is_object_v<_Ty> && is_destructible_v<_Ty>, + "T in shared_future must meet the Cpp17Destructible requirements (N4878 [futures.shared.future]/4)."); + shared_future() noexcept {} shared_future(const shared_future& _Other) noexcept : _Mybase(_Other) {} @@ -1139,6 +1145,9 @@ private: template class promise { // class that defines an asynchronous provider that holds a value public: + static_assert(!is_array_v<_Ty> && is_object_v<_Ty> && is_destructible_v<_Ty>, + "T in promise must meet the Cpp17Destructible requirements (N4878 [futures.promise]/1)."); + promise() : _MyPromise(new _Associated_state<_Ty>) {} template diff --git a/stl/inc/header-units.json b/stl/inc/header-units.json new file mode 100644 index 00000000000..cb1ef9efeb5 --- /dev/null +++ b/stl/inc/header-units.json @@ -0,0 +1,149 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +{ + "Version": "1.0", + "BuildAsHeaderUnits": [ + // "__msvc_all_public_headers.hpp", // for testing, not production + "__msvc_system_error_abi.hpp", + "algorithm", + "any", + "array", + "atomic", + "barrier", + "bit", + "bitset", + // "cassert", // design is permanently incompatible with header units + // "ccomplex", // removed in C++20 + "cctype", + "cerrno", + "cfenv", + "cfloat", + "charconv", + "chrono", + "cinttypes", + // "ciso646", // removed in C++20 + "climits", + "clocale", + "cmath", + "codecvt", + "compare", + "complex", + "concepts", + "condition_variable", + "coroutine", + "csetjmp", + "csignal", + // "cstdalign", // removed in C++20 + "cstdarg", + // "cstdbool", // removed in C++20 + "cstddef", + "cstdint", + "cstdio", + "cstdlib", + "cstring", + // "ctgmath", // removed in C++20 + "ctime", + "cuchar", + "cwchar", + "cwctype", + "deque", + "exception", + "execution", + "filesystem", + "forward_list", + "fstream", + "functional", + "future", + // "hash_map", // non-Standard, will be removed soon + // "hash_set", // non-Standard, will be removed soon + "initializer_list", + "iomanip", + "ios", + "iosfwd", + "iostream", + "iso646.h", + "istream", + "iterator", + "latch", + "limits", + "list", + "locale", + "map", + "memory", + "memory_resource", + "mutex", + "new", + "numbers", + "numeric", + "optional", + "ostream", + "queue", + "random", + "ranges", + "ratio", + "regex", + "scoped_allocator", + "semaphore", + "set", + "shared_mutex", + "span", + "sstream", + "stack", + "stdexcept", + "stop_token", + "streambuf", + "string", + "string_view", + "strstream", + "system_error", + "thread", + "tuple", + "type_traits", + "typeindex", + "typeinfo", + "unordered_map", + "unordered_set", + "use_ansi.h", + "utility", + "valarray", + "variant", + "vector", + "version", + "xatomic.h", + "xatomic_wait.h", + "xbit_ops.h", + "xcall_once.h", + "xcharconv.h", + "xcharconv_ryu.h", + "xcharconv_ryu_tables.h", + "xerrc.h", + "xfacet", + "xfilesystem_abi.h", + "xhash", + "xiosbase", + // "xkeycheck.h", // internal header, provides no machinery, scans for macroized keywords only + "xlocale", + "xlocbuf", + "xlocinfo", + "xlocinfo.h", + "xlocmes", + "xlocmon", + "xlocnum", + "xloctime", + "xmemory", + "xnode_handle.h", + "xpolymorphic_allocator.h", + "xsmf_control.h", + "xstddef", + "xstring", + "xthreads.h", + "xtimec.h", + "xtr1common", + "xtree", + "xutility", + "ymath.h", + "yvals.h", + "yvals_core.h" + ] +} diff --git a/stl/inc/limits b/stl/inc/limits index f93a63ac90f..bfe6ee02843 100644 --- a/stl/inc/limits +++ b/stl/inc/limits @@ -1058,9 +1058,14 @@ _NODISCARD int _Checked_x86_x64_countr_zero(const _Ty _Val) noexcept { constexpr _Ty _Max = (numeric_limits<_Ty>::max)(); #ifndef __AVX2__ - const bool _Definitely_have_tzcnt = __isa_available >= __ISA_AVAILABLE_AVX2; - if (!_Definitely_have_tzcnt && _Val == 0) { - return _Digits; + // Because the widening done below will always give a non-0 value, checking for tzcnt + // is not required for 8-bit and 16-bit since the only difference in behavior between + // bsf and tzcnt is when the value is 0. + if constexpr (_Digits > 16) { + const bool _Definitely_have_tzcnt = __isa_available >= __ISA_AVAILABLE_AVX2; + if (!_Definitely_have_tzcnt && _Val == 0) { + return _Digits; + } } #endif // __AVX2__ diff --git a/stl/inc/memory b/stl/inc/memory index 91e7860b9c3..beac884f890 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -2082,10 +2082,10 @@ struct _Alignas_storage_unit { alignas(_Align) char _Space[_Align]; }; -enum class _Check_overflow : bool { _No, _Yes }; +enum class _Check_overflow : bool { _Nope, _Yes }; template -_NODISCARD size_t _Calculate_bytes_for_flexible_array(const size_t _Count) noexcept(_Check == _Check_overflow::_No) { +_NODISCARD size_t _Calculate_bytes_for_flexible_array(const size_t _Count) noexcept(_Check == _Check_overflow::_Nope) { constexpr size_t _Align = alignof(_Refc); size_t _Bytes = sizeof(_Refc); // contains storage for one element @@ -2691,7 +2691,7 @@ private: _Rebind_alloc_t<_Alloc, _Storage> _Al(this->_Get_val()); const size_t _Bytes = - _Calculate_bytes_for_flexible_array<_Ref_count_unbounded_array_alloc, _Check_overflow::_No>(_Size); + _Calculate_bytes_for_flexible_array<_Ref_count_unbounded_array_alloc, _Check_overflow::_Nope>(_Size); const size_t _Storage_units = _Bytes / sizeof(_Storage); this->~_Ref_count_unbounded_array_alloc(); diff --git a/stl/inc/regex b/stl/inc/regex index 32ef9439b46..0cd17c2b3a0 100644 --- a/stl/inc/regex +++ b/stl/inc/regex @@ -936,7 +936,7 @@ basic_ostream<_Elem, _Traits>& operator<<(basic_ostream<_Elem, _Traits>& _Ostr, } // FORWARD DECLARATIONS -template >> +template >> class match_results; template diff --git a/stl/inc/sstream b/stl/inc/sstream index e5c6235cf2f..90ce15f8ea0 100644 --- a/stl/inc/sstream +++ b/stl/inc/sstream @@ -29,6 +29,10 @@ public: using _Mystr = basic_string<_Elem, _Traits, _Alloc>; using _Mysize_type = typename _Mystr::size_type; +#if _HAS_CXX20 + using _Mystr_view = basic_string_view<_Elem, _Traits>; +#endif // _HAS_CXX20 + basic_stringbuf() : _Seekhigh(nullptr), _Mystate(_Getstate(ios_base::in | ios_base::out)), _Al() {} explicit basic_stringbuf(ios_base::openmode _Mode) : _Seekhigh(nullptr), _Mystate(_Getstate(_Mode)), _Al() {} @@ -38,6 +42,37 @@ public: _Init(_Str.c_str(), _Str.size(), _Getstate(_Mode)); } +#if _HAS_CXX20 + explicit basic_stringbuf(const _Alloc& _Al_) : basic_stringbuf(ios_base::in | ios_base::out, _Al_) {} + + basic_stringbuf(ios_base::openmode _Mode, const _Alloc& _Al_) + : _Seekhigh(nullptr), _Mystate(_Getstate(_Mode)), _Al(_Al_) {} + + explicit basic_stringbuf(_Mystr&& _Str, ios_base::openmode _Mode = ios_base::in | ios_base::out) + : _Al(_STD move(_Str._Getal())) { + _Init_string_inplace(_STD move(_Str), _Getstate(_Mode)); + } + + template + basic_stringbuf(const basic_string<_Elem, _Traits, _Alloc2>& _Str, ios_base::openmode _Mode, const _Alloc& _Al_) + : _Al(_Al_) { + _Init(_Str.c_str(), _Str.size(), _Getstate(_Mode)); + } + + template + basic_stringbuf(const basic_string<_Elem, _Traits, _Alloc2>& _Str, const _Alloc& _Al_) + : basic_stringbuf(_Str, ios_base::in | ios_base::out, _Al_) {} + + template , int> = 0> + explicit basic_stringbuf( + const basic_string<_Elem, _Traits, _Alloc2>& _Str, ios_base::openmode _Mode = ios_base::in | ios_base::out) + : basic_stringbuf(_Str, _Mode, _Alloc{}) {} + + basic_stringbuf(basic_stringbuf&& _Right, const _Alloc& _Al_) : _Al(_Al_) { + _Assign_rv(_STD move(_Right)); + } +#endif // _HAS_CXX20 + basic_stringbuf(basic_stringbuf&& _Right) { _Assign_rv(_STD move(_Right)); } @@ -54,12 +89,18 @@ public: } } - void swap(basic_stringbuf& _Right) { + void swap(basic_stringbuf& _Right) noexcept /* strengthened */ { if (this != _STD addressof(_Right)) { + if constexpr (!allocator_traits<_Alloc>::propagate_on_container_swap::value) { + _STL_ASSERT( + _Al == _Right._Al, "The allocators of basic_stringbuf should propagate or be equal on swap."); + } + _Mysb::swap(_Right); _STD swap(_Seekhigh, _Right._Seekhigh); _STD swap(_Mystate, _Right._Mystate); - _Swap_adl(_Al, _Right._Al); + // The same as basic_string::swap + _Pocs(_Al, _Right._Al); } } @@ -71,27 +112,53 @@ public: } enum { // constants for bits in stream state - _Allocated = 1, // set if character array storage has been allocated, eback() points to allocated storage - _Constant = 2, // set if character array nonmutable - _Noread = 4, // set if character array cannot be read - _Append = 8, // set if all writes are appends - _Atend = 16 // set if initial writes are appends + _Allocated = 1, // set if character array storage has been allocated, eback() points to allocated storage + _Constant = 2, // set if character array nonmutable + _Noread = 4, // set if character array cannot be read + _Append = 8, // set if all writes are appends + _Atend = 16, // set if initial writes are appends + _From_rvalue = 32 // set if character array is released from an rvalue of basic_string }; using int_type = typename _Traits::int_type; using pos_type = typename _Traits::pos_type; using off_type = typename _Traits::off_type; - _NODISCARD _Mystr str() const { - _Mystr _Result(_Al); - if (!(_Mystate & _Constant) && _Mysb::pptr()) { // writable, make string from write buffer + struct _Buffer_view { + _Elem* _Ptr; + _Mysize_type _Size; + _Mysize_type _Res; + }; + + _NODISCARD _Buffer_view _Get_buffer_view() const noexcept { + _Buffer_view _Result{}; + if ((!(_Mystate & _Constant) || (_Mystate & _From_rvalue)) && _Mysb::pptr()) { + // writable, make string view from write buffer const auto _Base = _Mysb::pbase(); - _Result.assign(_Base, static_cast<_Mysize_type>((_STD max)(_Mysb::pptr(), _Seekhigh) - _Base)); - } else if (!(_Mystate & _Noread) && _Mysb::gptr()) { // readable, make string from read buffer + _Result._Ptr = _Base; + _Result._Size = static_cast<_Mysize_type>((_STD max)(_Mysb::pptr(), _Seekhigh) - _Base); + _Result._Res = static_cast<_Mysize_type>(_Mysb::epptr() - _Base); + } else if (!(_Mystate & _Noread) && _Mysb::gptr()) { + // readable, make string view from read buffer const auto _Base = _Mysb::eback(); - _Result.assign(_Base, static_cast<_Mysize_type>(_Mysb::egptr() - _Base)); + _Result._Ptr = _Base; + _Result._Size = static_cast<_Mysize_type>(_Mysb::egptr() - _Base); + _Result._Res = _Result._Size; } + return _Result; + } +#if _HAS_CXX20 + _NODISCARD _Mystr str() const& +#else + _NODISCARD _Mystr str() const +#endif // _HAS_CXX20 + { + _Mystr _Result(_Al); + const auto _View = _Get_buffer_view(); + if (_View._Ptr) { + _Result.assign(_View._Ptr, _View._Size); + } return _Result; } @@ -100,6 +167,72 @@ public: _Init(_Newstr.c_str(), _Newstr.size(), _Mystate); } +#if _HAS_CXX20 + template <_Allocator _Alloc2> + _NODISCARD basic_string<_Elem, _Traits, _Alloc2> str(const _Alloc2& _Al) const { + return basic_string<_Elem, _Traits, _Alloc2>{view(), _Al}; + } + + // The buffer cannot be moved to a string directly, because + // the buffer may already be full, and the terminating char is not '\0'. + // In that case, copy the string as usual. + _NODISCARD _Mystr str() && { + _Mystr _Result{_String_constructor_rvalue_allocator_tag{}, _STD move(_Al)}; + const auto _View = _Get_buffer_view(); + // _Size cannot be larger than _Res, but it could be equal, + // because basic_stringbuf doesn't allocate for the terminating '\0'. + if (_View._Size == _View._Res) { + // the buffer is full + _Result.assign(_View._Ptr, _View._Size); + } else { + _Traits::assign(_View._Ptr[_View._Size], _Elem()); + if (_Result._Move_assign_from_buffer(_View._Ptr, _View._Size, _View._Res)) { + _Mystate &= ~_Allocated; + } + } + _Tidy(); + return _Result; + } + + _NODISCARD _Mystr_view view() const noexcept { + const auto _View = _Get_buffer_view(); + return _Mystr_view{_View._Ptr, _View._Size}; + } + + template , int> = 0> + void str(const basic_string<_Elem, _Traits, _Alloc2>& _Newstr) { + _Tidy(); + _Init(_Newstr.c_str(), _Newstr.size(), _Mystate); + } + + void _Str(_Mystr&& _Newstr, _Equal_allocators) { + _Tidy(); + _Pocma(_Al, _Newstr._Getal()); + _Init_string_inplace(_STD move(_Newstr), _Mystate); + } + + void _Str(_Mystr&& _Newstr, _Propagate_allocators) { + _Str(_STD move(_Newstr), _Equal_allocators{}); + } + + void _Str(_Mystr&& _Newstr, _No_propagate_allocators) { + if (_Al == _Newstr._Getal()) { + _Str(_STD move(_Newstr), _Equal_allocators{}); + } else { + _Tidy(); + _Init(_Newstr.c_str(), _Newstr.size(), _Mystate); + } + } + + void str(_Mystr&& _Newstr) { + _Str(_STD move(_Newstr), _Choose_pocma<_Alloc>{}); + } + + _NODISCARD allocator_type get_allocator() const noexcept { + return _Al; + } +#endif // _HAS_CXX20 + protected: virtual int_type overflow(int_type _Meta = _Traits::eof()) { // put an element to stream if (_Mystate & _Constant) { @@ -205,7 +338,7 @@ protected: off_type _Off, ios_base::seekdir _Way, ios_base::openmode _Mode = ios_base::in | ios_base::out) { // change position by _Off, according to _Way, _Mode const auto _Gptr_old = _Mysb::gptr(); - const auto _Pptr_old = _Mysb::pptr(); + const auto _Pptr_old = (_Mystate & _Constant) ? nullptr : _Mysb::pptr(); if (_Pptr_old && _Seekhigh < _Pptr_old) { // update high-water pointer _Seekhigh = _Pptr_old; } @@ -267,7 +400,7 @@ protected: // change position to _Pos, according to _Mode const auto _Off = static_cast(_Pos); const auto _Gptr_old = _Mysb::gptr(); - const auto _Pptr_old = _Mysb::pptr(); + const auto _Pptr_old = (_Mystate & _Constant) ? nullptr : _Mysb::pptr(); if (_Pptr_old && _Seekhigh < _Pptr_old) { // update high-water pointer _Seekhigh = _Pptr_old; } @@ -297,6 +430,8 @@ protected: protected: void _Init(const _Elem* _Ptr, _Mysize_type _Count, int _State) { // initialize buffer to [_Ptr, _Ptr + _Count), set state + _State &= ~_From_rvalue; + if (_Count > INT_MAX) { // TRANSITION, VSO-485517 _Xbad_alloc(); } @@ -328,6 +463,34 @@ protected: _Mystate = _State; } +#if _HAS_CXX20 + void _Init_string_inplace(_Mystr&& _Str, int _State) { + _State |= _From_rvalue; + + auto [_Ptr, _Size, _Res] = _Str._Release_to_buffer(_Al); + _Elem* const _Pnew = _Unfancy(_Ptr); + if (_Res != 0 && (_State & (_Noread | _Constant)) != (_Noread | _Constant)) { + // finite buffer that can be read or written, set it up + _Seekhigh = _Pnew + _Size; + auto _End_buffer = _Pnew + _Res; + + _Mysb::setp(_Pnew, (_State & (_Atend | _Append)) ? _Seekhigh : _Pnew, _End_buffer); + + if (_State & _Noread) { // maintain "_Allocated == eback() points to buffer base" invariant + _Mysb::setg(_Pnew, nullptr, _Pnew); + } else { + _Mysb::setg(_Pnew, _Pnew, _Seekhigh); + } + + _State |= _Allocated; + } else { + _Seekhigh = nullptr; + } + + _Mystate = _State; + } +#endif // _HAS_CXX20 + void _Tidy() noexcept { // discard any allocated buffer and clear pointers if (_Mystate & _Allocated) { _Al.deallocate(_Ptr_traits::pointer_to(*_Mysb::eback()), @@ -369,13 +532,15 @@ private: return _State; } + // TRANSITION, ABI, see GH-938 _Elem* _Seekhigh; // the high-water pointer in character array int _Mystate; // the stream state allocator_type _Al; // the allocator object }; template -void swap(basic_stringbuf<_Elem, _Traits, _Alloc>& _Left, basic_stringbuf<_Elem, _Traits, _Alloc>& _Right) { +void swap(basic_stringbuf<_Elem, _Traits, _Alloc>& _Left, basic_stringbuf<_Elem, _Traits, _Alloc>& _Right) noexcept +/* strengthened */ { _Left.swap(_Right); } @@ -388,6 +553,10 @@ public: using _Mysb = basic_stringbuf<_Elem, _Traits, _Alloc>; using _Mystr = basic_string<_Elem, _Traits, _Alloc>; +#if _HAS_CXX20 + using _Mystr_view = basic_string_view<_Elem, _Traits>; +#endif // _HAS_CXX20 + basic_istringstream() : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(ios_base::in) {} explicit basic_istringstream(ios_base::openmode _Mode) @@ -396,6 +565,27 @@ public: explicit basic_istringstream(const _Mystr& _Str, ios_base::openmode _Mode = ios_base::in) : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, _Mode | ios_base::in) {} +#if _HAS_CXX20 + basic_istringstream(ios_base::openmode _Mode, const _Alloc& _Al) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Mode | ios_base::in, _Al) {} + + explicit basic_istringstream(_Mystr&& _Str, ios_base::openmode _Mode = ios_base::in) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_STD move(_Str), _Mode | ios_base::in) {} + + template + basic_istringstream(const basic_string<_Elem, _Traits, _Alloc2>& _Str, const _Alloc& _Al) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, ios_base::in, _Al) {} + + template + basic_istringstream(const basic_string<_Elem, _Traits, _Alloc2>& _Str, ios_base::openmode _Mode, const _Alloc& _Al) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, _Mode | ios_base::in, _Al) {} + + template , int> = 0> + explicit basic_istringstream( + const basic_string<_Elem, _Traits, _Alloc2>& _Str, ios_base::openmode _Mode = ios_base::in) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, _Mode | ios_base::in) {} +#endif // _HAS_CXX20 + basic_istringstream(basic_istringstream&& _Right) : _Mybase(_STD addressof(_Stringbuffer)) { _Assign_rv(_STD move(_Right)); } @@ -428,7 +618,12 @@ public: return const_cast<_Mysb*>(_STD addressof(_Stringbuffer)); } - _NODISCARD _Mystr str() const { +#if _HAS_CXX20 + _NODISCARD _Mystr str() const& +#else + _NODISCARD _Mystr str() const +#endif // _HAS_CXX20 + { return _Stringbuffer.str(); } @@ -436,6 +631,30 @@ public: _Stringbuffer.str(_Newstr); } +#if _HAS_CXX20 + template <_Allocator _Alloc2> + _NODISCARD basic_string<_Elem, _Traits, _Alloc2> str(const _Alloc2& _Al) const { + return _Stringbuffer.str(_Al); + } + + _NODISCARD _Mystr str() && { + return _STD move(_Stringbuffer).str(); + } + + _NODISCARD _Mystr_view view() const noexcept { + return _Stringbuffer.view(); + } + + template , int> = 0> + void str(const basic_string<_Elem, _Traits, _Alloc2>& _Newstr) { + _Stringbuffer.str(_Newstr); + } + + void str(_Mystr&& _Newstr) { + _Stringbuffer.str(_STD move(_Newstr)); + } +#endif // _HAS_CXX20 + private: _Mysb _Stringbuffer; }; @@ -454,6 +673,10 @@ public: using _Mysb = basic_stringbuf<_Elem, _Traits, _Alloc>; using _Mystr = basic_string<_Elem, _Traits, _Alloc>; +#if _HAS_CXX20 + using _Mystr_view = basic_string_view<_Elem, _Traits>; +#endif // _HAS_CXX20 + basic_ostringstream() : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(ios_base::out) {} explicit basic_ostringstream(ios_base::openmode _Mode) @@ -462,6 +685,27 @@ public: explicit basic_ostringstream(const _Mystr& _Str, ios_base::openmode _Mode = ios_base::out) : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, _Mode | ios_base::out) {} +#if _HAS_CXX20 + basic_ostringstream(ios_base::openmode _Mode, const _Alloc& _Al) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Mode | ios_base::out, _Al) {} + + explicit basic_ostringstream(_Mystr&& _Str, ios_base::openmode _Mode = ios_base::out) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_STD move(_Str), _Mode | ios_base::out) {} + + template + basic_ostringstream(const basic_string<_Elem, _Traits, _Alloc2>& _Str, const _Alloc& _Al) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, ios_base::out, _Al) {} + + template + basic_ostringstream(const basic_string<_Elem, _Traits, _Alloc2>& _Str, ios_base::openmode _Mode, const _Alloc& _Al) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, _Mode | ios_base::out, _Al) {} + + template , int> = 0> + explicit basic_ostringstream( + const basic_string<_Elem, _Traits, _Alloc2>& _Str, ios_base::openmode _Mode = ios_base::out) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, _Mode | ios_base::out) {} +#endif // _HAS_CXX20 + basic_ostringstream(basic_ostringstream&& _Right) : _Mybase(_STD addressof(_Stringbuffer)) { _Assign_rv(_STD move(_Right)); } @@ -494,7 +738,12 @@ public: return const_cast<_Mysb*>(_STD addressof(_Stringbuffer)); } - _NODISCARD _Mystr str() const { +#if _HAS_CXX20 + _NODISCARD _Mystr str() const& +#else + _NODISCARD _Mystr str() const +#endif // _HAS_CXX20 + { return _Stringbuffer.str(); } @@ -502,6 +751,30 @@ public: _Stringbuffer.str(_Newstr); } +#if _HAS_CXX20 + template <_Allocator _Alloc2> + _NODISCARD basic_string<_Elem, _Traits, _Alloc2> str(const _Alloc2& _Al) const { + return _Stringbuffer.str(_Al); + } + + _NODISCARD _Mystr str() && { + return _STD move(_Stringbuffer).str(); + } + + _NODISCARD _Mystr_view view() const noexcept { + return _Stringbuffer.view(); + } + + template , int> = 0> + void str(const basic_string<_Elem, _Traits, _Alloc2>& _Newstr) { + _Stringbuffer.str(_Newstr); + } + + void str(_Mystr&& _Newstr) { + _Stringbuffer.str(_STD move(_Newstr)); + } +#endif // _HAS_CXX20 + private: _Mysb _Stringbuffer; }; @@ -526,6 +799,10 @@ public: using _Mysb = basic_stringbuf<_Elem, _Traits, _Alloc>; using _Mystr = basic_string<_Elem, _Traits, _Alloc>; +#if _HAS_CXX20 + using _Mystr_view = basic_string_view<_Elem, _Traits>; +#endif // _HAS_CXX20 + basic_stringstream() : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(ios_base::in | ios_base::out) {} explicit basic_stringstream(ios_base::openmode _Mode) @@ -534,6 +811,27 @@ public: explicit basic_stringstream(const _Mystr& _Str, ios_base::openmode _Mode = ios_base::in | ios_base::out) : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, _Mode) {} +#if _HAS_CXX20 + basic_stringstream(ios_base::openmode _Mode, const _Alloc& _Al) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Mode, _Al) {} + + explicit basic_stringstream(_Mystr&& _Str, ios_base::openmode _Mode = ios_base::in | ios_base::out) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_STD move(_Str), _Mode) {} + + template + basic_stringstream(const basic_string<_Elem, _Traits, _Alloc2>& _Str, const _Alloc& _Al) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, ios_base::in | ios_base::out, _Al) {} + + template + basic_stringstream(const basic_string<_Elem, _Traits, _Alloc2>& _Str, ios_base::openmode _Mode, const _Alloc& _Al) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, _Mode, _Al) {} + + template , int> = 0> + explicit basic_stringstream( + const basic_string<_Elem, _Traits, _Alloc2>& _Str, ios_base::openmode _Mode = ios_base::in | ios_base::out) + : _Mybase(_STD addressof(_Stringbuffer)), _Stringbuffer(_Str, _Mode) {} +#endif // _HAS_CXX20 + basic_stringstream(basic_stringstream&& _Right) : _Mybase(_STD addressof(_Stringbuffer)) { _Assign_rv(_STD move(_Right)); } @@ -566,7 +864,12 @@ public: return const_cast<_Mysb*>(_STD addressof(_Stringbuffer)); } - _NODISCARD _Mystr str() const { +#if _HAS_CXX20 + _NODISCARD _Mystr str() const& +#else + _NODISCARD _Mystr str() const +#endif // _HAS_CXX20 + { return _Stringbuffer.str(); } @@ -574,6 +877,30 @@ public: _Stringbuffer.str(_Newstr); } +#if _HAS_CXX20 + template <_Allocator _Alloc2> + _NODISCARD basic_string<_Elem, _Traits, _Alloc2> str(const _Alloc2& _Al) const { + return _Stringbuffer.str(_Al); + } + + _NODISCARD _Mystr str() && { + return _STD move(_Stringbuffer).str(); + } + + _NODISCARD _Mystr_view view() const noexcept { + return _Stringbuffer.view(); + } + + template , int> = 0> + void str(const basic_string<_Elem, _Traits, _Alloc2>& _Newstr) { + _Stringbuffer.str(_Newstr); + } + + void str(_Mystr&& _Newstr) { + _Stringbuffer.str(_STD move(_Newstr)); + } +#endif // _HAS_CXX20 + private: _Mysb _Stringbuffer; }; diff --git a/stl/inc/xlocale b/stl/inc/xlocale index 107e80d0a5a..fcdbf63bb17 100644 --- a/stl/inc/xlocale +++ b/stl/inc/xlocale @@ -3242,8 +3242,11 @@ protected: }; // FUNCTION TEMPLATE _Getloctxt -template -int __CRTDECL _Getloctxt(_InIt& _First, _InIt& _Last, size_t _Numfields, const _Elem* _Ptr) { +enum class _Case_sensitive : bool { _Nope, _Yes }; + +template +int __CRTDECL _Getloctxt( + _InIt& _First, _InIt& _Last, size_t _Numfields, const _Elem* _Ptr, const _Case_sensitive _Matching) { // find field at _Ptr that matches longest in [_First, _Last) for (size_t _Off = 0; _Ptr[_Off] != _Elem{}; ++_Off) { if (_Ptr[_Off] == _Ptr[0]) { @@ -3271,7 +3274,10 @@ int __CRTDECL _Getloctxt(_InIt& _First, _InIt& _Last, size_t _Numfields, const _ || _Ptr[_Off] == _Elem{}) { // matched all of field, save as possible answer _Str[_Field] = static_cast(_Column < 127 ? _Column : 127); // save skip count if small enough _Ans = static_cast(_Field); // save answer - } else if (_First == _Last || _CType.tolower(_Ptr[_Off]) != _CType.tolower(static_cast<_Elem>(*_First))) { + } else if (_First == _Last + || (_Matching == _Case_sensitive::_Yes + ? _Ptr[_Off] != *_First + : _CType.tolower(_Ptr[_Off]) != _CType.tolower(static_cast<_Elem>(*_First)))) { _Str[_Field] = static_cast(_Column < 127 ? _Column : 127); // no match, just save skip count } else { _Prefix = true; // still a valid prefix diff --git a/stl/inc/xlocnum b/stl/inc/xlocnum index 89d33a7d02f..2643b4cbe56 100644 --- a/stl/inc/xlocnum +++ b/stl/inc/xlocnum @@ -369,7 +369,7 @@ protected: _Str += _Punct_fac.falsename(); _Str.push_back(_Elem{}); _Str += _Punct_fac.truename(); // construct "\0false\0true" - switch (_Getloctxt(_First, _Last, 2, _Str.c_str())) { + switch (_Getloctxt(_First, _Last, 2, _Str.c_str(), _Case_sensitive::_Yes)) { case 0: _Val = false; break; diff --git a/stl/inc/xloctime b/stl/inc/xloctime index 55cb9f0df2d..d812c1b5743 100644 --- a/stl/inc/xloctime +++ b/stl/inc/xloctime @@ -326,7 +326,7 @@ protected: virtual _InIt __CLR_OR_THIS_CALL do_get_weekday(_InIt _First, _InIt _Last, ios_base&, ios_base::iostate& _State, tm* _Pt) const { // get weekday from [_First, _Last) into _Pt - int _Num = _Getloctxt(_First, _Last, 0, _Days); + int _Num = _Getloctxt(_First, _Last, 0, _Days, _Case_sensitive::_Nope); if (_Num < 0) { _State |= ios_base::failbit; } else { @@ -338,7 +338,7 @@ protected: virtual _InIt __CLR_OR_THIS_CALL do_get_monthname(_InIt _First, _InIt _Last, ios_base&, ios_base::iostate& _State, tm* _Pt) const { // get month from [_First, _Last) into _Pt - int _Num = _Getloctxt(_First, _Last, 0, _Months); + int _Num = _Getloctxt(_First, _Last, 0, _Months, _Case_sensitive::_Nope); if (_Num < 0) { _State |= ios_base::failbit; @@ -444,7 +444,7 @@ protected: break; case 'p': - _Ans = _Getloctxt(_First, _Last, 0, ":AM:am:PM:pm"); + _Ans = _Getloctxt(_First, _Last, 0, ":AM:am:PM:pm", _Case_sensitive::_Nope); if (_Ans < 0) { _State |= ios_base::failbit; } else if (1 < _Ans) { diff --git a/stl/inc/xstring b/stl/inc/xstring index 00a4d2354b1..80355987c2c 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -2272,6 +2272,11 @@ struct _String_constructor_concat_tag { explicit _String_constructor_concat_tag() = default; }; +struct _String_constructor_rvalue_allocator_tag { + // tag to select constructors used by basic_stringbuf's rvalue str() + explicit _String_constructor_rvalue_allocator_tag() = default; +}; + [[noreturn]] inline void _Xlen_string() { _Xlength_error("string too long"); } @@ -2280,6 +2285,7 @@ template , class _Alloc = alloca class basic_string { // null-terminated transparent array of elements private: friend _Tidy_deallocate_guard; + friend basic_stringbuf<_Elem, _Traits, _Alloc>; using _Alty = _Rebind_alloc_t<_Alloc, _Elem>; using _Alty_traits = allocator_traits<_Alty>; @@ -2633,6 +2639,14 @@ public: } #endif // _HAS_CXX17 +#if _HAS_CXX20 + basic_string(_String_constructor_rvalue_allocator_tag, _Alloc&& _Al) + : _Mypair(_One_then_variadic_args_t{}, _STD move(_Al)) { + _Mypair._Myval2._Alloc_proxy(_GET_PROXY_ALLOCATOR(_Alty, _Getal())); + _Tidy_init(); + } +#endif // _HAS_CXX20 + private: void _Move_assign(basic_string& _Right, _Equal_allocators) noexcept { _Tidy_deallocate(); @@ -2662,6 +2676,51 @@ 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() + _Tidy_deallocate(); + pointer _Fancy_right = _Refancy(_Right); + auto& _My_data = _Mypair._Myval2; + _My_data._Mysize = _Size; + _My_data._Myres = _Res - 1; + if (_My_data._Large_string_engaged()) { + _Construct_in_place(_My_data._Bx._Ptr, _Fancy_right); + return true; + } else { + _Traits::copy(_My_data._Bx._Buf, _Right, _Res); + _My_data._Myres = _BUF_SIZE - 1; + return false; + } + } + + // No instance of this type can exist where an exception may be thrown. + struct _Released_buffer { + pointer _Ptr; + size_type _Size; + size_type _Res; + }; + + _NODISCARD _Released_buffer _Release_to_buffer(_Alloc& _Al) { + // Release to a buffer, or allocate a new one if in small string mode + _Released_buffer _Result; + auto& _My_data = _Mypair._Myval2; + _Result._Size = _My_data._Mysize; + if (_My_data._Large_string_engaged()) { + _Result._Ptr = _My_data._Bx._Ptr; + _Result._Res = _My_data._Myres + 1; + } else { + // use _BUF_SIZE + 1 to avoid SSO, if the buffer is assigned back + _Result._Ptr = _Al.allocate(_BUF_SIZE + 1); + _Traits::copy(_Unfancy(_Result._Ptr), _My_data._Bx._Buf, _BUF_SIZE); + _Result._Res = _BUF_SIZE + 1; + } + _My_data._Orphan_all(); + _Tidy_init(); + return _Result; + } +#endif // _HAS_CXX20 + 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>{}); diff --git a/stl/inc/xutility b/stl/inc/xutility index 653646d5425..9f1c687ad01 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1441,6 +1441,11 @@ using _Enable_if_execution_policy_t = typename remove_reference_t<_ExPo>::_Stand #endif // _HAS_CXX17 +#if _HAS_CXX20 +template +concept _Allocator = _Is_allocator<_Ty>::value; +#endif // _HAS_CXX20 + // FUNCTION TEMPLATE _Idl_distance template _NODISCARD constexpr auto _Idl_distance(const _Iter& _First, const _Iter& _Last) { @@ -4361,11 +4366,12 @@ struct _Is_character_or_byte_or_bool : true_type {}; template <> struct _Is_character_or_byte_or_bool : true_type {}; -// _Fill_memset_is_safe determines if _FwdIt and _Ty are eligible for memset optimization in fill +// _Fill_memset_is_safe determines if _FwdIt and _Ty are eligible for memset optimization in fill. +// Need to explicitly test for volatile because _Unwrap_enum_t discards qualifiers. template > _INLINE_VAR constexpr bool _Fill_memset_is_safe = conjunction_v, _Is_character_or_byte_or_bool<_Unwrap_enum_t>>>, - is_assignable<_Iter_ref_t<_FwdIt>, const _Ty&>>; + negation>>>, is_assignable<_Iter_ref_t<_FwdIt>, const _Ty&>>; template _INLINE_VAR constexpr bool _Fill_memset_is_safe<_FwdIt, _Ty, false> = false; diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 28011748cb5..e37d8daf036 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -140,6 +140,7 @@ // P0339R6 polymorphic_allocator<> // P0356R5 bind_front() // P0357R3 Supporting Incomplete Types In reference_wrapper +// P0408R7 Efficient Access To basic_stringbuf's Buffer // P0415R1 constexpr For (Again) // P0439R0 enum class memory_order // P0457R2 starts_with()/ends_with() For basic_string/basic_string_view diff --git a/tests/std/test.lst b/tests/std/test.lst index 3d71f043206..014d2c798d4 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -172,6 +172,7 @@ tests\GH_001103_countl_zero_correctness tests\GH_001105_custom_streambuf_throws tests\GH_001123_random_cast_out_of_range tests\GH_001411_core_headers +tests\GH_001541_case_sensitive_boolalpha tests\LWG2597_complex_branch_cut tests\LWG3018_shared_ptr_function tests\P0019R8_atomic_ref @@ -228,6 +229,7 @@ tests\P0339R6_polymorphic_allocator tests\P0355R7_calendars_and_time_zones_io tests\P0356R5_bind_front tests\P0357R3_supporting_incomplete_types_in_reference_wrapper +tests\P0408R7_efficient_access_to_stringbuf_buffer tests\P0414R2_shared_ptr_for_arrays tests\P0415R1_constexpr_complex tests\P0426R1_constexpr_char_traits diff --git a/tests/std/tests/GH_001541_case_sensitive_boolalpha/env.lst b/tests/std/tests/GH_001541_case_sensitive_boolalpha/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_001541_case_sensitive_boolalpha/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_001541_case_sensitive_boolalpha/test.cpp b/tests/std/tests/GH_001541_case_sensitive_boolalpha/test.cpp new file mode 100644 index 00000000000..42cdbdc11d5 --- /dev/null +++ b/tests/std/tests/GH_001541_case_sensitive_boolalpha/test.cpp @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +using namespace std; + +enum class Parse { Success, Failure }; + +struct TestCase { + const char* str; + ios_base::fmtflags flags; + Parse expected; + bool result; +}; + +// clang-format off +constexpr TestCase test_cases[] = { + {"0", ios_base::fmtflags{}, Parse::Success, false}, + {"1", ios_base::fmtflags{}, Parse::Success, true }, + {"2", ios_base::fmtflags{}, Parse::Failure, true }, // N4868 [facet.num.get.virtuals]/6 + {"WOOF", ios_base::fmtflags{}, Parse::Failure, false}, // N4868 [facet.num.get.virtuals]/3.6 + {"false", ios_base::boolalpha, Parse::Success, false}, + {"true", ios_base::boolalpha, Parse::Success, true }, + {"WOOF", ios_base::boolalpha, Parse::Failure, false}, // N4868 [facet.num.get.virtuals]/7 + {"FALSE", ios_base::boolalpha, Parse::Failure, false}, // GH-1541 + {"TRUE", ios_base::boolalpha, Parse::Failure, false}, // GH-1541 +}; +// clang-format on + +int main() { + for (const auto& test : test_cases) { + bool val = !test.result; + istringstream iss(test.str); + iss.setf(test.flags); + iss >> val; + assert(iss.fail() == (test.expected == Parse::Failure)); + assert(val == test.result); + } +} diff --git a/tests/std/tests/P0220R1_optional/test.cpp b/tests/std/tests/P0220R1_optional/test.cpp index a7bb292e541..ec0b2904315 100644 --- a/tests/std/tests/P0220R1_optional/test.cpp +++ b/tests/std/tests/P0220R1_optional/test.cpp @@ -5495,13 +5495,17 @@ constexpr int test() { optional opt(in_place, 2); Y y(3); +#ifndef __EDG__ // TRANSITION, VSO-1268140 assert(std::move(opt).value_or(y) == 2); assert(*opt == 0); +#endif // ^^^ no workaround ^^^ } { optional opt(in_place, 2); +#ifndef __EDG__ // TRANSITION, VSO-1268140 assert(std::move(opt).value_or(Y(3)) == 2); assert(*opt == 0); +#endif // ^^^ no workaround ^^^ } { optional opt; diff --git a/tests/std/tests/P0408R7_efficient_access_to_stringbuf_buffer/env.lst b/tests/std/tests/P0408R7_efficient_access_to_stringbuf_buffer/env.lst new file mode 100644 index 00000000000..e5b00aee0d4 --- /dev/null +++ b/tests/std/tests/P0408R7_efficient_access_to_stringbuf_buffer/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_winsdk_matrix.lst diff --git a/tests/std/tests/P0408R7_efficient_access_to_stringbuf_buffer/test.cpp b/tests/std/tests/P0408R7_efficient_access_to_stringbuf_buffer/test.cpp new file mode 100644 index 00000000000..84d4474aa25 --- /dev/null +++ b/tests/std/tests/P0408R7_efficient_access_to_stringbuf_buffer/test.cpp @@ -0,0 +1,302 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; + +constexpr char empty_string[] = ""; +constexpr char small_string[] = "a"; +constexpr char large_string[] = + "This is a long long long long long long long long string to avoid small string optimization."; + +template +struct test_rvalue { + void operator()(const string& init_value) { + string buffer{init_value}; + size_t res = buffer.capacity(); + Stream stream{move(buffer)}; + assert(stream.view() == init_value); + assert(stream.str() == init_value); + assert(stream.view() == init_value); + assert(stream.rdbuf()->get_allocator() == init_value.get_allocator()); + // Move out the buffer, the underlying buffer should be empty. + buffer = move(stream).str(); + if (buffer == large_string) { + // stream doesn't actually have space for a null-terminator + assert(buffer.capacity() == res); + } else { + assert(buffer.capacity() == res + 1); + } + assert(buffer == init_value); + assert(stream.view().empty()); + assert(stream.str().empty()); + // Move in the buffer string + stream.str(move(buffer)); + assert(stream.view() == init_value); + assert(stream.str() == init_value); + assert(stream.rdbuf()->get_allocator() == init_value.get_allocator()); + // Move to another stream + Stream stream2 = move(stream); + assert(stream.view().empty()); + assert(stream2.view() == init_value); + } +}; + +template +struct test_allocator { + void operator()(const pmr::string& init_value) { + Stream stream{init_value}; + assert(stream.view() == init_value); + assert(stream.str(init_value.get_allocator()) == init_value); + assert(stream.str() == string_view{init_value}); + assert(move(stream).str() == string_view{init_value}); + assert(stream.view().empty()); + stream.str(init_value); + assert(stream.view() == init_value); + assert(stream.str(init_value.get_allocator()) == init_value); + assert(stream.str() == string_view{init_value}); + } +}; + +template +struct test_pmr_allocator { + pmr::polymorphic_allocator alloc; + + void operator()(const pmr::string& init_value) { + Stream stream{init_value, alloc}; + assert(stream.rdbuf()->get_allocator() != init_value.get_allocator()); + { + pmr::string new_str = stream.str(init_value.get_allocator()); + assert(new_str == init_value); + assert(new_str.get_allocator() == init_value.get_allocator()); + } + { + pmr::string new_str = stream.str(); + assert(new_str == init_value); + assert(new_str.get_allocator() != init_value.get_allocator()); + } + stream.str(init_value); + assert(stream.rdbuf()->get_allocator() != init_value.get_allocator()); + { + pmr::string new_str = stream.str(init_value.get_allocator()); + assert(new_str == init_value); + assert(new_str.get_allocator() == init_value.get_allocator()); + } + { + pmr::string new_str = stream.str(); + assert(new_str == init_value); + assert(new_str.get_allocator() != init_value.get_allocator()); + } + Stream stream2{init_value, init_value.get_allocator()}; + assert(stream2.rdbuf()->get_allocator() == init_value.get_allocator()); + } +}; + +template +void run_test_util() { + Test test{}; + test(empty_string); + test(small_string); + test(large_string); +} + +template