diff --git a/.github/workflows/update-status-chart.yml b/.github/workflows/update-status-chart.yml new file mode 100644 index 00000000000..c231c3529cb --- /dev/null +++ b/.github/workflows/update-status-chart.yml @@ -0,0 +1,31 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +name: Update Status Chart +on: + schedule: + - cron: "0 7 * * *" + workflow_dispatch: +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout gh-pages + uses: actions/checkout@v2 + with: + ref: gh-pages + - name: Setup Node.js + uses: actions/setup-node@v2 + with: + node-version: ">=15.12.0" + - name: Run gather_stats.js + run: | + npm ci + SECRET_GITHUB_PERSONAL_ACCESS_TOKEN="${{ github.token }}" node gather_stats.js + - name: Commit Changes + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add daily_table.js monthly_table.js + git diff-index --quiet HEAD || git commit -m "Automated update." + git push origin gh-pages diff --git a/README.md b/README.md index f81f691f560..1555cc8ce9c 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ issue. The [bug tag][] and [enhancement tag][] are being populated. # Goals -We're implementing the latest C++ Working Draft, currently [N4878][], which will eventually become the next C++ +We're implementing the latest C++ Working Draft, currently [N4885][], which will eventually become the next C++ International Standard. The terms Working Draft (WD) and Working Paper (WP) are interchangeable; we often informally refer to these drafts as "the Standard" while being aware of the difference. (There are other relevant Standards; for example, supporting `/std:c++14` and `/std:c++17` involves understanding how the C++14 and C++17 @@ -172,14 +172,14 @@ acquire this dependency. To build the x86 target: -1. Open an "x86 Native Tools Command Prompt for VS 2019". +1. Open an "x86 Native Tools Command Prompt for VS 2019 Preview". 2. Change directories to the previously cloned `STL` directory. 3. `cmake -G Ninja -S . -B out\build\x86` 4. `ninja -C out\build\x86` To build the x64 target: -1. Open an "x64 Native Tools Command Prompt for VS 2019". +1. Open an "x64 Native Tools Command Prompt for VS 2019 Preview". 2. Change directories to the previously cloned `STL` directory. 3. `cmake -G Ninja -S . -B out\build\x64` 4. `ninja -C out\build\x64` @@ -205,7 +205,7 @@ your .exe would "win" over the versions in System32. The compiler looks for include directories according to the `INCLUDE` environment variable, and the linker looks for import library directories according to the `LIB` environment variable, and the Windows loader will (eventually) look for DLL dependencies according to directories in the `PATH` environment variable. From an -"x64 Native Tools Command Prompt for VS 2019": +"x64 Native Tools Command Prompt for VS 2019 Preview": ``` C:\Users\username\Desktop>set INCLUDE=C:\Dev\STL\out\build\x64\out\inc;%INCLUDE% @@ -362,6 +362,57 @@ The `SKIPPED` result code indicates that a given test was explicitly skipped by * taking a very long time to run * failing or passing for the incorrect reason +### Debugging Individual Tests + +While `stl-lit` is super awesome in finding out that *something* is wrong or not even compiling, it is not really +helpful in debugging *what* is going wrong. However, debugging individual tests is rather simple given some additional +steps. Let's assume we want to debug a new feature with tests located in `tests\std\tests\GH_XXXX_meow`. + +As always, build the STL from your branch and run the tests: +``` +C:\STL\out\build\x64> ninja +C:\STL\out\build\x64> python tests\utils\stl-lit\stl-lit.py -v C:\STL\tests\std\tests\GH_XXXX_meow +``` + +Let's assume one of the tests fails an assert and we want to debug that configuration. `stl-lit` will conveniently print +the build command, which is far too long to provide here in full. The important part is to add the following options to +provide debug symbols: `/Zi /Fdbark.pdb`. + +You can replace `bark` with any descriptive name you like. Add these before the `"-link"` option in the command line +and recompile. Example: +``` +C:\STL\out\build\x64>cl "C:\STL\tests\std\tests\GH_XXXX_meow\test.cpp" [... more arguments ...] +"-FeC:\STL\out\build\x64\tests\std\tests\GH_XXXX_meow\Output\02\GH_XXXX_meow.exe" /Zi /Fdbark.pdb "-link" +[... more arguments ...] +``` + +You can now start debugging the test via: +``` +devenv "C:\STL\out\build\x64\tests\std\tests\GH_XXXX_meow\Output\02\GH_XXXX_meow.exe" + "C:\STL\tests\std\tests\GH_XXXX_meow\test.cpp" +``` + +However, this might not work right away, as Visual Studio may complain about a missing `msvcp140_oss.dll`. The reason +is that the STL builds those and other DLLs itself and we should under no circumstances overwrite the installed ones. +If you are testing one of the configurations with dynamic linkage (`/MD` or `/MDd`) the easiest solution is to add the +build folder to your path: +``` +set PATH=C:\STL\out\build\x64\out\bin\amd64;%PATH% +``` + +# Editing And Testing The Debugger Visualizer + +### Modify The Visualizer + +To modify how components are visualized in the debugger edit the file `stl\debugger\STL.natvis`. For more information on +how to modify this file check the [natvis documentation][]. + +### Test Your Changes + +You can add the natvis file to any Visual Studio C++ project if you right click your project > Add > Existing Item and +select the STL.natvis file. After doing this you should be able to see your changes in a Visual Studio debugging +session. + # Block Diagram The STL is built atop other compiler support libraries that ship with Windows and Visual Studio, like the UCRT, @@ -405,7 +456,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception [LWG issues]: https://cplusplus.github.io/LWG/lwg-toc.html [LWG tag]: https://github.com/microsoft/STL/issues?q=is%3Aopen+is%3Aissue+label%3ALWG [Microsoft Open Source Code of Conduct]: https://opensource.microsoft.com/codeofconduct/ -[N4878]: https://wg21.link/n4878 +[N4885]: https://wg21.link/n4885 [NOTICE.txt]: NOTICE.txt [Ninja]: https://ninja-build.org [Pipelines]: https://dev.azure.com/vclibs/STL/_build/latest?definitionId=4&branchName=main @@ -423,3 +474,4 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception [opencode@microsoft.com]: mailto:opencode@microsoft.com [redistributables]: https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads [vcpkg]: https://github.com/microsoft/vcpkg +[natvis documentation]: https://docs.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects diff --git a/azure-devops/create-vmss.ps1 b/azure-devops/create-vmss.ps1 index 64fa971fdc8..a927b1cc164 100644 --- a/azure-devops/create-vmss.ps1 +++ b/azure-devops/create-vmss.ps1 @@ -25,7 +25,9 @@ $Prefix = 'StlBuild-' + (Get-Date -Format 'yyyy-MM-dd') $VMSize = 'Standard_D32ds_v4' $ProtoVMName = 'PROTOTYPE' $LiveVMPrefix = 'BUILD' -$WindowsServerSku = '2019-Datacenter' +$ImagePublisher = 'MicrosoftWindowsDesktop' +$ImageOffer = 'Windows-10' +$ImageSku = '20h2-ent-g2' $ProgressActivity = 'Creating Scale Set' $TotalProgress = 12 @@ -268,9 +270,9 @@ $VM = Set-AzVMOperatingSystem ` $VM = Add-AzVMNetworkInterface -VM $VM -Id $Nic.Id $VM = Set-AzVMSourceImage ` -VM $VM ` - -PublisherName 'MicrosoftWindowsServer' ` - -Offer 'WindowsServer' ` - -Skus $WindowsServerSku ` + -PublisherName $ImagePublisher ` + -Offer $ImageOffer ` + -Skus $ImageSku ` -Version latest $VM = Set-AzVMBootDiagnostic -VM $VM -Disable @@ -340,7 +342,7 @@ Set-AzVM ` $VM = Get-AzVM -ResourceGroupName $ResourceGroupName -Name $ProtoVMName $PrototypeOSDiskName = $VM.StorageProfile.OsDisk.Name -$ImageConfig = New-AzImageConfig -Location $Location -SourceVirtualMachineId $VM.ID +$ImageConfig = New-AzImageConfig -Location $Location -SourceVirtualMachineId $VM.ID -HyperVGeneration 'V2' $Image = New-AzImage -Image $ImageConfig -ImageName $ProtoVMName -ResourceGroupName $ResourceGroupName #################################################################################################### diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8e8832a925f..9c0fa71b56e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -8,7 +8,7 @@ variables: buildOutputLocation: 'D:\build' vcpkgLocation: '$(Build.SourcesDirectory)/vcpkg' -pool: 'StlBuild-2021-03-02' +pool: 'StlBuild-2021-03-09-win10' stages: - stage: Code_Format diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index 33a9104b08d..751c6ad78aa 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -4,6 +4,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_all_public_headers.hpp ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_system_error_abi.hpp + ${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_tzdb.hpp ${CMAKE_CURRENT_LIST_DIR}/inc/algorithm ${CMAKE_CURRENT_LIST_DIR}/inc/any ${CMAKE_CURRENT_LIST_DIR}/inc/array @@ -400,6 +401,7 @@ set(SOURCES_SATELLITE_ATOMIC_WAIT ${CMAKE_CURRENT_LIST_DIR}/src/atomic_wait.cpp ${CMAKE_CURRENT_LIST_DIR}/src/parallel_algorithms.cpp ${CMAKE_CURRENT_LIST_DIR}/src/syncstream.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/tzdb.cpp ) set(SOURCES_SATELLITE_CODECVT_IDS @@ -499,7 +501,7 @@ function(add_stl_dlls D_SUFFIX THIS_CONFIG_DEFINITIONS THIS_CONFIG_COMPILE_OPTIO file(WRITE "${_ATOMIC_WAIT_DEF_NAME}" "${_ATOMIC_WAIT_DEF_CONTENTS}") add_library(msvcp${D_SUFFIX}_atomic_wait SHARED "${_ATOMIC_WAIT_DEF_NAME}") - target_link_libraries(msvcp${D_SUFFIX}_atomic_wait PRIVATE msvcp${D_SUFFIX}_atomic_wait_objects msvcp${D_SUFFIX}_satellite_objects msvcp${D_SUFFIX}_implib_objects "msvcp${D_SUFFIX}" "${TOOLSET_LIB}/vcruntime${D_SUFFIX}.lib" "${TOOLSET_LIB}/msvcrt${D_SUFFIX}.lib" "ucrt${D_SUFFIX}.lib") + target_link_libraries(msvcp${D_SUFFIX}_atomic_wait PRIVATE msvcp${D_SUFFIX}_atomic_wait_objects msvcp${D_SUFFIX}_satellite_objects msvcp${D_SUFFIX}_implib_objects "msvcp${D_SUFFIX}" "${TOOLSET_LIB}/vcruntime${D_SUFFIX}.lib" "${TOOLSET_LIB}/msvcrt${D_SUFFIX}.lib" "ucrt${D_SUFFIX}.lib" "advapi32.lib") set_target_properties(msvcp${D_SUFFIX}_atomic_wait PROPERTIES ARCHIVE_OUTPUT_NAME "msvcp140_atomic_wait${D_SUFFIX}${VCLIBS_SUFFIX}") set_target_properties(msvcp${D_SUFFIX}_atomic_wait PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") set_target_properties(msvcp${D_SUFFIX}_atomic_wait PROPERTIES OUTPUT_NAME "${_ATOMIC_WAIT_OUTPUT_NAME}") diff --git a/stl/debugger/STL.natvis b/stl/debugger/STL.natvis new file mode 100644 index 00000000000..aed5ae4210d --- /dev/null +++ b/stl/debugger/STL.natvis @@ -0,0 +1,1634 @@ + + + + + + + {*($T1 *)this} + + *($T1 *)this + + + + + + {_Myval1} + + _Myval1 + + + + + + + null + + + + + + {{ size={_Last - _First} }} + + + _Last - _First + _First + + + + + + + {first}, {second} + ({first}, {second}) + + first + second + + + + + + + ({*this,view(noparens)}) + + + + + {_Myfirst._Val} + ({*this,view(noparens)}) + + _Myfirst._Val + + + + + {_Myfirst._Val}, {((_Mybase *) this)->_Myfirst._Val} + ({*this,view(noparens)}) + + _Myfirst._Val + ((_Mybase *) this)->_Myfirst._Val + + + + + {_Myfirst._Val}, {((_Mybase *) this)->_Myfirst._Val}, {((_Mybase::_Mybase *) this)->_Myfirst._Val} + ({*this,view(noparens)}) + + _Myfirst._Val + ((_Mybase *) this)->_Myfirst._Val + ((_Mybase::_Mybase *) this)->_Myfirst._Val + + + + + {_Myfirst._Val}, {((_Mybase *) this)->_Myfirst._Val}, {((_Mybase::_Mybase *) this)->_Myfirst._Val}, {((_Mybase::_Mybase::_Mybase *) this)->_Myfirst._Val} + ({*this,view(noparens)}) + + _Myfirst._Val + ((_Mybase *) this)->_Myfirst._Val + ((_Mybase::_Mybase *) this)->_Myfirst._Val + ((_Mybase::_Mybase::_Mybase *) this)->_Myfirst._Val + + + + + {_Myfirst._Val}, {((_Mybase *) this)->_Myfirst._Val}, {((_Mybase::_Mybase *) this)->_Myfirst._Val}, {((_Mybase::_Mybase::_Mybase *) this)->_Myfirst._Val}, {((_Mybase::_Mybase::_Mybase::_Mybase *) this)->_Myfirst._Val} + ({*this,view(noparens)}) + + _Myfirst._Val + ((_Mybase *) this)->_Myfirst._Val + ((_Mybase::_Mybase *) this)->_Myfirst._Val + ((_Mybase::_Mybase::_Mybase *) this)->_Myfirst._Val + ((_Mybase::_Mybase::_Mybase::_Mybase *) this)->_Myfirst._Val + + + + + {_Myfirst._Val}, {((_Mybase *) this)->_Myfirst._Val}, {((_Mybase::_Mybase *) this)->_Myfirst._Val}, {((_Mybase::_Mybase::_Mybase *) this)->_Myfirst._Val}, {((_Mybase::_Mybase::_Mybase::_Mybase *) this)->_Myfirst._Val}, {*((_Mybase::_Mybase::_Mybase::_Mybase::_Mybase *) this),view(noparens)} + ({*this,view(noparens)}) + + _Myfirst._Val + ((_Mybase *) this)->_Myfirst._Val + ((_Mybase::_Mybase *) this)->_Myfirst._Val + ((_Mybase::_Mybase::_Mybase *) this)->_Myfirst._Val + ((_Mybase::_Mybase::_Mybase::_Mybase *) this)->_Myfirst._Val + Next five elements: + *((_Mybase::_Mybase::_Mybase::_Mybase::_Mybase *) this) + + + + + + nullopt + + + + + + nullopt + {value()} + + value() + + + + + + {_Elem} + + + + + [valueless_by_exception] + {{ index=0, value={_Head} }} + {{ index=1, value={_Tail._Head} }} + {{ index=2, value={_Tail._Tail._Head} }} + {{ index=3, value={_Tail._Tail._Tail._Head} }} + {{ index=4, value={_Tail._Tail._Tail._Tail._Head} }} + {{ index=5, value={_Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=6, value={_Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=7, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=8, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=9, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=10, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=11, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=12, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=13, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=14, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=15, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=16, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=17, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=18, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=19, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=20, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=21, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=22, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=23, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=24, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=25, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=26, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=27, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=28, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=29, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=30, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + {{ index=31, value={_Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head} }} + + index() + _Head + _Tail._Head + _Tail._Tail._Head + _Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + _Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Tail._Head + + + + + monostate + + + + + + + + + + + [empty] + [not empty (Small)] + [not empty (Large)] + + + {has_value()} + + + {type()} + + + (Small/Trivial Object) + + + (Small Object) + + + (Dynamic Allocation) + + + + + + + {{ size={$T1} }} + + + $T1 + (_Array[$i / _Bitsperword] >> ($i % _Bitsperword)) & 1,d + + + + + + {(_Pbitset->_Array[_Mypos / _Pbitset->_Bitsperword] >> (_Mypos % _Pbitset->_Bitsperword)) & 1,d} + + _Pbitset + _Mypos + + + + + + allocator + + + + + + default_delete + + + + + + _Myptr + empty + unique_ptr {*_Myptr} + + _Myptr + + + + + + _Mypair._Myval2 + empty + unique_ptr {*_Mypair._Myval2} + + _Mypair._Myval2 + _Mypair + + + + + + default + + _Ptr + + + + + + custom deleter + + _Ptr + _Dtor + + + + + + custom deleter + + _Mypair._Myval2 + _Mypair + + + + + + custom deleter, custom allocator + + _Ptr + _Dtor + _Myal + + + + + + custom deleter, custom allocator + + _Mypair._Myval2._Myval2 + _Mypair + _Mypair._Myval2 + + + + + make_shared + + ($T1 *) &_Storage + + + + + + allocate_shared + + ($T1 *) &_Storage + _Myal + + + + + + allocate_shared + + ($T1 *) &_Mypair._Myval2 + _Mypair + + + + + + custom deleter + + _Mypair._Myval2 + _Mypair + + + + + + custom deleter, custom allocator + + _Mypair._Myval2._Myval2 + _Mypair + _Mypair._Myval2 + + + + + _Ptr + empty + nullptr + {*_Ptr} + shared_ptr {*this,view(ptr)} [{_Rep->_Uses} strong ref] [{*_Rep}] + shared_ptr {*this,view(ptr)} [{_Rep->_Uses} strong ref, {_Rep->_Weaks - 1} weak ref] [{*_Rep}] + shared_ptr {*this,view(ptr)} [{_Rep->_Uses} strong ref, {_Rep->_Weaks - 1} weak refs] [{*_Rep}] + shared_ptr {*this,view(ptr)} [{_Rep->_Uses} strong refs] [{*_Rep}] + shared_ptr {*this,view(ptr)} [{_Rep->_Uses} strong refs, {_Rep->_Weaks - 1} weak ref] [{*_Rep}] + shared_ptr {*this,view(ptr)} [{_Rep->_Uses} strong refs, {_Rep->_Weaks - 1} weak refs] [{*_Rep}] + + _Ptr + *_Rep + + + + + empty + nullptr + {*_Ptr} + expired [{_Rep->_Weaks} weak ref] [{*_Rep}] + expired [{_Rep->_Weaks} weak refs] [{*_Rep}] + weak_ptr {*this,view(ptr)} [{_Rep->_Uses} strong ref, {_Rep->_Weaks - 1} weak ref] [{*_Rep}] + weak_ptr {*this,view(ptr)} [{_Rep->_Uses} strong ref, {_Rep->_Weaks - 1} weak refs] [{*_Rep}] + weak_ptr {*this,view(ptr)} [{_Rep->_Uses} strong refs, {_Rep->_Weaks - 1} weak ref] [{*_Rep}] + weak_ptr {*this,view(ptr)} [{_Rep->_Uses} strong refs, {_Rep->_Weaks - 1} weak refs] [{*_Rep}] + + _Ptr + *_Rep + + + + + + + {*_Ptr} + + _Ptr + + + + + + {_Object} + + + + + + {_Callee} + + _Callee + + + + + + {*_Ptr} + + _Ptr + + + + + plus<> + minus<> + multiplies<> + divides<> + modulus<> + negate<> + equal_to<> + not_equal_to<> + greater<> + less<> + greater_equal<> + less_equal<> + logical_and<> + logical_or<> + logical_not<> + bit_and<> + bit_or<> + bit_xor<> + bit_not<> + + + + plus + + + + + minus + + + + + multiplies + + + + + divides + + + + + modulus + + + + + negate + + + + + equal_to + + + + + not_equal_to + + + + + greater + + + + + less + + + + + greater_equal + + + + + less_equal + + + + + logical_and + + + + + logical_or + + + + + logical_not + + + + + bit_and + + + + + bit_or + + + + + bit_xor + + + + + bit_not + + + + + + not1({_Functor}) + + _Functor + + + + + not2({_Functor}) + + _Functor + + + + + + _{$T1,d} + + + + + + bind({_Myfun}, {_Mybargs,view(noparens)}) + + _Myfun + _Mybargs + + + + + + bind({_Mypair}, {_Mypair._Myval2,view(noparens)}) + + _Mypair + _Mypair._Myval2 + + + + + + + mem_fn({_Callee._Object}) + + + + + + mem_fn({_Pm}) + + + + + + + {_Callee._Object} + + _Callee._Object + _Myal + + + + + + {_Mypair._Myval2} + + _Mypair._Myval2 + _Mypair + + + + + {_Callee} + + _Callee + + + + + + empty + {*_Impl} + + *_Impl + + + + + + empty + {*_Mystorage._Ptrs[_EEN_IMPL]} + + *_Mystorage._Ptrs[_EEN_IMPL] + + + + + + hash + + + + + + {_MyRep} nanoseconds + + + + + {_MyRep} microseconds + + + + + {_MyRep} milliseconds + + + + + {_MyRep} seconds + + + + + {_MyRep} minutes + + + + + {_MyRep} hours + + + + + + + {_Bx._Buf,na} + {_Bx._Ptr,na} + _Bx._Buf,na + _Bx._Ptr,na + + _Mysize + _Myres + + _Mysize + _Bx._Buf + _Bx._Ptr + + + + + + + + {_Bx._Buf,su} + {_Bx._Ptr,su} + _Bx._Buf,su + _Bx._Ptr,su + + _Mysize + _Myres + + _Mysize + _Bx._Buf + _Bx._Ptr + + + + + + + + + + + + + {_Mypair._Myval2._Bx._Buf,na} + {_Mypair._Myval2._Bx._Ptr,na} + _Mypair._Myval2._Bx._Buf,na + _Mypair._Myval2._Bx._Ptr,na + + size() + capacity() + _Mypair + + _Mypair._Myval2._Mysize + _Mypair._Myval2._Bx._Buf + _Mypair._Myval2._Bx._Ptr + + + + + + + + + + + + + + + {_Mypair._Myval2._Bx._Buf,su} + {_Mypair._Myval2._Bx._Ptr,su} + _Mypair._Myval2._Bx._Buf,su + _Mypair._Myval2._Bx._Ptr,su + + size() + capacity() + _Mypair + + _Mypair._Myval2._Mysize + _Mypair._Myval2._Bx._Buf + _Mypair._Myval2._Bx._Ptr + + + + + + + + + + + + {_Mypair._Myval2._Bx._Buf,s32} + {_Mypair._Myval2._Bx._Ptr,s32} + _Mypair._Myval2._Bx._Buf,s32 + _Mypair._Myval2._Bx._Ptr,s32 + + size() + capacity() + _Mypair + + _Mypair._Myval2._Mysize + _Mypair._Myval2._Bx._Buf + _Mypair._Myval2._Bx._Ptr + + + + + + + _Ptr,na + + _Ptr + + + + + + + + + + _Ptr,su + + _Ptr + + + + + + _Ptr,s32 + + _Ptr + + + + + + + {_Mydata,[_Mysize]} + _Mydata,[_Mysize] + + size() + + size() + data() + + + + + + + _Myptr + + _Myptr + + + + + + _Mydata + _Myoff + + _Mydata + _Myoff + _Myoff + _Mydata,[_Mysize] + + + + + + {{ size={$T2} }} + + + $T2 + _Elems + + + + + + + + _Ptr,na + + _Ptr + + + + + + + _Ptr + _Idx + {_Ptr[_Idx]} + end + + _Ptr + _Idx + + + + + + {{ size={_Mysize} }} + + + _Mysize + _Map[(($i + _Myoff) / _EEN_DS) % _Mapsize][($i + _Myoff) % _EEN_DS] + + + + + + + {{ size={_Mypair._Myval2._Mysize} }} + + _Mypair + + _Mypair._Myval2._Mysize + _Mypair._Myval2._Map[(($i + _Mypair._Myval2._Myoff) / _EEN_DS) % _Mypair._Myval2._Mapsize][($i + _Mypair._Myval2._Myoff) % _EEN_DS] + + + + + + + {((_Mydeque_t *)_Myproxy->_Mycont)->_Map[(_Myoff / _EEN_DS) % ((_Mydeque_t *)_Myproxy->_Mycont)->_Mapsize][_Myoff % _EEN_DS]} + end + + _Myoff - ((_Mydeque_t *)_Myproxy->_Mycont)->_Myoff + &((_Mydeque_t *)_Myproxy->_Mycont)->_Map[(_Myoff / _EEN_DS) % ((_Mydeque_t *)_Myproxy->_Mycont)->_Mapsize][_Myoff % _EEN_DS] + + + + + + + empty + non-empty + + + _Myhead + _Next + _Myval + + + + + + + empty + non-empty + + _Mypair + + _Mypair._Myval2._Myhead + _Next + _Myval + + + + + + + &_Ptr->_Myval,na + end + {**this} + + + + + + {{ size={_Mysize} }} + + + _Mysize + _Myhead->_Next + _Next + _Myval + + + + + + + {{ size={_Mypair._Myval2._Mysize} }} + + _Mypair + + _Mypair._Myval2._Mysize + _Mypair._Myval2._Myhead->_Next + _Next + _Myval + + + + + + + &_Ptr->_Myval,na + + + + + + {{ size={_Mysize} }} + + (_Myvec._Myend - _Myvec._Myfirst) * _EEN_VBITS + + _Mysize + (bool)((_Myvec._Myfirst[$i / _EEN_VBITS] >> ($i % _EEN_VBITS)) & 1) + + + + + + + + + {{ size={size()} }} + + capacity() + _Myvec._Mypair + + _Mysize + (bool)((_Myvec._Mypair._Myval2._Myfirst[$i / _EEN_VBITS] >> ($i % _EEN_VBITS)) & 1) + + + + + + + + {(bool)((*_Myptr >> _Myoff) & 1)} + + _Myptr + _Myoff + + + + + + + {{ size={_Mylast - _Myfirst} }} + + _Myend - _Myfirst + + _Mylast - _Myfirst + _Myfirst + + + + + + + + + {{ size={size()} }} + + capacity() + _Mypair + + size() + _Mypair._Myval2._Myfirst + + + + + + + _Ptr,na + + _Ptr + + + + + + + + + + {{ size={_Mysize} }} + + + _Mysize + _Myhead->_Parent + _Left + _Right + _Myval + + + + + + + + + + {{ size={_Mypair._Myval2._Myval2._Mysize} }} + + _Mypair + _Mypair._Myval2 + + _Mypair._Myval2._Myval2._Mysize + _Mypair._Myval2._Myval2._Myhead->_Parent + _Left + _Right + _Myval + + + + + + + + {{ size={_Mypair._Myval2._Myval2._Mysize} }} + + _Mypair + _Mypair._Myval2 + + _Mypair._Myval2._Myval2._Mysize + _Mypair._Myval2._Myval2._Myhead->_Parent + _Left + _Right + _Myval,view(MapHelper) + + + + + + {second} + + + + + _Ptr->_Isnil ? nullptr : &_Ptr->_Myval + {_Ptr->_Myval} + end + + &_Ptr->_Myval + + + + + + + + + + + + + + {_List} + + _List + + + + + + + + + + + + + {_List} + + _Maxidx + ((float)_List._Mypair._Myval2._Mysize) / ((float)_Maxidx) + _Traitsobj._Mypair._Myval2._Myval2 + _Traitsobj._Mypair + _Traitsobj._Mypair._Myval2 + _List._Mypair + _List,view(simple) + + + + + + {_List} + + _Maxidx + ((float)_List._Mypair._Myval2._Mysize) / ((float)_Maxidx) + _Traitsobj._Mypair._Myval2._Myval2 + _Traitsobj._Mypair + _Traitsobj._Mypair._Myval2 + _List._Mypair + _List,view(MapHelper) + + + + + + Test + + + _Mypair._Myval2._Mysize + _Mypair._Myval2._Myhead->_Next + _Next + _Myval + + + + + + + + {c} + + c + + + + + + {c} + + c + comp + + + + + + + + current._Ptr - 1,na + reverse_iterator {current._Ptr[-1]} + + current._Ptr - 1 + + + + + + + current._Ptr + current._Idx - 1,na + reverse_iterator {current._Ptr[current._Idx - 1]} + reverse_iterator rend + + current._Ptr + current._Idx - 1 + + + + + + &current._Ptr->_Prev->_Myval,na + reverse_iterator {**this} + + + + + current._Ptr - 1,na + reverse_iterator {**this} + + current._Ptr - 1 + + + + + reverse_iterator base() {current} + + NOTE: *ri is equivalent to *prev(ri.base()) + current + + + + + + current - 1 + reverse_iterator {current[-1]} + + current - 1 + + + + + + back_insert_iterator into {container} + + container + + + + + + front_insert_iterator into {container} + + container + + + + + + insert_iterator into {container} at {iter} + + container + iter + + + + + + move_iterator {current} + + current + + + + + + -i*{-_Val[1]} + {_Val[0]}-i*{-_Val[1]} + {_Val[0]} + i*{_Val[1]} + {_Val[0]}+i*{_Val[1]} + + _Val[0] + _Val[1] + + + + + + {{ size={_Mysize} }} + + + _Mysize + _Myptr + + + + + + + + + {data(),[size()]} + + + + + {_Stringbuffer} + + _Stringbuffer + + + + + {_Stringbuffer} + + _Stringbuffer + + + + + {_Stringbuffer} + + _Stringbuffer + + + + + + empty + {_Visualization} + _Visualization + + + + + + {first,[second - first]na} + false + + matched + first + second + + + + + + {first._Ptr,[second._Ptr - first._Ptr]na} + false + + matched + first + second + + + + + + not ready + {_Matches} + + _Matches,view(simple) + _Prefix + _Suffix + + + + + + &_MyVal + end + {_MyVal} + + _Begin + _End + _MyRe + _Flags + _MyVal + + + + + + _Res + end + {*_Res} + + _Pos + _Res + _Suffix + _Cur + _Subs + + + + + + + + + + {value()} + + value() + + + + + + + + + + {value()} + + + + + + + unlocked + locked + unlocked + locked + + *(long *)((char *)(&_Mtx_storage) + 40) + *(long *)((char *)(&_Mtx_storage) + 72) + *(int *)((char *)(&_Mtx_storage) + 44) + *(int *)((char *)(&_Mtx_storage) + 76) + + + + + + + + _Retrieved + _Result + _Exception + + + + + + + empty + pending + has_result + + *_MyPromise._State._Assoc_state + _MyPromise._Future_retrieved + + + + + + + empty + pending + has_result + + *_Assoc_state + + + + + + + ptr_fun({_Pfun}) + + + + + + + + mem_fun({_Pmemfun}) + + + + + + + + mem_fun_ref({_Pmemfun}) + + + + + bind1st({op}, {value}) + + op + value + + + + + bind2nd({op}, {value}) + + op + value + + + + + empty + auto_ptr {*_Myptr} + + _Myptr + + + + + + + {_Ptr->_Fn,na}, #initial suspend + {_Ptr->_Fn,na}, #final suspend + {_Ptr->_Fn,na}, suspend point #{(_Ptr->_Index)/2 - 1} + {_Ptr->_Fn,na} + + "initial suspend" + "final suspend" + (_Ptr->_Index)/2 - 1 + this,view(ViewPromise) + + + + + + + + + + + + *reinterpret_cast<$T1 *>(reinterpret_cast<char*>(_Ptr) - _ALIGNED_SIZE) + + + + + + + *reinterpret_cast<$T1 *>(reinterpret_cast<char*>(_Ptr) + 2*sizeof(void*)) + + + + + + + + + + + + + + empty + {primary_function(_Ptr),na} #final suspend + {primary_function(_Ptr),na} #initial suspend + {primary_function(_Ptr),na} #suspend point {suspend_point(_Ptr)} + {primary_function(_Ptr),na} #suspend point {suspend_point(_Ptr)}, line {suspend_point_line(_Ptr)} + + this,view(ViewPromise) + + + + + + + + + diff --git a/stl/inc/__msvc_tzdb.hpp b/stl/inc/__msvc_tzdb.hpp new file mode 100644 index 00000000000..d252f8a6d5d --- /dev/null +++ b/stl/inc/__msvc_tzdb.hpp @@ -0,0 +1,154 @@ +// __msvc_tzdb.hpp internal header + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef __MSVC_TZDB_HPP +#define __MSVC_TZDB_HPP +#include +#if _STL_COMPILER_PREPROCESSOR +#include +#include +#include +#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 + +using __std_tzdb_epoch_milli = double; + +struct __std_tzdb_leap_info { + uint16_t _Year; + uint16_t _Month; + uint16_t _Day; + uint16_t _Hour; + uint16_t _Negative; + uint16_t _Reserved; +}; + +enum class __std_tzdb_error { + _Success = 0, + _Win_error = 1, + _Icu_error = 2, +}; + +struct __std_tzdb_time_zones_info { + __std_tzdb_error _Err; + // timezone data version currently being used + const char* _Version; + size_t _Num_time_zones; + // ordered list of null-terminated time_zone/time_zone_link names + const char** _Names; + // contains corresponding entry for every name, if: + // (_Links[i] == nullptr) then _Names[i] is a time_zone + // (_Links[i] != nullptr) then _Names[i] is a time_zone_link to time_zone with name _Links[i] + const char** _Links; +}; + +struct __std_tzdb_current_zone_info { + __std_tzdb_error _Err; + const char* _Tz_name; +}; + +struct __std_tzdb_sys_info { + __std_tzdb_error _Err; + __std_tzdb_epoch_milli _Begin; + __std_tzdb_epoch_milli _End; + int32_t _Offset; + int32_t _Save; + const char* _Abbrev; +}; + +_EXTERN_C + +_NODISCARD __std_tzdb_time_zones_info* __stdcall __std_tzdb_get_time_zones() noexcept; +void __stdcall __std_tzdb_delete_time_zones(__std_tzdb_time_zones_info* _Info) noexcept; + +_NODISCARD __std_tzdb_current_zone_info* __stdcall __std_tzdb_get_current_zone() noexcept; +void __stdcall __std_tzdb_delete_current_zone(__std_tzdb_current_zone_info* _Info) noexcept; + +_NODISCARD __std_tzdb_sys_info* __stdcall __std_tzdb_get_sys_info( + const char* _Tz, size_t _Tz_len, __std_tzdb_epoch_milli _Local) noexcept; +void __stdcall __std_tzdb_delete_sys_info(__std_tzdb_sys_info* _Info) noexcept; + +_NODISCARD __std_tzdb_leap_info* __stdcall __std_tzdb_get_leap_seconds( + size_t _Prev_ls_size, size_t* _Current_ls_size) noexcept; +void __stdcall __std_tzdb_delete_leap_seconds(__std_tzdb_leap_info* _Info) noexcept; + +_NODISCARD void* __stdcall __std_calloc_crt(size_t _Count, size_t _Size) noexcept; +void __stdcall __std_free_crt(void* _Ptr) noexcept; + +_END_EXTERN_C + +_STD_BEGIN + +template +struct _Tzdb_deleter; + +template <> +struct _Tzdb_deleter<__std_tzdb_time_zones_info> { + void operator()(__std_tzdb_time_zones_info* _Info) const noexcept { + __std_tzdb_delete_time_zones(_Info); + } +}; + +template <> +struct _Tzdb_deleter<__std_tzdb_current_zone_info> { + void operator()(__std_tzdb_current_zone_info* _Info) const noexcept { + __std_tzdb_delete_current_zone(_Info); + } +}; + +template <> +struct _Tzdb_deleter<__std_tzdb_sys_info> { + void operator()(__std_tzdb_sys_info* _Info) const noexcept { + __std_tzdb_delete_sys_info(_Info); + } +}; + +template <> +struct _Tzdb_deleter<__std_tzdb_leap_info[]> { + void operator()(__std_tzdb_leap_info* _Info) const noexcept { + __std_tzdb_delete_leap_seconds(_Info); + } +}; + +template +class _Crt_allocator { +public: + using value_type = _Ty; + using propagate_on_container_move_assignment = true_type; + using is_always_equal = true_type; + + constexpr _Crt_allocator() noexcept = default; + + constexpr _Crt_allocator(const _Crt_allocator&) noexcept = default; + template + constexpr _Crt_allocator(const _Crt_allocator<_Other>&) noexcept {} + + _NODISCARD __declspec(allocator) _Ty* allocate(_CRT_GUARDOVERFLOW const size_t _Count) { + const auto _Ptr = __std_calloc_crt(_Count, sizeof(_Ty)); + if (!_Ptr) { + _Xbad_alloc(); + } + return static_cast<_Ty*>(_Ptr); + } + + void deallocate(_Ty* const _Ptr, size_t) noexcept { + __std_free_crt(_Ptr); + } +}; + +_STD_END + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) +#endif // _STL_COMPILER_PREPROCESSOR +#endif // __MSVC_TZDB_HPP diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 8f13788c196..e128b93569c 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -114,23 +114,6 @@ namespace ranges { } }; - // STRUCT TEMPLATE in_in_result - template - struct in_in_result { - /* [[no_unique_address]] */ _In1 in1; - /* [[no_unique_address]] */ _In2 in2; - - template <_Convertible_from _IIn1, _Convertible_from _IIn2> - constexpr operator in_in_result<_IIn1, _IIn2>() const& { - return {in1, in2}; - } - - template <_Convertible_from<_In1> _IIn1, _Convertible_from<_In2> _IIn2> - constexpr operator in_in_result<_IIn1, _IIn2>() && { - return {_STD move(in1), _STD move(in2)}; - } - }; - // STRUCT TEMPLATE in_in_out_result template struct in_in_out_result { @@ -416,21 +399,6 @@ namespace ranges { inline constexpr _For_each_n_fn for_each_n{_Not_quite_object::_Construct_tag{}}; // VARIABLE ranges::find - // clang-format off - // concept-constrained for strict enforcement as it is used by several algorithms - template _Se, class _Ty, class _Pj> - requires indirect_binary_predicate, const _Ty*> - _NODISCARD constexpr _It _Find_unchecked(_It _First, const _Se _Last, const _Ty& _Val, _Pj _Proj) { - for (; _First != _Last; ++_First) { - if (_STD invoke(_Proj, *_First) == _Val) { - break; - } - } - - return _First; - } - // clang-format on - class _Find_fn : private _Not_quite_object { public: using _Not_quite_object::_Not_quite_object; @@ -792,107 +760,19 @@ _NODISCARD pair<_FwdIt1, _FwdIt2> mismatch( #ifdef __cpp_lib_concepts namespace ranges { - // ALIAS TEMPLATE mismatch_result - template - using mismatch_result = in_in_result<_In1, _In2>; - - // VARIABLE ranges::mismatch - class _Mismatch_fn : private _Not_quite_object { - private: - template - _NODISCARD static constexpr mismatch_result<_It1, _It2> _Mismatch_n( - _It1 _First1, _It2 _First2, iter_difference_t<_It1> _Count, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { - auto _UFirst1 = _Get_unwrapped(_STD move(_First1)); - auto _UFirst2 = _Get_unwrapped(_STD move(_First2)); - - for (; _Count != 0; ++_UFirst1, (void) ++_UFirst2, --_Count) { - if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_UFirst1), _STD invoke(_Proj2, *_UFirst2))) { - break; - } - } - - _Seek_wrapped(_First1, _STD move(_UFirst1)); - _Seek_wrapped(_First2, _STD move(_UFirst2)); - return {_STD move(_First1), _STD move(_First2)}; - } - - template - _NODISCARD static constexpr mismatch_result<_It1, _It2> _Mismatch_4( - _It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { - auto _UFirst1 = _Get_unwrapped(_STD move(_First1)); - const auto _ULast1 = _Get_unwrapped(_STD move(_Last1)); - auto _UFirst2 = _Get_unwrapped(_STD move(_First2)); - const auto _ULast2 = _Get_unwrapped(_STD move(_Last2)); - - for (; _UFirst1 != _ULast1 && _UFirst2 != _ULast2; ++_UFirst1, (void) ++_UFirst2) { - if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_UFirst1), _STD invoke(_Proj2, *_UFirst2))) { - break; - } - } - - _Seek_wrapped(_First1, _STD move(_UFirst1)); - _Seek_wrapped(_First2, _STD move(_UFirst2)); - return {_STD move(_First1), _STD move(_First2)}; - } - - public: - using _Not_quite_object::_Not_quite_object; - - // clang-format off - template _Se1, input_iterator _It2, sentinel_for<_It2> _Se2, - class _Pr = ranges::equal_to, class _Pj1 = identity, class _Pj2 = identity> - requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2> - _NODISCARD constexpr mismatch_result<_It1, _It2> operator()(_It1 _First1, _Se1 _Last1, - _It2 _First2, _Se2 _Last2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { - _Adl_verify_range(_First1, _Last1); - _Adl_verify_range(_First2, _Last2); - - if constexpr (sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2>) { - iter_difference_t<_It1> _Count1 = _Last1 - _First1; - const iter_difference_t<_It2> _Count2 = _Last2 - _First2; - if (_Count1 > _Count2) { - _Count1 = static_cast(_Count2); - } - - return _Mismatch_n(_STD move(_First1), _STD move(_First2), _Count1, - _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); - } else { - return _Mismatch_4(_STD move(_First1), _STD move(_Last1), _STD move(_First2), _STD move(_Last2), - _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); - } - } - - template - requires indirectly_comparable, iterator_t<_Rng2>, _Pr, _Pj1, _Pj2> - _NODISCARD constexpr mismatch_result, borrowed_iterator_t<_Rng2>> operator()( - _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { - if constexpr (sized_range<_Rng1> && sized_range<_Rng2>) { - range_difference_t<_Rng1> _Count1 = _RANGES distance(_Range1); - const range_difference_t<_Rng2> _Count2 = _RANGES distance(_Range2); - if (_Count1 > _Count2) { - _Count1 = static_cast>(_Count2); - } - - return _Mismatch_n(_RANGES begin(_Range1), _RANGES begin(_Range2), _Count1, - _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); - } else { - return _Mismatch_4(_RANGES begin(_Range1), _RANGES end(_Range1), - _RANGES begin(_Range2), _RANGES end(_Range2), - _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); - } - } - // clang-format on - }; - - inline constexpr _Mismatch_fn mismatch{_Not_quite_object::_Construct_tag{}}; - // VARIABLE ranges::equal class _Equal_fn : private _Not_quite_object { private: template _NODISCARD static constexpr bool _Equal_count( _It1 _First1, _It2 _First2, _Size _Count, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + if constexpr (_Equal_memcmp_is_safe<_It1, _It2, + _Pr> && same_as<_Pj1, identity> && same_as<_Pj2, identity>) { + if (!_STD is_constant_evaluated()) { + return _Memcmp_count(_First1, _First2, static_cast(_Count)) == 0; + } + } + for (; _Count != 0; ++_First1, (void) ++_First2, --_Count) { if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { return false; @@ -1508,7 +1388,15 @@ namespace ranges { // clang-format off template _Se, weakly_incrementable _Out> requires indirectly_copyable<_It, _Out> - _NODISCARD constexpr copy_result<_It, _Out> _Copy_unchecked(_It _First, const _Se _Last, _Out _Result) { + _NODISCARD constexpr copy_result<_It, _Out> _Copy_unchecked(_It _First, _Se _Last, _Out _Result) { + if constexpr (_Ptr_copy_cat<_It, _Out>::_Trivially_copyable && sized_sentinel_for<_Se, _It>) { + if (!_STD is_constant_evaluated()) { + auto _Final = _RANGES next(_First, _STD move(_Last)); + _Result = _Copy_memmove(_STD move(_First), _Final, _STD move(_Result)); + return {_STD move(_Final), _STD move(_Result)}; + } + } + for (; _First != _Last; ++_First, (void) ++_Result) { *_Result = *_First; } @@ -1560,6 +1448,15 @@ namespace ranges { requires indirectly_copyable<_It, _Out> constexpr copy_n_result<_It, _Out> operator()(_It _First, iter_difference_t<_It> _Count, _Out _Result) const { auto _UFirst = _Get_unwrapped_n(_STD move(_First), _Count); + if constexpr (_Ptr_copy_cat::_Trivially_copyable) { + if (!_STD is_constant_evaluated()) { + auto _Final = _UFirst + _Count; + _Result = _Copy_memmove(_STD move(_UFirst), _Final, _STD move(_Result)); + _Seek_wrapped(_First, _STD move(_Final)); + return {_STD move(_First), _STD move(_Result)}; + } + } + for (; _Count > 0; ++_UFirst, (void) ++_Result, --_Count) { *_Result = *_UFirst; } @@ -1703,8 +1600,15 @@ namespace ranges { // clang-format off template _Se, weakly_incrementable _Out> requires indirectly_movable<_It, _Out> - constexpr move_result<_It, _Out> _Move_unchecked(_It _First, const _Se _Last, _Out _Result) { + constexpr move_result<_It, _Out> _Move_unchecked(_It _First, _Se _Last, _Out _Result) { // clang-format on + if constexpr (_Ptr_move_cat<_It, _Out>::_Trivially_copyable) { + if (!_STD is_constant_evaluated()) { + auto _Final = _RANGES next(_First, _STD move(_Last)); + _Result = _Copy_memmove(_STD move(_First), _Final, _STD move(_Result)); + return {_STD move(_Final), _STD move(_Result)}; + } + } for (; _First != _Last; ++_First, (void) ++_Result) { *_Result = _RANGES iter_move(_First); @@ -2127,10 +2031,7 @@ _NODISCARD _CONSTEXPR20 bool _Equal_rev_pred_unchecked(_InIt1 _First1, _InIt2 _F if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - const auto _First1_ch = _To_pointer(_First1); - const auto _First2_ch = _To_pointer(_First2); - const auto _Count = static_cast(_To_pointer(_Last2) - _First2_ch); - return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; + return _Memcmp_ranges(_First2, _Last2, _First1) == 0; } } @@ -2222,7 +2123,7 @@ namespace ranges { // clang-format off template concept _Equal_rev_pred_can_memcmp = is_same_v<_Pj1, identity> && is_same_v<_Pj2, identity> - && is_same_v<_Se2, _It2> && _Equal_memcmp_is_safe<_It1, _It2, _Pr>; + && sized_sentinel_for<_Se2, _It2> && _Equal_memcmp_is_safe<_It1, _It2, _Pr>; template _Se2, class _Pr, class _Pj1, class _Pj2> requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2> @@ -2233,12 +2134,14 @@ namespace ranges { constexpr bool _Optimize = _Equal_rev_pred_can_memcmp<_It1, _It2, _Se2, _Pr, _Pj1, _Pj2>; if constexpr (_Optimize) { if (!_STD is_constant_evaluated()) { - const auto _First1_ch = reinterpret_cast(_STD to_address(_First1)); - const auto _First2_ch = reinterpret_cast(_STD to_address(_First2)); - const auto _Count = - static_cast(reinterpret_cast(_STD to_address(_Last2)) - _First2_ch); - const bool _Eq = _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; - if (_Eq) { + bool _Ans; + if constexpr (same_as<_It2, _Se2>) { + _Ans = _Memcmp_ranges(_First2, _Last2, _First1) == 0; + } else { + _Ans = _Memcmp_count(_First1, _First2, static_cast(_Last2 - _First2)) == 0; + } + + if (_Ans) { _First1 += (_Last2 - _First2); return {true, _STD move(_First1)}; } else { @@ -3596,17 +3499,19 @@ namespace ranges { auto _UFirst = _Get_unwrapped(_STD move(_First)); const auto _ULast = _Get_unwrapped(_STD move(_Last)); if (!_STD is_constant_evaluated()) { - if constexpr (_Fill_memset_is_safe) { - const auto _Distance = static_cast(_ULast - _UFirst); - _Fill_memset(_UFirst, _Value, _Distance); - _Seek_wrapped(_First, _UFirst + _Distance); - return _First; - } else if constexpr (_Fill_zero_memset_is_safe) { - if (_Is_all_bits_zero(_Value)) { + if constexpr (sized_sentinel_for) { + if constexpr (_Fill_memset_is_safe) { const auto _Distance = static_cast(_ULast - _UFirst); - _Fill_zero_memset(_UFirst, _Distance); + _Fill_memset(_UFirst, _Value, _Distance); _Seek_wrapped(_First, _UFirst + _Distance); return _First; + } else if constexpr (_Fill_zero_memset_is_safe) { + if (_Is_all_bits_zero(_Value)) { + const auto _Distance = static_cast(_ULast - _UFirst); + _Fill_zero_memset(_UFirst, _Distance); + _Seek_wrapped(_First, _UFirst + _Distance); + return _First; + } } } } @@ -4482,10 +4387,11 @@ _CONSTEXPR20 _OutIt reverse_copy(_BidIt _First, _BidIt _Last, _OutIt _Dest) { auto _UDest = _Get_unwrapped_n(_Dest, _Idl_distance<_BidIt>(_UFirst, _ULast)); #if _USE_STD_VECTOR_ALGORITHMS - using _Elem = remove_pointer_t; - using _DestElem = remove_pointer_t; + using _Elem = remove_reference_t<_Iter_ref_t>>; + using _DestElem = remove_reference_t<_Iter_ref_t>; constexpr bool _Allow_vectorization = conjunction_v, _DestElem>, - is_pointer, is_trivially_copyable<_Elem>, negation>>; + bool_constant<_Iterators_are_contiguous>, is_trivially_copyable<_Elem>, + negation>>; constexpr size_t _Nx = sizeof(_Elem); #pragma warning(suppress : 6326) // Potential comparison of a constant with another constant @@ -4495,13 +4401,13 @@ _CONSTEXPR20 _OutIt reverse_copy(_BidIt _First, _BidIt _Last, _OutIt _Dest) { #endif // __cpp_lib_is_constant_evaluated { if constexpr (_Nx == 1) { - __std_reverse_copy_trivially_copyable_1(_UFirst, _ULast, _UDest); + __std_reverse_copy_trivially_copyable_1(_To_address(_UFirst), _To_address(_ULast), _To_address(_UDest)); } else if constexpr (_Nx == 2) { - __std_reverse_copy_trivially_copyable_2(_UFirst, _ULast, _UDest); + __std_reverse_copy_trivially_copyable_2(_To_address(_UFirst), _To_address(_ULast), _To_address(_UDest)); } else if constexpr (_Nx == 4) { - __std_reverse_copy_trivially_copyable_4(_UFirst, _ULast, _UDest); + __std_reverse_copy_trivially_copyable_4(_To_address(_UFirst), _To_address(_ULast), _To_address(_UDest)); } else { - __std_reverse_copy_trivially_copyable_8(_UFirst, _ULast, _UDest); + __std_reverse_copy_trivially_copyable_8(_To_address(_UFirst), _To_address(_ULast), _To_address(_UDest)); } _UDest += _ULast - _UFirst; @@ -4898,20 +4804,18 @@ _SampleIt sample(_PopIt _First, _PopIt _Last, _SampleIt _Dest, _Diff _Count, } #ifdef __cpp_lib_concepts -// STRUCT TEMPLATE _Require_constant -template -struct _Require_constant; // not defined; _Require_constant is a valid type if E is a constant expression - // CONCEPT uniform_random_bit_generator // clang-format off template -concept uniform_random_bit_generator = invocable<_Ty&> && unsigned_integral> && requires { - { (_Ty::min)() } -> same_as>; - { (_Ty::max)() } -> same_as>; - typename _Require_constant<(_Ty::min)()>; - typename _Require_constant<(_Ty::max)()>; - requires (_Ty::min)() < (_Ty::max)(); -}; +concept uniform_random_bit_generator = invocable<_Ty&> + && unsigned_integral> + && requires { + { (_Ty::min)() } -> same_as>; + { (_Ty::max)() } -> same_as>; + typename _Require_constant<(_Ty::min)()>; + typename _Require_constant<(_Ty::max)()>; + requires (_Ty::min)() < (_Ty::max)(); + }; // clang-format on namespace ranges { @@ -10488,6 +10392,18 @@ namespace ranges { _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); _STL_INTERNAL_STATIC_ASSERT(indirect_strict_weak_order<_Pr, projected<_It1, _Pj1>, projected<_It2, _Pj2>>); + using _Memcmp_classification_pred = + typename decltype(_Lex_compare_memcmp_classify(_First1, _First2, _Pred))::_Pred; + if constexpr (!is_void_v<_Memcmp_classification_pred> && sized_sentinel_for<_Se1, _It1> // + && sized_sentinel_for<_Se2, _It2> && same_as<_Pj1, identity> && same_as<_Pj2, identity>) { + if (!_STD is_constant_evaluated()) { + const auto _Num1 = static_cast(_Last1 - _First1); + const auto _Num2 = static_cast(_Last2 - _First2); + const int _Ans = _Memcmp_count(_First1, _First2, (_STD min)(_Num1, _Num2)); + return _Memcmp_classification_pred{}(_Ans, 0) || (_Ans == 0 && _Num1 < _Num2); + } + } + for (;; ++_First1, (void) ++_First2) { if (_First2 == _Last2) { return false; diff --git a/stl/inc/chrono b/stl/inc/chrono index 69d0f848fcf..2c52c87919c 100644 --- a/stl/inc/chrono +++ b/stl/inc/chrono @@ -16,10 +16,18 @@ #include #if _HAS_CXX20 +#include <__msvc_tzdb.hpp> +#include +#include +#include #include -#ifdef __cpp_lib_concepts -#include -#endif // defined(__cpp_lib_concepts) +#include +#include +#include +#include +#include +#include +#include #endif // _HAS_CXX20 #pragma pack(push, _CRT_PACKING) @@ -58,20 +66,19 @@ namespace chrono { }; #if _HAS_CXX20 + template + inline constexpr bool _Is_clock_v = false; + template - concept _Is_clock = requires { - typename _Clock::rep; - typename _Clock::period; - typename _Clock::duration; - typename _Clock::time_point; - _Clock::is_steady; - _Clock::now(); - }; + inline constexpr bool + _Is_clock_v<_Clock, void_t> = + true; // TRANSITION, GH-602 template - struct is_clock : bool_constant<_Is_clock<_Clock>> {}; + struct is_clock : bool_constant<_Is_clock_v<_Clock>> {}; template - inline constexpr bool is_clock_v = _Is_clock<_Clock>; + inline constexpr bool is_clock_v = _Is_clock_v<_Clock>; #endif // _HAS_CXX20 // CLASS TEMPLATE duration @@ -204,7 +211,8 @@ namespace chrono { using rep = typename _Duration::rep; using period = typename _Duration::period; - static_assert(_Is_duration_v<_Duration>, "duration must be an instance of std::duration"); + static_assert(_Is_duration_v<_Duration>, + "N4885 [time.point.general]/1 mandates Duration to be a specialization of chrono::duration."); constexpr time_point() = default; @@ -2120,8 +2128,11 @@ namespace chrono { } template - requires _Is_duration_v<_Duration> class hh_mm_ss { + class hh_mm_ss { public: + static_assert(_Is_duration_v<_Duration>, + "N4885 [time.hms.overview]/2 mandates Duration to be a specialization of chrono::duration."); + static constexpr unsigned int fractional_width = [] { auto _Num = _Duration::period::num; constexpr auto _Den = _Duration::period::den; @@ -2209,6 +2220,2957 @@ namespace chrono { return hours{_Ret}; } + + // [time.zone.info] + + // STRUCT sys_info + struct sys_info { + sys_seconds begin; + sys_seconds end; + seconds offset; + minutes save; + string abbrev; + }; + + // STRUCT local_info + struct local_info { + static constexpr int unique = 0; + static constexpr int nonexistent = 1; + static constexpr int ambiguous = 2; + + int result; + sys_info first; + sys_info second; + }; + + // CLASS nonexistent_local_time + class nonexistent_local_time : public runtime_error { + public: + template + nonexistent_local_time(const local_time<_Duration>&, const local_info&) + : runtime_error("TRANSITION: work in progress") {} + }; + + // CLASS ambiguous_local_time + class ambiguous_local_time : public runtime_error { + public: + template + ambiguous_local_time(const local_time<_Duration>&, const local_info&) + : runtime_error("TRANSITION: work in progress") {} + }; + + // [time.zone.timezone] + + // ENUM CLASS choose + enum class choose { earliest, latest }; + + // CLASS time_zone + class time_zone { + public: + explicit time_zone(string_view _Name_) : _Name(_Name_) {} + + time_zone(time_zone&&) = default; + time_zone& operator=(time_zone&&) = default; + + _NODISCARD string_view name() const noexcept { + return _Name; + } + + template + _NODISCARD sys_info get_info(const sys_time<_Duration>& _Sys) const { + return _Get_info(_Sys.time_since_epoch()); + } + + template + _NODISCARD local_info get_info(const local_time<_Duration>& _Local) const { + local_info _Info{}; + const auto _Time_since_ep = _Local.time_since_epoch(); + _Info.first = _Get_info(_Time_since_ep); + + const sys_seconds _Local_sys{_CHRONO duration_cast(_Time_since_ep)}; + const auto _Curr_sys = _Local_sys - _Info.first.offset; + if (_Info.first.begin != _Min_seconds && _Curr_sys < _Info.first.begin + days{1}) { + // get previous transition information + _Info.second = get_info(_Info.first.begin - seconds{1}); + + const auto _Transition = _Info.first.begin; + const auto _Prev_sys = _Local_sys - _Info.second.offset; + if (_Curr_sys >= _Transition) { + if (_Prev_sys < _Transition) { + _Info.result = local_info::ambiguous; + _STD swap(_Info.first, _Info.second); + } else { + _Info.result = local_info::unique; + _Info.second = {}; + } + } else { + if (_Prev_sys >= _Transition) { + _Info.result = local_info::nonexistent; + _STD swap(_Info.first, _Info.second); + } else { + _Info.result = local_info::unique; + _Info.first = _STD move(_Info.second); + _Info.second = {}; + } + } + } else if (_Info.first.end != _Max_seconds && _Curr_sys > _Info.first.end - days{1}) { + // get next transition information + _Info.second = get_info(_Info.first.end + seconds{1}); + + const auto _Transition = _Info.first.end; + const auto _Next_sys = _Local_sys - _Info.second.offset; + if (_Curr_sys < _Transition) { + if (_Next_sys >= _Transition) { + _Info.result = local_info::ambiguous; + } else { + _Info.result = local_info::unique; + _Info.second = {}; + } + } else { + if (_Next_sys < _Transition) { + _Info.result = local_info::nonexistent; + } else { + _Info.result = local_info::unique; + _Info.first = _STD move(_Info.second); + _Info.second = {}; + } + } + } else { + // local time is contained inside of first transition boundaries by at least 1 day + _Info.result = local_info::unique; + _Info.second = {}; + } + + return _Info; + } + + template + _NODISCARD sys_time> to_sys(const local_time<_Duration>& _Local) const { + const auto _Info = get_info(_Local); + if (_Info.result == local_info::nonexistent) { + _THROW(nonexistent_local_time(_Local, _Info)); + } else if (_Info.result == local_info::ambiguous) { + _THROW(ambiguous_local_time(_Local, _Info)); + } + + return sys_time>{_Local.time_since_epoch() - _Info.first.offset}; + } + + template + _NODISCARD sys_time> to_sys( + const local_time<_Duration>& _Local, const choose _Choose) const { + const auto _Info = get_info(_Local); + if (_Info.result == local_info::nonexistent) { + return _Info.first.end; + } + + const auto _Offset = (_Info.result == local_info::unique || _Choose == choose::earliest) + ? _Info.first.offset + : _Info.second.offset; + return sys_time>{_Local.time_since_epoch() - _Offset}; + } + + template + _NODISCARD local_time> to_local(const sys_time<_Duration>& _Sys) const { + const auto _Info = get_info(_Sys); + return local_time>{_Sys.time_since_epoch() + _Info.offset}; + } + + static constexpr sys_seconds _Min_seconds{sys_days{(year::min)() / January / 1}}; + static constexpr sys_seconds _Max_seconds{sys_seconds{sys_days{(year::max)() / December / 32}} - seconds{1}}; + + private: + template + _NODISCARD sys_info _Get_info(const _Duration& _Dur) const { + using _Internal_duration = duration<__std_tzdb_epoch_milli, milli>; + const auto _Internal_dur = _CHRONO duration_cast<_Internal_duration>(_Dur); + const unique_ptr<__std_tzdb_sys_info, _Tzdb_deleter<__std_tzdb_sys_info>> _Info{ + __std_tzdb_get_sys_info(_Name.c_str(), _Name.length(), _Internal_dur.count())}; + if (_Info == nullptr) { + _Xbad_alloc(); + } else if (_Info->_Err == __std_tzdb_error::_Win_error) { + _XGetLastError(); + } else if (_Info->_Err == __std_tzdb_error::_Icu_error) { + _Xruntime_error("Internal error loading IANA database information"); + } + + constexpr auto _Min_internal = + _CHRONO duration_cast<_Internal_duration>(_Min_seconds.time_since_epoch()).count(); + constexpr auto _Max_internal = + _CHRONO duration_cast<_Internal_duration>(_Max_seconds.time_since_epoch()).count(); + const auto _Begin = + _Info->_Begin <= _Min_internal + ? _Min_seconds + : sys_seconds{_CHRONO duration_cast(_Internal_duration{_Info->_Begin})}; + const auto _End = + _Info->_End >= _Max_internal + ? _Max_seconds + : sys_seconds{_CHRONO duration_cast(_Internal_duration{_Info->_End})}; + return {.begin = _Begin, + .end = _End, + .offset = _CHRONO duration_cast(_Internal_duration{_Info->_Offset}), + .save = _CHRONO duration_cast(_Internal_duration{_Info->_Save}), + .abbrev = _Info->_Abbrev}; + } + + string _Name; + }; + + _NODISCARD inline bool operator==(const time_zone& _Left, const time_zone& _Right) noexcept { + return _Left.name() == _Right.name(); + } + +#ifdef __cpp_lib_concepts + _NODISCARD inline strong_ordering operator<=>(const time_zone& _Left, const time_zone& _Right) noexcept { + return _Left.name() <=> _Right.name(); + } +#endif // __cpp_lib_concepts + + // [time.zone.leap] + + // CLASS leap_second + class leap_second { + public: + leap_second(const leap_second&) = default; + leap_second& operator=(const leap_second&) = default; + + constexpr leap_second( + const sys_seconds& _Date_, const bool _Is_positive_, const seconds& _Prev_elapsed) noexcept + : _Date{_Date_}, _Is_positive{_Is_positive_} { + _Elapsed_offset = _Prev_elapsed + value(); + } + + _NODISCARD constexpr sys_seconds date() const noexcept { + return _Date; + } + + _NODISCARD constexpr seconds value() const noexcept { + return _Is_positive ? seconds{1} : seconds{-1}; + } + + _NODISCARD constexpr bool _Positive() const noexcept { + return _Is_positive; + } + + _NODISCARD constexpr seconds _Elapsed() const noexcept { + return _Elapsed_offset; + } + + private: + sys_seconds _Date; + bool _Is_positive; + seconds _Elapsed_offset; + }; + + _NODISCARD constexpr bool operator==(const leap_second& _Left, const leap_second& _Right) noexcept { + return _Left.date() == _Right.date(); + } + template + _NODISCARD constexpr bool operator==(const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { + return _Left.date() == _Right; + } + + template + _NODISCARD constexpr bool operator<(const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { + return _Left.date() < _Right; + } + template + _NODISCARD constexpr bool operator<(const sys_time<_Duration>& _Left, const leap_second& _Right) noexcept { + return _Left < _Right.date(); + } + + template + _NODISCARD constexpr bool operator>(const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { + return _Right < _Left.date(); + } + template + _NODISCARD constexpr bool operator>(const sys_time<_Duration>& _Left, const leap_second& _Right) noexcept { + return _Right.date() < _Left; + } + + template + _NODISCARD constexpr bool operator<=(const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { + return !(_Right < _Left.date()); + } + template + _NODISCARD constexpr bool operator<=(const sys_time<_Duration>& _Left, const leap_second& _Right) noexcept { + return !(_Right.date() < _Left); + } + + template + _NODISCARD constexpr bool operator>=(const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { + return !(_Left.date() < _Right); + } + template + _NODISCARD constexpr bool operator>=(const sys_time<_Duration>& _Left, const leap_second& _Right) noexcept { + return !(_Left < _Right.date()); + } + +#ifdef __cpp_lib_concepts + // clang-format off + template + requires three_way_comparable_with> + _NODISCARD constexpr auto operator<=>( + const leap_second& _Left, const sys_time<_Duration>& _Right) noexcept { + // clang-format on + return _Left.date() <=> _Right; + } + _NODISCARD constexpr strong_ordering operator<=>(const leap_second& _Left, const leap_second& _Right) noexcept { + return _Left.date() <=> _Right.date(); + } +#endif // __cpp_lib_concepts + + // [time.zone.link] + + // CLASS time_zone_link + class time_zone_link { + public: + explicit time_zone_link(string_view _Name_, string_view _Target_) : _Name(_Name_), _Target(_Target_) {} + + time_zone_link(time_zone_link&&) = default; + time_zone_link& operator=(time_zone_link&&) = default; + + _NODISCARD string_view name() const noexcept { + return _Name; + } + + _NODISCARD string_view target() const noexcept { + return _Target; + } + + private: + string _Name; + string _Target; + }; + + _NODISCARD inline bool operator==(const time_zone_link& _Left, const time_zone_link& _Right) noexcept { + return _Left.name() == _Right.name(); + } + +#ifdef __cpp_lib_concepts + _NODISCARD inline strong_ordering operator<=>(const time_zone_link& _Left, const time_zone_link& _Right) noexcept { + return _Left.name() <=> _Right.name(); + } +#endif // __cpp_lib_concepts + + // [time.zone.db] + + _NODISCARD inline string _Tzdb_generate_current_zone() { + unique_ptr<__std_tzdb_current_zone_info, _Tzdb_deleter<__std_tzdb_current_zone_info>> _Info{ + __std_tzdb_get_current_zone()}; + if (_Info == nullptr) { + _Xbad_alloc(); + } else if (_Info->_Err == __std_tzdb_error::_Win_error) { + _XGetLastError(); + } else if (_Info->_Err == __std_tzdb_error::_Icu_error) { + _Xruntime_error("Internal error loading IANA database information"); + } + + return {_Info->_Tz_name}; + } + + template + _NODISCARD const _Ty* _Locate_zone_impl(const vector<_Ty>& _Vec, string_view _Name) { + const auto _Result = _STD find_if(_Vec.begin(), _Vec.end(), [&](auto& _Tz) { return _Tz.name() == _Name; }); + return _Result == _Vec.end() ? nullptr : &*_Result; + } + + // STRUCT tzdb + struct tzdb { + string version; + vector zones; + vector links; + vector leap_seconds; + bool _All_ls_positive; + + _NODISCARD const time_zone* locate_zone(string_view _Tz_name) const { + auto _Tz = _Locate_zone_impl(zones, _Tz_name); + if (_Tz != nullptr) { + return _Tz; + } + + const auto _Link = _Locate_zone_impl(links, _Tz_name); + if (_Link != nullptr) { + _Tz = _Locate_zone_impl(zones, _Link->target()); + if (_Tz != nullptr) { + return _Tz; + } + } + + _Xruntime_error("unable to locate time_zone with given name"); + } + + _NODISCARD const time_zone* current_zone() const { + return locate_zone(_Tzdb_generate_current_zone()); + } + }; + + _NODISCARD inline tuple _Tzdb_generate_time_zones() { + unique_ptr<__std_tzdb_time_zones_info, _Tzdb_deleter<__std_tzdb_time_zones_info>> _Info{ + __std_tzdb_get_time_zones()}; + if (_Info == nullptr) { + _Xbad_alloc(); + } else if (_Info->_Err == __std_tzdb_error::_Win_error) { + _XGetLastError(); + } else if (_Info->_Err == __std_tzdb_error::_Icu_error) { + _Xruntime_error("Internal error loading IANA database information"); + } + + decltype(tzdb::zones) _Time_zones; + decltype(tzdb::links) _Time_zone_links; + for (size_t _Idx = 0; _Idx < _Info->_Num_time_zones; ++_Idx) { + const string_view _Name{_Info->_Names[_Idx]}; + if (_Info->_Links[_Idx] == nullptr) { + _Time_zones.emplace_back(_Name); + } else { + const string_view _Target{_Info->_Links[_Idx]}; + _Time_zone_links.emplace_back(_Name, _Target); + } + } + + return {_Info->_Version, _STD move(_Time_zones), _STD move(_Time_zone_links)}; + } + + _NODISCARD inline pair _Tzdb_generate_leap_seconds(const size_t _Current_size) { + // Returns empty vector if no new leap seconds are found. + static constexpr leap_second _Known_leap_seconds[]{ + {sys_seconds{seconds{78796800}}, true, seconds{0}}, + {sys_seconds{seconds{94694400}}, true, seconds{1}}, + {sys_seconds{seconds{126230400}}, true, seconds{2}}, + {sys_seconds{seconds{157766400}}, true, seconds{3}}, + {sys_seconds{seconds{189302400}}, true, seconds{4}}, + {sys_seconds{seconds{220924800}}, true, seconds{5}}, + {sys_seconds{seconds{252460800}}, true, seconds{6}}, + {sys_seconds{seconds{283996800}}, true, seconds{7}}, + {sys_seconds{seconds{315532800}}, true, seconds{8}}, + {sys_seconds{seconds{362793600}}, true, seconds{9}}, + {sys_seconds{seconds{394329600}}, true, seconds{10}}, + {sys_seconds{seconds{425865600}}, true, seconds{11}}, + {sys_seconds{seconds{489024000}}, true, seconds{12}}, + {sys_seconds{seconds{567993600}}, true, seconds{13}}, + {sys_seconds{seconds{631152000}}, true, seconds{14}}, + {sys_seconds{seconds{662688000}}, true, seconds{15}}, + {sys_seconds{seconds{709948800}}, true, seconds{16}}, + {sys_seconds{seconds{741484800}}, true, seconds{17}}, + {sys_seconds{seconds{773020800}}, true, seconds{18}}, + {sys_seconds{seconds{820454400}}, true, seconds{19}}, + {sys_seconds{seconds{867715200}}, true, seconds{20}}, + {sys_seconds{seconds{915148800}}, true, seconds{21}}, + {sys_seconds{seconds{1136073600}}, true, seconds{22}}, + {sys_seconds{seconds{1230768000}}, true, seconds{23}}, + {sys_seconds{seconds{1341100800}}, true, seconds{24}}, + {sys_seconds{seconds{1435708800}}, true, seconds{25}}, + {sys_seconds{seconds{1483228800}}, true, seconds{26}}, + }; + + // __std_tzdb_get_leap_seconds gets leap second (LS) data from the registry, but only if it contains more + // LSs than we already know about. The registry only contains LSs after 2018, so we need to tell it how many of + // *those* we already know about. The *total* number of LSs known at this point is a combination of what the + // caller knows (_Current_size, 0 on first call) and the _Known_leap_seconds entries. + constexpr size_t _Pre_2018_count = 27; + const size_t _Known_post_2018_ls_size = + (_STD max)(_Current_size, _STD size(_Known_leap_seconds)) - _Pre_2018_count; + + size_t _Reg_post_2018_ls_size; // number of post-2018 LSs found in the registry + unique_ptr<__std_tzdb_leap_info[], _Tzdb_deleter<__std_tzdb_leap_info[]>> _Reg_ls_data{ + __std_tzdb_get_leap_seconds(_Known_post_2018_ls_size, &_Reg_post_2018_ls_size)}; + + if (_Reg_post_2018_ls_size > _Known_post_2018_ls_size && !_Reg_ls_data) { + _Xbad_alloc(); // registry has new data, but failed to allocate storage + } else if (_Reg_post_2018_ls_size == 0 && _Reg_ls_data) { + _XGetLastError(); // allocated storage for registry data, but failed to read + } + + const size_t _New_size = _Pre_2018_count + _Reg_post_2018_ls_size; // total size with registry data + decltype(tzdb::leap_seconds) _Leap_sec_info; + bool _All_ls_positive = true; + + if (_New_size > _Current_size) { + _Leap_sec_info.reserve(_New_size); + _Leap_sec_info.assign(_STD cbegin(_Known_leap_seconds), _STD cend(_Known_leap_seconds)); + + for (size_t _Idx = 0; _Idx < _Reg_post_2018_ls_size; ++_Idx) { + // Leap seconds occur at _Ls._Hour:59:59. We store the next second after, so we need to add an entire + // hour. + const auto& _Ls = _Reg_ls_data[_Idx]; + const auto _Date = + static_cast(year_month_day{year{_Ls._Year}, month{_Ls._Month}, day{_Ls._Day}}) + + hours{_Ls._Hour + 1}; + _Leap_sec_info.emplace_back(_Date, !_Ls._Negative, _Leap_sec_info.back()._Elapsed()); + _All_ls_positive = _All_ls_positive && !_Ls._Negative; + } + } + + return {_STD move(_Leap_sec_info), _All_ls_positive}; + } + + _NODISCARD inline string _Tzdb_update_version(const string_view _Version, const size_t _Num_leap_seconds) { + string _Icu_version{_Version.substr(0, _Version.find_last_of('.'))}; + return _STD move(_Icu_version) + "." + _STD to_string(_Num_leap_seconds); + } + + // CLASS tzdb_list + class tzdb_list { + private: + using _ListType = forward_list>; + + public: + using const_iterator = _ListType::const_iterator; + + tzdb_list(const tzdb_list&) = delete; + tzdb_list& operator=(const tzdb_list&) = delete; + + tzdb_list() { + auto [_Icu_version, _Zones, _Links] = _Tzdb_generate_time_zones(); + auto [_Leap_sec, _All_ls_positive] = _Tzdb_generate_leap_seconds(0); + auto _Version = _Icu_version + "." + _STD to_string(_Leap_sec.size()); + _Tzdb_list.emplace_front(tzdb{ + _STD move(_Version), _STD move(_Zones), _STD move(_Links), _STD move(_Leap_sec), _All_ls_positive}); + } + + _NODISCARD const tzdb& front() const noexcept { + _Shared_lock _Lk(_Tzdb_mutex); + return _Tzdb_list.front(); + } + + const_iterator erase_after(const_iterator _Where) noexcept /* strengthened */ { + _Unique_lock _Lk(_Tzdb_mutex); + return _Tzdb_list.erase_after(_Where); + } + + _NODISCARD const_iterator begin() const noexcept { + _Shared_lock _Lk(_Tzdb_mutex); + return _Tzdb_list.begin(); + } + + _NODISCARD const_iterator end() const noexcept { + return _Tzdb_list.end(); // no lock necessary for forward_list::end() + } + + _NODISCARD const_iterator cbegin() const noexcept { + _Shared_lock _Lk(_Tzdb_mutex); + return _Tzdb_list.cbegin(); + } + + _NODISCARD const_iterator cend() const noexcept { + return _Tzdb_list.cend(); // no lock necessary for forward_list::cend() + } + + template + void _Emplace_front(_ArgsTy&&... _Args) { + _Unique_lock _Lk(_Tzdb_mutex); + _Tzdb_list.emplace_front(_STD forward<_ArgsTy>(_Args)...); + } + + const tzdb& _Reload() { + _Unique_lock _Lk(_Tzdb_mutex); + auto [_Leap_sec, _All_ls_positive] = _Tzdb_generate_leap_seconds(_Tzdb_list.front().leap_seconds.size()); + if (!_Leap_sec.empty()) { + const auto& _Tzdb = _Tzdb_list.front(); + vector _Zones; + _STD transform(_Tzdb.zones.begin(), _Tzdb.zones.end(), _STD back_inserter(_Zones), + [](const auto& _Tz) { return time_zone{_Tz.name()}; }); + vector _Links; + _STD transform( + _Tzdb.links.begin(), _Tzdb.links.end(), _STD back_inserter(_Links), [](const auto& _Link) { + return time_zone_link{_Link.name(), _Link.target()}; + }); + auto _Version = _Tzdb_update_version(_Tzdb.version, _Leap_sec.size()); + _Tzdb_list.emplace_front(tzdb{ + _STD move(_Version), _STD move(_Zones), _STD move(_Links), _STD move(_Leap_sec), _All_ls_positive}); + } + return _Tzdb_list.front(); + } + + private: + _ListType _Tzdb_list; + mutable _Smtx_t _Tzdb_mutex = {}; + + struct _NODISCARD _Shared_lock { + explicit _Shared_lock(_Smtx_t& _Mtx_) : _Mtx{&_Mtx_} { + _Smtx_lock_shared(_Mtx); + } + + _Shared_lock(const _Shared_lock&) = delete; + _Shared_lock& operator=(const _Shared_lock&) = delete; + + ~_Shared_lock() { + _Smtx_unlock_shared(_Mtx); + } + + _Smtx_t* _Mtx; + }; + + struct _NODISCARD _Unique_lock { + explicit _Unique_lock(_Smtx_t& _Mtx_) : _Mtx{&_Mtx_} { + _Smtx_lock_exclusive(_Mtx); + } + + _Unique_lock(const _Unique_lock&) = delete; + _Unique_lock& operator=(const _Unique_lock&) = delete; + + ~_Unique_lock() { + _Smtx_unlock_exclusive(_Mtx); + } + + _Smtx_t* _Mtx; + }; + }; + + inline atomic _Global_tzdb_list; + + // FUNCTION get_tzdb_list + _NODISCARD inline tzdb_list& get_tzdb_list() { + auto* _Tzdb_ptr = _Global_tzdb_list.load(); + if (_Tzdb_ptr != nullptr) { + return *_Tzdb_ptr; + } + + auto _My_tzdb = static_cast(__std_calloc_crt(1, sizeof(tzdb_list))); + if (_My_tzdb == nullptr) { + _Xruntime_error("bad allocation"); // not bad_alloc, see N4878 [time.zone.db.access]/4 + } + + _TRY_BEGIN + _STD construct_at(_My_tzdb); + _CATCH(const runtime_error&) + __std_free_crt(_My_tzdb); + _RERAISE; + _CATCH(const exception& _Except) +#if _HAS_EXCEPTIONS + __std_free_crt(_My_tzdb); + _Xruntime_error(_Except.what()); +#endif // _HAS_EXCEPTIONS + _CATCH_END + + if (_Global_tzdb_list.compare_exchange_strong(_Tzdb_ptr, _My_tzdb)) { + _Tzdb_ptr = _My_tzdb; + } else { + _STD destroy_at(_My_tzdb); + __std_free_crt(_My_tzdb); + } + + return *_Tzdb_ptr; + } + + // FUNCTION get_tzdb + _NODISCARD inline const tzdb& get_tzdb() { + return _CHRONO get_tzdb_list().front(); + } + + // FUNCTION locate_zone + _NODISCARD inline const time_zone* locate_zone(string_view _Tz_name) { + return _CHRONO get_tzdb().locate_zone(_Tz_name); + } + + // FUNCTION current_zone + _NODISCARD inline const time_zone* current_zone() { + return _CHRONO get_tzdb().current_zone(); + } + + // FUNCTION reload_tzdb + inline const tzdb& reload_tzdb() { + _TRY_BEGIN + return _CHRONO get_tzdb_list()._Reload(); + _CATCH(const runtime_error&) + _RERAISE; + _CATCH(const exception& _Except) +#if _HAS_EXCEPTIONS + _Xruntime_error(_Except.what()); +#endif // _HAS_EXCEPTIONS + _CATCH_END + } + + // FUNCTION remote_version + _NODISCARD inline string remote_version() { + const auto& _Tzdb = _CHRONO get_tzdb(); + const auto& _Version = _Tzdb.version; + const auto [_Leap_sec, _Ignored] = _Tzdb_generate_leap_seconds(_Tzdb.leap_seconds.size()); + return _Leap_sec.empty() ? _Version : _Tzdb_update_version(_Version, _Leap_sec.size()); + } + + // [time.zone.zonedtraits] + + // STRUCT TEMPLATE zoned_traits + template + struct zoned_traits {}; + + // STRUCT zoned_traits + template <> + struct zoned_traits { + _NODISCARD static const time_zone* default_zone() { + return _CHRONO get_tzdb().locate_zone("UTC"); + } + + _NODISCARD static const time_zone* locate_zone(string_view _Name) { + return _CHRONO get_tzdb().locate_zone(_Name); + } + }; + + // [time.zone.zonedtime] + + // CLASS TEMPLATE zoned_time + template + class zoned_time { + private: + static_assert(_Is_duration_v<_Duration>, + "N4885 [time.zone.zonedtime.overview]/2 mandates Duration to be a specialization of chrono::duration."); + + using _Traits = zoned_traits<_TimeZonePtr>; + + public: + using duration = common_type_t<_Duration, seconds>; + + template > + zoned_time() : _Zone{_Traits::default_zone()} {} + zoned_time(const zoned_time&) = default; + zoned_time& operator=(const zoned_time&) = default; + + template > + zoned_time(const sys_time<_Duration>& _Sys) : _Zone{_Traits::default_zone()}, _Tp{_Sys} {} + + explicit zoned_time(_TimeZonePtr _Tz) noexcept /* strengthened */ : _Zone{_STD move(_Tz)} {} + + // clang-format off + template ::value, + int> = 0> + // clang-format on + explicit zoned_time(string_view _Name) : _Zone{_Traits::locate_zone(_Name)} {} + + template , sys_time<_Duration>>, int> = 0> + zoned_time(const zoned_time<_Duration2, _TimeZonePtr>& _Zt) noexcept /* strengthened */ + : _Zone{_Zt.get_time_zone()}, _Tp{_Zt.get_sys_time()} {} + + zoned_time(_TimeZonePtr _Tz, const sys_time<_Duration>& _Sys) : _Zone{_STD move(_Tz)}, _Tp{_Sys} {} + + // clang-format off + template &>::value, + int> = 0> + // clang-format on + zoned_time(string_view _Name, type_identity_t&> _Sys) + : zoned_time{_Traits::locate_zone(_Name), _Sys} {} + + template ()->to_sys(local_time<_Duration>{})), sys_time>, + int> = 0> + zoned_time(_TimeZonePtr _Tz, const local_time<_Duration>& _Local) + : _Zone{_STD move(_Tz)}, _Tp{_Zone->to_sys(_Local)} {} + + // clang-format off + template &>::value, + int> = 0> + // clang-format on + zoned_time(string_view _Name, type_identity_t>& _Local) + : zoned_time{_Traits::locate_zone(_Name), _Local} {} + + template ()->to_sys(local_time<_Duration>{}, choose::earliest)), + sys_time>, + int> = 0> + zoned_time(_TimeZonePtr _Tz, const local_time<_Duration>& _Local, choose _Choose) + : _Zone{_STD move(_Tz)}, _Tp{_Zone->to_sys(_Local, _Choose)} {} + + // clang-format off + template &, choose>::value, + int> = 0> + // clang-format on + zoned_time(string_view _Name, type_identity_t&> _Local, choose _Choose) + : zoned_time{_Traits::locate_zone(_Name), _Local, _Choose} {} + + template , sys_time<_Duration>>, int> = 0> + zoned_time(_TimeZonePtr _Tz, const zoned_time<_Duration2, _TimeZonePtr2>& _Zt) noexcept /* strengthened */ + : _Zone{_STD move(_Tz)}, _Tp{_Zt.get_sys_time()} {} + + template , sys_time<_Duration>>, int> = 0> + zoned_time( + _TimeZonePtr _Tz, const zoned_time<_Duration2, _TimeZonePtr2>& _Zt, choose) noexcept /* strengthened */ + : zoned_time{_Tz, _Zt} {} + + // clang-format off + template &>::value, + int> = 0> + // clang-format on + zoned_time(string_view _Name, const zoned_time<_Duration2, _TimeZonePtr2>& _Zt) + : zoned_time{_Traits::locate_zone(_Name), _Zt} {} + + // clang-format off + template &, choose>::value, + int> = 0> + // clang-format on + zoned_time(string_view _Name, const zoned_time<_Duration2, _TimeZonePtr2>& _Zt, choose _Choose) + : zoned_time{_Traits::locate_zone(_Name), _Zt, _Choose} {} + + zoned_time& operator=(const sys_time<_Duration>& _Sys) noexcept /* strengthened */ { + _Tp = _Sys; + return *this; + } + + zoned_time& operator=(const local_time<_Duration>& _Local) { + _Tp = _Zone->to_sys(_Local); + return *this; + } + + operator sys_time() const noexcept /* strengthened */ { + return get_sys_time(); + } + + explicit operator local_time() const { + return get_local_time(); + } + + _NODISCARD _TimeZonePtr get_time_zone() const noexcept /* strengthened */ { + return _Zone; + } + + _NODISCARD local_time get_local_time() const { + return _Zone->to_local(_Tp); + } + + _NODISCARD sys_time get_sys_time() const noexcept /* strengthened */ { + return _Tp; + } + + _NODISCARD sys_info get_info() const { + return _Zone->get_info(_Tp); + } + + private: + _TimeZonePtr _Zone; + sys_time _Tp{}; + }; + + zoned_time()->zoned_time; + + template + zoned_time(sys_time<_Duration>) -> zoned_time>; + + template + using _Time_zone_representation = conditional_t, const time_zone*, + remove_cvref_t<_TimeZonePtrOrName>>; + + template + zoned_time(_TimeZonePtrOrName&&) -> zoned_time>; + + template + zoned_time(_TimeZonePtrOrName&&, sys_time<_Duration>) + -> zoned_time, _Time_zone_representation<_TimeZonePtrOrName>>; + + template + zoned_time(_TimeZonePtrOrName&&, local_time<_Duration>, choose = choose::earliest) + -> zoned_time, _Time_zone_representation<_TimeZonePtrOrName>>; + + template + zoned_time(_TimeZonePtrOrName&&, zoned_time<_Duration, _TimeZonePtr2>, choose = choose::earliest) + -> zoned_time, _Time_zone_representation<_TimeZonePtrOrName>>; + + using zoned_seconds = zoned_time; + + template + _NODISCARD bool operator==( + const zoned_time<_Duration1, _TimeZonePtr>& _Left, const zoned_time<_Duration2, _TimeZonePtr>& _Right) { + return _Left.get_time_zone() == _Right.get_time_zone() && _Left.get_sys_time() == _Right.get_sys_time(); + } + + // [time.clock.utc] + + class utc_clock; + template + using utc_time = time_point; + using utc_seconds = utc_time; + + // STRUCT leap_second_info + struct leap_second_info { + bool is_leap_second; + seconds elapsed; + }; + + // FUNCTION TEMPLATE get_leap_second_info + template + _NODISCARD leap_second_info get_leap_second_info(const utc_time<_Duration>& _Time) { + const utc_seconds _Time_floor = _CHRONO floor(_Time); + const auto& _Tzdb = _CHRONO get_tzdb(); + const auto& _Ls_vector = _Tzdb.leap_seconds; + + // Find first leap second after _Time. + vector::const_iterator _It; + if (_Tzdb._All_ls_positive) { + // Where "target_ls" is the next leap second at or after _Time, _It either points to: + // (1) The 2nd leap second after _Time if _Time_floor is in the range [target_ls - _Elapsed() - 1, + // target_ls), or + // (2) The leap second just after _Time otherwise. + // Note that we can always use prev(_It) to determine whether _Time is *during* a leap second insertion, + // since that falls under case (2) above. However, when we fall under case (1), we need to execute an + // additional decrement to get the correct elapsed offset. For example, if leap seconds are inserted at + // seconds {100, 200, 300, 400}, we have: + // + // UTC sys *_It + // 99 99 100 + // 100 X 200 + // 101 100 200 + // 102 101 200 + // ... + // 199 198 200 + // 200 199 300^ + // 201 X 300 + // 202 200 300 + // ... + // 299 297 300 + // 300 298 400^ + // 301 299 400^ + // 302 X 400 + // 303 300 400 + // + // ^_It points to 2nd leap second + + _It = _STD upper_bound(_Ls_vector.begin(), _Ls_vector.end(), sys_seconds{_Time_floor.time_since_epoch()}); + } else { + seconds _Prev_elapsed{0}; + for (_It = _Ls_vector.begin(); _It != _Ls_vector.end(); ++_It) { + // UTC time when leap second insertion begins. In all cases, _It->date() + _It->_Elapsed() is the *end* + // of the insertion. For a negative leap that's also the beginning, but for a positive one, insertion + // begins 1 second earlier. + const utc_seconds _This_ls_begin{ + _It->date().time_since_epoch() + (_It->_Positive() ? _Prev_elapsed : _It->_Elapsed())}; + if (_This_ls_begin > _Time_floor) { + break; + } + _Prev_elapsed = _It->_Elapsed(); + } + } + + if (_It == _Ls_vector.begin()) { + return {false, seconds{0}}; + } else { + // Convert to the last leap second before or equal to _Time. + const auto& _Last_leap = *--_It; + const utc_seconds _Utc_leap_second{_Last_leap.date().time_since_epoch() + _It->_Elapsed() - seconds{1}}; +#ifdef __cpp_lib_concepts + const auto _Leap_cmp = _Utc_leap_second <=> _Time_floor; +#else // ^^^ __cpp_lib_concepts / TRANSITION, GH-395 workaround vvv + const auto _Leap_cmp = _Utc_leap_second > _Time_floor ? strong_ordering::greater + : _Utc_leap_second == _Time_floor ? strong_ordering::equal + : strong_ordering::less; +#endif // ^^^ workaround + if (_Tzdb._All_ls_positive && _STD is_gt(_Leap_cmp)) { // Case (1) + --_It; + } + return {_Last_leap._Positive() && _STD is_eq(_Leap_cmp), _It->_Elapsed()}; + } + } + + // CLASS utc_clock + class utc_clock { + public: + using rep = system_clock::rep; + using period = system_clock::period; + using duration = _CHRONO duration; + using time_point = _CHRONO time_point; + static constexpr bool is_steady = system_clock::is_steady; + + _NODISCARD static time_point now() { + return from_sys(system_clock::now()); + } + + template + _NODISCARD static sys_time> to_sys(const utc_time<_Duration>& _Utc_time) { + using _CommonType = common_type_t<_Duration, seconds>; + const auto _Lsi{_CHRONO get_leap_second_info(_Utc_time)}; + _CommonType _Ticks; + if (_Lsi.is_leap_second) { + const auto _Leap_sec_minus_one = _CHRONO floor(_Utc_time.time_since_epoch()) - _Lsi.elapsed; + if constexpr (is_integral_v) { + constexpr auto _Delta{seconds{1} - _CommonType{1}}; + _Ticks = _Leap_sec_minus_one + _Delta; + } else { + const auto _Leap_sec_begin = _CHRONO ceil<_CommonType>(_Leap_sec_minus_one + seconds{1}); + _Ticks = _CommonType{_STD nextafter(_Leap_sec_begin.count(), typename _CommonType::rep{0})}; + } + } else { + _Ticks = _Utc_time.time_since_epoch() - _Lsi.elapsed; + } + return sys_time<_CommonType>{_Ticks}; + } + + template + _NODISCARD static utc_time> from_sys(const sys_time<_Duration>& _Sys_time) { + const auto& _Tzdb = _CHRONO get_tzdb(); + const auto& _Ls_vector = _Tzdb.leap_seconds; + auto _It = _STD upper_bound(_Ls_vector.begin(), _Ls_vector.end(), _CHRONO floor(_Sys_time)); + const auto _Offset = _It == _Ls_vector.begin() ? seconds{0} : (--_It)->_Elapsed(); + return utc_time>{_Sys_time.time_since_epoch() + _Offset}; + } + }; + + // [time.clock.tai] + + class tai_clock; + + template + using tai_time = time_point; + using tai_seconds = tai_time; + + // CLASS tai_clock + class tai_clock { + public: + using rep = system_clock::rep; + using period = system_clock::period; + using duration = _CHRONO duration; + using time_point = _CHRONO time_point; + static constexpr bool is_steady = system_clock::is_steady; + + static constexpr seconds _Tai_epoch_adjust{378691210}; + + _NODISCARD static time_point now() { + return from_utc(utc_clock::now()); + } + + template + _NODISCARD static utc_time> to_utc( + const tai_time<_Duration>& _Time) noexcept { + return utc_time>{_Time.time_since_epoch()} - _Tai_epoch_adjust; + } + + template + _NODISCARD static tai_time> from_utc( + const utc_time<_Duration>& _Time) noexcept { + return tai_time>{_Time.time_since_epoch()} + _Tai_epoch_adjust; + } + }; + + // [time.clock.gps] + + class gps_clock; + + template + using gps_time = time_point; + using gps_seconds = gps_time; + + // CLASS gps_clock + class gps_clock { + public: + using rep = system_clock::rep; + using period = system_clock::period; + using duration = _CHRONO duration; + using time_point = _CHRONO time_point; + static constexpr bool is_steady = system_clock::is_steady; + + static constexpr seconds _Gps_epoch_adjust{-315964809}; + + _NODISCARD static time_point now() { + return from_utc(utc_clock::now()); + } + + template + _NODISCARD static utc_time> to_utc( + const gps_time<_Duration>& _Time) noexcept { + return utc_time>{_Time.time_since_epoch()} - _Gps_epoch_adjust; + } + + template + _NODISCARD static gps_time> from_utc( + const utc_time<_Duration>& _Time) noexcept { + return gps_time>{_Time.time_since_epoch()} + _Gps_epoch_adjust; + } + }; +#endif // ^^^ _HAS_CXX20 +} // namespace chrono + +// [time.clock.file] + +#if _HAS_CXX20 +namespace filesystem { + struct _File_time_clock; +} // namespace filesystem + +namespace chrono { + // ALIAS file_clock + using file_clock = filesystem::_File_time_clock; + + template + using file_time = time_point; +} // namespace chrono +#endif // ^^^ _HAS_CXX20 + +#if _HAS_CXX17 +namespace filesystem { + inline constexpr long long __std_fs_file_time_epoch_adjustment = 0x19DB1DED53E8000LL; // TRANSITION, ABI + + struct _File_time_clock { // Implementation of trivial-clock + using rep = long long; + using period = chrono::system_clock::period; + using duration = chrono::duration; + using time_point = chrono::time_point<_File_time_clock>; + static constexpr bool is_steady = false; + + _NODISCARD static time_point now() noexcept { // get current time; undo epoch adjustment + return time_point(duration(_Xtime_get_ticks() + __std_fs_file_time_epoch_adjustment)); // TRANSITION, ABI + } + +#if _HAS_CXX20 + // Assumes that FILETIME counts leap seconds only after the first 27 (i.e., after 1 January 2017), even though + // systems can opt out of this behavior. + static constexpr chrono::seconds _Skipped_filetime_leap_seconds{27}; + static constexpr chrono::sys_days _Cutoff{ + chrono::year_month_day{chrono::year{2017}, chrono::January, chrono::day{1}}}; + + template + _NODISCARD static chrono::utc_time> to_utc( + const chrono::file_time<_Duration>& _File_time) { + using namespace chrono; + using _CommonType = common_type_t<_Duration, seconds>; + const auto _Ticks = _File_time.time_since_epoch() + - _CHRONO duration_cast(duration{__std_fs_file_time_epoch_adjustment}); + + if (_Ticks < _Cutoff.time_since_epoch()) { + return utc_clock::from_sys(sys_time<_CommonType>{_Ticks}); + } else { + return utc_time<_CommonType>{_Ticks + _Skipped_filetime_leap_seconds}; + } + } + + template + _NODISCARD static chrono::file_time> from_utc( + const chrono::utc_time<_Duration>& _Utc_time) { + using namespace chrono; + file_time> _File_time{ + _CHRONO duration_cast(duration{__std_fs_file_time_epoch_adjustment})}; + + if (_Utc_time < utc_seconds{_Cutoff.time_since_epoch()} + _Skipped_filetime_leap_seconds) { + _File_time += utc_clock::to_sys(_Utc_time).time_since_epoch(); + } else { + _File_time += _Utc_time.time_since_epoch() - _Skipped_filetime_leap_seconds; + } + + return _File_time; + } +#endif // ^^^ _HAS_CXX20 + }; +} // namespace filesystem +#endif // ^^^ _HAS_CXX17 + +namespace chrono { +#if _HAS_CXX20 + // [time.clock.conv] + + // STRUCT TEMPLATE clock_time_conversion + template + struct clock_time_conversion {}; + + // [time.clock.cast.id] + + template + struct clock_time_conversion<_Clock, _Clock> { + template + _NODISCARD time_point<_Clock, _Duration> operator()(const time_point<_Clock, _Duration>& _Time) const + noexcept(is_arithmetic_v) /* strengthened */ { + return _Time; + } + }; + + template <> + struct clock_time_conversion { + template + _NODISCARD sys_time<_Duration> operator()(const sys_time<_Duration>& _Time) const + noexcept(is_arithmetic_v) /* strengthened */ { + return _Time; + } + }; + + template <> + struct clock_time_conversion { + template + _NODISCARD utc_time<_Duration> operator()(const utc_time<_Duration>& _Time) const + noexcept(is_arithmetic_v) /* strengthened */ { + return _Time; + } + }; + + // [time.clock.cast.sys.utc] + + template <> + struct clock_time_conversion { + template + _NODISCARD utc_time> operator()(const sys_time<_Duration>& _Sys_time) const { + return utc_clock::from_sys(_Sys_time); + } + }; + + template <> + struct clock_time_conversion { + template + _NODISCARD sys_time> operator()(const utc_time<_Duration>& _Utc_time) const { + return utc_clock::to_sys(_Utc_time); + } + }; + + // [time.clock.cast.sys] + + template + inline constexpr bool _Is_time_point_for_clock = false; + + template + inline constexpr bool _Is_time_point_for_clock, _Clock> = true; + + template + struct clock_time_conversion { + template &>()))>> + _NODISCARD auto operator()(const time_point<_SourceClock, _Duration>& _Time) const + noexcept(noexcept(_SourceClock::to_sys(_Time))) /* strengthened */ { + static_assert(_Is_time_point_for_clock, + "N4885 [time.clock.cast.sys]/2: Mandates: SourceClock::to_sys(t) returns a sys_time"); + return _SourceClock::to_sys(_Time); + } + }; + + template + struct clock_time_conversion<_DestClock, system_clock> { + template &>()))>> + _NODISCARD auto operator()(const sys_time<_Duration>& _Time) const + noexcept(noexcept(_DestClock::from_sys(_Time))) /* strengthened */ { + static_assert(_Is_time_point_for_clock, + "N4885 [time.clock.cast.sys]/5: Mandates: DestClock::from_sys(t) returns a " + "time_point"); + return _DestClock::from_sys(_Time); + } + }; + + // [time.clock.cast.utc] + + template + struct clock_time_conversion { + template &>()))>> + _NODISCARD auto operator()(const time_point<_SourceClock, _Duration>& _Time) const + noexcept(noexcept(_SourceClock::to_utc(_Time))) /* strengthened */ { + static_assert(_Is_time_point_for_clock, + "N4885 [time.clock.cast.utc]/2: Mandates: SourceClock::to_utc(t) returns a utc_time"); + return _SourceClock::to_utc(_Time); + } + }; + + template + struct clock_time_conversion<_DestClock, utc_clock> { + template &>()))>> + _NODISCARD auto operator()(const utc_time<_Duration>& _Time) const + noexcept(noexcept(_DestClock::from_utc(_Time))) /* strengthened */ { + static_assert(_Is_time_point_for_clock, + "N4885 [time.clock.cast.utc]/5: Mandates: DestClock::from_utc(t) returns a " + "time_point"); + return _DestClock::from_utc(_Time); + } + }; + + // [time.clock.cast.fn] + + enum class _Clock_cast_strategy { + _Direct, + _Via_sys, + _Via_utc, + _Via_utc_from_sys, + _Via_sys_from_utc, + _Two_step_ambiguous, + _Three_step_ambiguous, + _None, + }; + + template + inline constexpr bool _Has_two_step_conversion = false; + + template + inline constexpr bool + _Has_two_step_conversion<_Conv1, _Conv2, _Tp, void_t())))>> = true; + + template + inline constexpr bool _Has_three_step_conversion = false; + + template + inline constexpr bool _Has_three_step_conversion<_Conv1, _Conv2, _Conv3, _Tp, + void_t()))))>> = true; + + template + _NODISCARD _CONSTEVAL _Clock_cast_strategy _Choose_clock_cast() noexcept { + using _Tp = const time_point<_SourceClock, _Duration>&; + + if constexpr (is_invocable_v, _Tp>) { + return _Clock_cast_strategy::_Direct; + } else { + constexpr bool _Has_sys = _Has_two_step_conversion< // + clock_time_conversion<_DestClock, system_clock>, // + clock_time_conversion, _Tp>; + + constexpr bool _Has_utc = _Has_two_step_conversion< // + clock_time_conversion<_DestClock, utc_clock>, // + clock_time_conversion, _Tp>; + + if constexpr (_Has_sys && _Has_utc) { + return _Clock_cast_strategy::_Two_step_ambiguous; + } else if constexpr (_Has_sys) { + return _Clock_cast_strategy::_Via_sys; + } else if constexpr (_Has_utc) { + return _Clock_cast_strategy::_Via_utc; + } else { + constexpr bool _Has_utc_from_sys = _Has_three_step_conversion< // + clock_time_conversion<_DestClock, utc_clock>, // + clock_time_conversion, // + clock_time_conversion, _Tp>; + + constexpr bool _Has_sys_from_utc = _Has_three_step_conversion< // + clock_time_conversion<_DestClock, system_clock>, // + clock_time_conversion, // + clock_time_conversion, _Tp>; + + if constexpr (_Has_utc_from_sys && _Has_sys_from_utc) { + return _Clock_cast_strategy::_Three_step_ambiguous; + } else if constexpr (_Has_utc_from_sys) { + return _Clock_cast_strategy::_Via_utc_from_sys; + } else if constexpr (_Has_sys_from_utc) { + return _Clock_cast_strategy::_Via_sys_from_utc; + } else { + return _Clock_cast_strategy::_None; + } + } + } + } + + template + inline constexpr auto _Clock_cast_choice = _Choose_clock_cast<_DestClock, _SourceClock, _Duration>(); + + // FUNCTION TEMPLATE clock_cast + template != _Clock_cast_strategy::_None, int> = 0> + _NODISCARD auto clock_cast(const time_point<_SourceClock, _Duration>& _Time) { + constexpr auto _Strat = _Clock_cast_choice<_DestClock, _SourceClock, _Duration>; + + if constexpr (_Strat == _Clock_cast_strategy::_Direct) { + return clock_time_conversion<_DestClock, _SourceClock>{}(_Time); + } else if constexpr (_Strat == _Clock_cast_strategy::_Via_sys) { + return clock_time_conversion<_DestClock, system_clock>{}( + clock_time_conversion{}(_Time)); + } else if constexpr (_Strat == _Clock_cast_strategy::_Via_utc) { + return clock_time_conversion<_DestClock, utc_clock>{}( + clock_time_conversion{}(_Time)); + } else if constexpr (_Strat == _Clock_cast_strategy::_Via_utc_from_sys) { + return clock_time_conversion<_DestClock, utc_clock>{}( // + clock_time_conversion{}( + clock_time_conversion{}(_Time))); + } else if constexpr (_Strat == _Clock_cast_strategy::_Via_sys_from_utc) { + return clock_time_conversion<_DestClock, system_clock>{}( // + clock_time_conversion{}( + clock_time_conversion{}(_Time))); + } else if constexpr (_Strat == _Clock_cast_strategy::_Two_step_ambiguous) { + static_assert(_Always_false<_Duration>, + "A two-step clock time conversion is required to be unique, " + "either through utc_clock or system_clock, but not both (N4878 [time.clock.cast.fn]/2)."); + } else if constexpr (_Strat == _Clock_cast_strategy::_Three_step_ambiguous) { + static_assert(_Always_false<_Duration>, + "A three-step clock time conversion is required to be unique, " + "either utc-to-system or system-to-utc, but not both (N4878 [time.clock.cast.fn]/2)."); + } else { + static_assert(_Always_false<_Duration>, "should be unreachable"); + } + } + + // [time.parse] + struct _Time_parse_fields { + using _SubsecondType = duration; + + // These are the primary fields, used to set the chrono type being parsed. + optional _Subsecond; + optional _Second; + optional _Minute; + optional _Hour_24; + optional _Weekday; // 0-based, starts Sunday + optional _Day; // 1-based + optional _Month; // 1-based + optional _Day_of_year; // 1-based + optional _Two_dig_year; + optional _Century; + optional _Utc_offset; // in minutes + optional _Tz_name; + + // These are the secondary fields, used to store parsed data. They must be converted to primary fields and + // checked for consistency. + optional _Hour_12; + optional _Ampm; + optional _Iso_year; + optional _Two_dig_iso_year; + optional _Iso_week; // 1-based + optional _Week_u; // week number, W01 begins on first Sunday + optional _Week_w; // week number, W01 begins on first Monday + + enum _FieldFlags : unsigned int { + _F_sec = 0x01, + _F_min = 0x02, + _F_hr = 0x04, + _F_day = 0x08, + _F_wkday = 0x10, + _F_mon = 0x20, + _F_doy = 0x40, + _F_year = 0x80, + }; + + _NODISCARD unsigned int _Used_fields() const { + unsigned int _Ret{0}; + if (_Second || _Subsecond) { + _Ret |= _F_sec; + } + if (_Minute) { + _Ret |= _F_min; + } + if (_Hour_24) { + _Ret |= _F_hr; + } + if (_Day) { + _Ret |= _F_day; + } + if (_Weekday) { + _Ret |= _F_wkday; + } + if (_Month) { + _Ret |= _F_mon; + } + if (_Day_of_year) { + _Ret |= _F_doy; + } + if (_Two_dig_year && _Century) { + _Ret |= _F_year; + } + return _Ret; + } + + _NODISCARD static bool _Test_bits( + const unsigned int _Bits, const unsigned int _Must_set, const unsigned int _Optional = 0) { + return (_Bits & ~_Optional) == _Must_set; + } + + + template + static constexpr _Ty _Invalid_time_field{numeric_limits<_Ty>::lowest()}; + + static void _Initialize_time_point(tm& _Tp) { + _Tp.tm_sec = _Invalid_time_field; + _Tp.tm_min = _Invalid_time_field; + _Tp.tm_hour = _Invalid_time_field; + _Tp.tm_wday = _Invalid_time_field; + _Tp.tm_mday = _Invalid_time_field; + _Tp.tm_mon = _Invalid_time_field; + _Tp.tm_yday = _Invalid_time_field; + _Tp.tm_year = _Invalid_time_field; + } + + template + _NODISCARD static bool _Update(optional<_Ty>& _Val, const _Ty& _New) { + // Update a field. Ignores invalid values. If _Val already has a value, returns true or false according to + // whether the new value matches the current one or not, so that inconsistencies can be detected. + + if constexpr (!is_same_v<_Ty, string>) { + if (_New == _Invalid_time_field<_Ty>) { + return true; + } + } + + if (!_Val.has_value()) { + _Val = _New; + return true; + } else { + return _STD exchange(_Val, _New) == _New; + } + } + + _NODISCARD static pair _Decompose_year(const int _Year) { + int _Two_d_year = _Year % 100; + if (_Two_d_year < 0) { + _Two_d_year += 100; + } + return {_Year - _Two_d_year, _Two_d_year}; + } + + _NODISCARD bool _Update_if_valid(const tm& _Tp, const bool _Full_year) { + bool _No_err{true}; + if (_Tp.tm_hour != _Invalid_time_field) { + _No_err = _No_err && _Update(_Hour_24, _Tp.tm_hour); + _No_err = _No_err && _Update(_Ampm, _Hour_24 >= 12 ? 1 : 0); + _No_err = _No_err && _Update(_Hour_12, _CHRONO make12(hours{*_Hour_24}).count()); + } + + _No_err = _No_err && _Update(_Minute, _Tp.tm_min); + _No_err = _No_err && _Update(_Second, _Tp.tm_sec); + _No_err = _No_err && _Update(_Day, _Tp.tm_mday); + _No_err = _No_err && _Update(_Weekday, _Tp.tm_wday); + + if (_Tp.tm_mon != _Invalid_time_field) { + _No_err = _No_err && _Update(_Month, _Tp.tm_mon + 1); + } + + if (_Tp.tm_yday != _Invalid_time_field) { + _No_err = _No_err && _Update(_Day_of_year, _Tp.tm_yday + 1); + } + + if (_Tp.tm_year != _Invalid_time_field) { + // Sometimes we expect only the last two digits. + const auto _Year_parts = _Decompose_year(_Tp.tm_year + 1900); + _No_err = _No_err && _Update(_Two_dig_year, _Year_parts.second); + if (_Full_year) { + _No_err = _No_err && _Update(_Century, _Year_parts.first); + } + } + + return _No_err; + } + + _NODISCARD bool _Yday_to_month_day(const int _Yday, const int _Year) { + // A day-of-year that's February 28 or earlier, by itself, is a valid month_day. Any later day is + // ambiguous without a year. + if (_Out_of_range(_Day_of_year, 1, _Two_dig_year || _Iso_year ? _Days_in_year(_Year) : 59)) { + return false; + } + + if (_Day_of_year <= 31) { + return _Update(_Day, _Yday) && _Update(_Month, 1); + } + + const int _Feb_end{_Is_leap(_Year) ? 60 : 59}; + if (_Day_of_year <= _Feb_end) { + return _Update(_Day, _Yday - 31) && _Update(_Month, 2); + } + + // Shift day-of-year so that 1 == March 1. This is the same as year_month_day::_Civil_from_days, except + // _Day_of_year --> _Shifted_yday-1 and _Mp --> _Month - 3. + const int _Shifted_yday{*_Day_of_year - _Feb_end}; + return _Update(_Month, (535 * _Shifted_yday + 48950) >> 14) + && _Update(_Day, _Shifted_yday - ((979 * *_Month - 2918) >> 5)); + } + + static constexpr int _Era_begin_wday{3}; // Wednesday + + _NODISCARD static constexpr int _Jan1_weekday(int _Year) { + --_Year; + const int _Era = (_Year >= 0 ? _Year : _Year - 399) / 400; + const int _Yoe = _Year - _Era * 400; + // Jan. 1 is always day 306 of the shifted [Mar, ..., Dec, Jan, Feb] year. + const int _Doe = ((1461 * _Yoe) >> 2) - _Yoe / 100 + 306; + return (_Doe + _Era_begin_wday) % 7; + } + + _NODISCARD static constexpr int _Iso8601_weeks(int _Year) { + const int _P_y = (_Year + _Year / 4 - _Year / 100 + _Year / 400) % 7; + --_Year; + const int _P_ym1 = (_Year + _Year / 4 - _Year / 100 + _Year / 400) % 7; + return 52 + (_P_y == 4 || _P_ym1 == 3); + } + + _NODISCARD static constexpr int _Iso8601_week(const int _Day_of_year, const int _Weekday, const int _Year) { + // Jan. 4 is always week 1; rollover to next week always happens on Monday. + const auto _Week{(_Day_of_year + 9 - _Prev_weekday(_Weekday, 1)) / 7}; + if (_Week < 1) { + return _Iso8601_weeks(_Year - 1); + } else if (_Week > _Iso8601_weeks(_Year)) { + return 1; + } else { + return _Week; + } + } + + _NODISCARD static constexpr bool _Is_leap(const int _Year) { + return _Year % 4 == 0 && (_Year % 100 != 0 || _Year % 400 == 0); + } + + _NODISCARD static constexpr int _Days_in_year(const int _Year) { + return _Is_leap(_Year) ? 366 : 365; + } + + _NODISCARD static constexpr int _Next_weekday(const int _Wday, const int _Shift) { + // 0 <= _Shift <= 6 + int _Result = _Wday + _Shift; + if (_Result >= 7) { + _Result -= 7; + } + return _Result; + } + + _NODISCARD static constexpr int _Prev_weekday(const int _Wday, const int _Shift) { + // 0 <= _Shift <= 6 + return (_Wday >= _Shift ? 0 : 7) + (_Wday - _Shift); + } + + _NODISCARD bool _Calculate_ymd_from_week_date(const int _Starting_wday, const int _Week, const int _Year) { + // (a) Calculate day-of-year of first _Starting_wday in January. + // (b) Shift *_Weekday so that it's relative to _Starting_wday. + // (c) Offset to desired week. + const int _Jan1_wday = _Jan1_weekday(_Year); + const int _Yday = 1 + _Prev_weekday(_Starting_wday, _Jan1_wday) // (a) + + _Prev_weekday(*_Weekday, _Starting_wday) // (b) + + 7 * (_Week - 1); // (c) + return _Update(_Day_of_year, _Yday) && !_Out_of_range(_Day_of_year, 1, _Days_in_year(_Year)) + && _Yday_to_month_day(*_Day_of_year, _Year); + } + + _NODISCARD bool _Calculate_ymd() { + bool _No_err = true; + // Flags to indicate if a field should be checked for consistency with other data. Set to false when the + // field is used to calculate the date, as it's necessarily self-consistent in that case (barring a bug). + bool _Check_u = true; + bool _Check_w = true; + bool _Check_iso = true; + bool _Check_wday = true; + + if (_Day_of_year && _Out_of_range(_Day_of_year, 1, 366)) { + return false; + } + + bool _Have_year = false; + int _Year{0}; + if (_Two_dig_year) { + _Year = *_Century + *_Two_dig_year; + _Have_year = true; + if (_Day_of_year) { + _No_err = _No_err && _Yday_to_month_day(*_Day_of_year, _Year); + } else if (_Week_u || _Week_w) { + _Check_wday = false; + if (_Week_u) { + _Check_u = false; + _No_err = _No_err && _Calculate_ymd_from_week_date(0 /*Sunday*/, *_Week_u, _Year); + } + + if (_Week_w) { + _Check_w = false; + _No_err = _No_err && _Calculate_ymd_from_week_date(1 /*Monday*/, *_Week_w, _Year); + } + } + } + + if (_Iso_year) { + // ISO weeks begin on Monday. W01 always contains January 4. There is no W00, so the beginning of + // January can be in W52 or W53 of the previous year. Likewise, the end of December can occur at the + // beginning of W01 of the following year, depending on where in the week Jan. 4 falls. + _Check_wday = false; + _Check_iso = false; + _Year = *_Iso_year; + _Have_year = true; + + // Shift weekdays to Monday-based. Jan. 4 is the anchor of week 1, so calculate where the parsed weekday + // is relative to that point. + const int _Jan4_wday = _Next_weekday(_Jan1_weekday(_Year), 3 - 1); + const int _Offset_from_jan4 = _Prev_weekday(*_Weekday, 1) - _Jan4_wday; + int _Trial_yday = 4 + 7 * (*_Iso_week - 1) + _Offset_from_jan4; + const int _Ref_num_days = _Trial_yday < 1 ? _Days_in_year(_Year - 1) : _Days_in_year(_Year); + if (_Trial_yday < 1) { + _Trial_yday += _Ref_num_days; + --_Year; + } else if (_Trial_yday > _Ref_num_days) { + _Trial_yday -= _Ref_num_days; + ++_Year; + } + + const auto _Year_parts = _Decompose_year(_Year); + _No_err = _No_err && _Update(_Day_of_year, _Trial_yday) && _Yday_to_month_day(*_Day_of_year, _Year) + && _Update(_Century, _Year_parts.first) && _Update(_Two_dig_year, _Year_parts.second); + } + + // Must have YMD by this point, either parsed directly or calculated above. + if (!_Have_year || !_Month || !_Day || !_No_err) { + return false; + } + + // consistency checks + if (_Check_wday && _Weekday) { + const auto _Era_year = _Year - (*_Month <= 2); + const int _Era = (_Era_year >= 0 ? _Era_year : _Era_year - 399) / 400; + const int _Yoe = _Era_year - _Era * 400; + const int _Yday_era = ((979 * (*_Month + (*_Month > 2 ? -3 : 9)) + 19) >> 5) + *_Day - 1; + const int _Doe = ((1461 * _Yoe) >> 2) - _Yoe / 100 + _Yday_era; + _No_err = _No_err && _Update(_Weekday, (_Doe + _Era_begin_wday) % 7); + } + + if (_Check_u && _Week_u) { + _No_err = _No_err && _Update(_Week_u, (*_Day_of_year + 6 - *_Weekday) / 7); + } + + if (_Check_w && _Week_w) { + _No_err = _No_err && _Update(_Week_w, (*_Day_of_year + 6 - _Prev_weekday(*_Weekday, 1)) / 7); + } + + if (_Check_iso && _Iso_week) { + _No_err = _No_err && _Update(_Iso_week, _Iso8601_week(*_Day_of_year, *_Weekday, _Year)); + } + + return _No_err; + } + + _NODISCARD bool _Calculate_hour24() { + if (_Hour_12) { + return _Update( + _Hour_24, _CHRONO make24(hours{*_Hour_12}, static_cast(_Ampm.value_or(0))).count()); + } else { + return true; + } + } + + _NODISCARD bool _Calculate_year_fields() { + bool _No_err = true; + // The order of these updates is significant. Updating the ISO date second allows + // formats with %g and %y, but not %C, to get the century implicitly from %y. + if (_Two_dig_year && !_Century) { + _No_err = _No_err && _Update(_Century, _Two_dig_year >= 69 ? 1900 : 2000); + } + + // %C is only combined with %g if %G is missing, to avoid an unnecessary parse failure when the ISO and + // Gregorian years are in different centuries. + if (_Two_dig_iso_year && _Century && !_Iso_year) { + _No_err = _No_err && _Update(_Iso_year, *_Century + *_Two_dig_iso_year); + } + + return _No_err; + } + + _NODISCARD static bool _Out_of_range(const optional& _Field, const int _Min, const int _Max) { + return _Field && (_Field < _Min || _Max < _Field); + } + + _NODISCARD bool _Is_complete() const { + // Check for data that is incomplete, ambiguous, or obviously out-of-range. The exception is 12-hour time + // without am/pm. Most strptime implementations will assume am in this case, so we'll do that too. Don't + // check for consistency yet, because the data might not even be representable by the type being parsed, + // and calendar computations are relatively expensive. + + // Most time-of-day fields are deferred until we know if we're parsing a time_point. + if (_Out_of_range(_Hour_12, 1, 12) // + || _Out_of_range(_Weekday, 0, 6) // + || _Out_of_range(_Day, 1, 31) // + || _Out_of_range(_Month, 1, 12)) { + return false; + } + + if (_Iso_year || _Two_dig_iso_year || _Iso_week) { + // Need to have %G or %C+%g. The century can be parsed explicitly, or derived implicitly from %y. + const bool _Has_complete_year{_Iso_year || ((_Century || _Two_dig_year) && _Two_dig_iso_year)}; + if (!_Has_complete_year || !_Iso_week || !_Weekday || _Out_of_range(_Iso_week, 1, 53)) { + return false; + } + } + + if (_Week_u || _Week_w) { + // Need a weekday and year to be complete. + if (!_Weekday || !_Two_dig_year) { + return false; + } + + if (_Out_of_range(_Week_u, 0, 53) || _Out_of_range(_Week_w, 0, 53)) { + return false; + } + } + + return true; + } + + template + _NODISCARD static constexpr bool _Can_represent() { + using _Rep1 = typename _Duration1::rep; + using _Period1 = typename _Duration1::period; + using _Period2 = typename _Duration2::period; + + // Returns whether _Duration1 can represent _Duration2{1}. Assumes 1 <= _Period2 <= + // 86,400, i.e., we're interested in time periods between seconds and days. + if constexpr (is_integral_v<_Rep1>) { + // Must have _Period1 <= _Period2 and numeric_limits<_Rep1>::max() >= _Period2 / _Period1. For example, + // std::days can't represent std::seconds, and duration::max() is ~9.2 seconds. + constexpr auto _Max_tick = static_cast((numeric_limits>::max)()); + + // clang-format off + return ratio_less_equal_v<_Period1, _Period2> + && ratio_greater_equal_v, ratio_divide, _Period1>>; + // clang-format on + } else if (is_floating_point_v<_Rep1>) { + // With the smallest possible _Period1, ratio<1,INTMAX_MAX>, one day has a tick count of + // 86,400*INTMAX_MAX ~= 7.97e23. This is representable by float and double, so they can always represent + // at least one day. On the other hand, one second with the largest possible _Period1 needs a tick count + // of 1/(INTMAX_MAX) ~= 1.08e-19, which is also representable in both float and double. So, both + // floating point types can represent durations between one second and one day, regardless of _Period1. + return true; + } else { + // TRANSITION: user-defined arithmetic-like types + return true; + } + } + + enum class _Parse_tp_or_duration { _Time_point, _Duration }; + + template <_Parse_tp_or_duration _Parse_type, class _DurationType> + _NODISCARD bool _Apply_duration_fields(_DurationType& _Result) { + constexpr bool _Can_rep_sec = _Can_represent<_DurationType, seconds>(); + constexpr bool _Can_rep_min = _Can_represent<_DurationType, minutes>(); + constexpr bool _Can_rep_hr = _Can_represent<_DurationType, hours>(); + constexpr bool _Can_rep_day = _Can_represent<_DurationType, days>(); + constexpr bool _For_time_point = _Parse_type == _Parse_tp_or_duration::_Time_point; + + const auto _Required{(_For_time_point ? _F_day | _F_mon | _F_year : 0)}; + const auto _Optional{(_For_time_point ? _F_wkday : 0) | (_Can_rep_sec ? _F_sec : 0) + | (_Can_rep_min ? _F_min : 0) | (_Can_rep_hr ? _F_hr : 0) + | (_Can_rep_day ? _F_doy : 0)}; + + const auto _Used{_Used_fields()}; + const auto _Time_out_of_range{ + _For_time_point + && (_Out_of_range(_Second, 0, 60) || _Out_of_range(_Minute, 0, 59) || _Out_of_range(_Hour_24, 0, 23))}; + + if (_Time_out_of_range || !_Test_bits(_Used, _Required, _Optional)) { + return false; + } + + _Result = _DurationType::zero(); + if constexpr (_Can_rep_sec) { + if (_Used & _F_sec) { + if (_Subsecond) { + using _CastedType = duration; + const _SubsecondType _Sub{*_Subsecond}; + if (treat_as_floating_point_v<_DurationType> // + || _CHRONO duration_cast<_CastedType>(_Sub) == _Sub) { + _Result += _CHRONO duration_cast<_DurationType>(_Sub); + } else { + return false; + } + } + + if (_Second) { + _Result += _CHRONO duration_cast<_DurationType>(seconds{*_Second}); + } + } + } + + if constexpr (_Can_rep_min) { + if (_Used & _F_min) { + _Result += _CHRONO duration_cast<_DurationType>(minutes{*_Minute}); + } + + if (_Utc_offset) { + _Result -= _CHRONO duration_cast<_DurationType>(minutes{*_Utc_offset}); + } + } + + if constexpr (_Can_rep_hr) { + if (_Used & _F_hr) { + _Result += _CHRONO duration_cast<_DurationType>(hours{*_Hour_24}); + } + } + + if constexpr (_Can_rep_day) { + if (_For_time_point) { + year_month_day _Ymd; + if (_Make_year_month_day(_Ymd, true)) { + _Result += _CHRONO duration_cast<_DurationType>(static_cast(_Ymd).time_since_epoch()); + } else { + return false; + } + } else if (_Used & _F_doy) { + _Result += _CHRONO duration_cast<_DurationType>(days{*_Day_of_year}); + } + } + + return true; + } + + template + _NODISCARD bool _Make_duration(duration<_Rep, _Period>& _Duration_result) { + const bool _Consistent = _Calculate_hour24(); + if (_Consistent) { + _Duration_result = duration<_Rep, _Period>::zero(); // TRANSITION: LWG-3536 & GH-1740 + return _Apply_duration_fields<_Parse_tp_or_duration::_Duration>(_Duration_result); + } + + return false; + } + + enum class _LeapSecondRep : unsigned int { + _None, // tai_clock, gps_clock; oblivious to leap seconds + _Negative, // system_clock, observes negative, but not positive, leap seconds + _All, // utc_clock + _File_time // _Negative before 1 January 2017, _All afterwards. + }; + + template + _NODISCARD bool _Make_time_point(_DurationType& _Dur, _LeapSecondRep _Leap) { + + const bool _Consistent{_Calculate_hour24() && _Calculate_year_fields()}; + if (!_Consistent || !_Apply_duration_fields<_Parse_tp_or_duration::_Time_point>(_Dur)) { + return false; + } + + const _LeapSecondRep _Original_leap{_Leap}; + if (_Leap == _LeapSecondRep::_File_time) { + const int _Year{*_Century + *_Two_dig_year}; + _Leap = _Year <= 2016 ? _LeapSecondRep::_Negative : _LeapSecondRep::_All; + } + + if (_Second > (_Leap == _LeapSecondRep::_All ? 60 : 59)) { + return false; + } + + // A distinction has to be made here between clocks that tick monotonically, even around a positive leap + // second (everything except system_clock) and ones that have a special representation for leap seconds + // (utc_clock and, for negative leap seconds, system_clock). gps_clock and tai_clock, in particular, always + // monotonically count 86,400 seconds/day. The correct tick count, therefore, can be determined without + // knowing whether any leap seconds have occurred, and there aren't any invalid times due to negative leap + // second deletions. + // + // system_clock also has 86,400 seconds/day, but observes negative leap seconds by skipping them in its tick + // count. So leap second data is needed to check for a valid time, but not to calculate the tick count. + + if (_Leap != _LeapSecondRep::_None) { + if (_Hour_24 == 23 && _Minute == 59 && _Second >= 59) { + // It's possible that the parsed time doesn't exist because (a) _Seconds == 60 and there *isn't* a + // leap second insertion or (b) _Seconds == 59 and there *is* a leap second subtraction. + + // Check for quick exit. + const auto& _Tzdb{_CHRONO get_tzdb()}; + if (_Leap == _LeapSecondRep::_Negative && _Tzdb._All_ls_positive) { + return true; + } + + const bool _Possible_insertion{_Second == 60}; + const sys_seconds _Target_sys_time{ + _CHRONO floor(_Dur) + (_Possible_insertion ? seconds{0} : seconds{1})}; + const auto& _Ls_vector{_Tzdb.leap_seconds}; + const auto _It{_STD lower_bound(_Ls_vector.begin(), _Ls_vector.end(), _Target_sys_time)}; + const bool _Match_leap{_It != _Ls_vector.end() && *_It == _Target_sys_time}; + + // Condition for a good parse: (_Seconds == 59 && !_Match_leap) || (_Match_leap && + // _It->_Is_positive()). Below is the inverse of this. + if (!(_Match_leap ? _It->_Positive() : !_Possible_insertion)) { + return false; + } + } + + if (_Leap == _LeapSecondRep::_All) { + constexpr bool _Can_rep_sec = _Can_represent<_DurationType, seconds>(); + if constexpr (_Can_rep_sec) { + _Dur = utc_clock::from_sys(sys_time<_DurationType>{_Dur}).time_since_epoch(); + // If we parsed a positive leap second, then _Dur, interpreted as a system time, refers to the + // second *after* insertion. Need to back up one second in this case. + if (_Second == 60) { + _Dur -= _CHRONO duration_cast<_DurationType>(seconds{1}); + } + + if (_Original_leap == _LeapSecondRep::_File_time) { + _Dur -= _CHRONO duration_cast<_DurationType>( + filesystem::_File_time_clock::_Skipped_filetime_leap_seconds); + } + } else { + // Error if _Dur can't represent the above adjustment. + return false; + } + } + } + + return true; + } + + _NODISCARD bool _Make_day(day& _Day_result) { + if (_Used_fields() == _F_day) { + _Day_result = day{static_cast(*_Day)}; + return true; + } else { + return false; + } + } + + _NODISCARD bool _Make_weekday(weekday& _Weekday_result) { + if (_Used_fields() == _F_wkday) { + _Weekday_result = weekday{static_cast(*_Weekday)}; + return true; + } else { + return false; + } + } + + _NODISCARD bool _Make_month(month& _Month_result) { + if (_Used_fields() == _F_mon) { + _Month_result = month{static_cast(*_Month)}; + return true; + } else { + return false; + } + } + + _NODISCARD bool _Make_month_day(month_day& _Month_day_result) { + if (_Day_of_year && !_Yday_to_month_day(*_Day_of_year, 0)) { + return false; + } + + constexpr auto _Required = _F_mon | _F_day; + constexpr auto _Optional = _F_doy; + if (_Test_bits(_Used_fields(), _Required, _Optional)) { + _Month_day_result = + month_day{month{static_cast(*_Month)}, day{static_cast(*_Day)}}; + return true; + } else { + return false; + } + } + + _NODISCARD bool _Make_year(year& _Year_result) { + if (_Calculate_year_fields() && _Used_fields() == _F_year) { + _Year_result = year{*_Century + *_Two_dig_year}; + return true; + } else { + return false; + } + } + + _NODISCARD bool _Make_year_month(year_month& _Year_month_result) { + if (_Calculate_year_fields() && _Used_fields() == (_F_mon | _F_year)) { + _Year_month_result = + year_month{year{*_Century + *_Two_dig_year}, month{static_cast(*_Month)}}; + return true; + } else { + return false; + } + } + + _NODISCARD bool _Make_year_month_day( + year_month_day& _Year_month_day_result, const bool _For_time_point = false) { + const bool _Consistent = _Calculate_year_fields() && _Calculate_ymd(); + constexpr auto _Required = _F_day | _F_mon | _F_year; + auto _Optional = _F_wkday | _F_doy; + if (_For_time_point) { + // These fields aren't used here, but they might be used later if converting to a time_point. + _Optional |= _F_sec | _F_min | _F_hr; + } + + if (_Consistent && _Test_bits(_Used_fields(), _Required, _Optional)) { + _Year_month_day_result = year_month_day{year{*_Century + *_Two_dig_year}, + month{static_cast(*_Month)}, day{static_cast(*_Day)}}; + return true; + } else { + return false; + } + } + + template + _NODISCARD _InIt _Parse_time_field(_InIt _First, ios_base& _Iosbase, ios_base::iostate& _State, + const char _Flag, const char _Modifier, const unsigned int _Width, + const unsigned int _Subsecond_precision) { + using _CharT = typename _InIt::value_type; + + const auto& _Ctype_fac = _STD use_facet>(_Iosbase.getloc()); + const auto& _Time_fac = _STD use_facet>(_Iosbase.getloc()); + constexpr _InIt _Last{}; + + int _Val{0}; + switch (_Flag) { + case 'a': + case 'A': + { + tm _Tp; + _Tp.tm_wday = _Invalid_time_field; + _First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'a'); + if (!_Update(_Weekday, _Tp.tm_wday)) { + _State |= ios_base::failbit; + } + break; + } + + case 'b': + case 'B': + case 'h': + { + tm _Tp; + _Tp.tm_mon = _Invalid_time_field; + _First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'b'); + if (_Tp.tm_mon == _Invalid_time_field || !_Update(_Month, ++_Tp.tm_mon)) { + _State |= ios_base::failbit; + } + break; + } + + case 'C': + if (_Modifier != 'E') { + _State |= _Get_int(_First, _Width == 0 ? 2u : _Width, _Val, _Ctype_fac); + _Val *= 100; + } else { + tm _Tp; + _Tp.tm_year = _Invalid_time_field; + _First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'C', 'E'); + _Val = _Tp.tm_year; + if (_Tp.tm_year != _Invalid_time_field) { + _Val += 1900; + } + } + + if (!_Update(_Century, _Val)) { + _State |= ios_base::failbit; + } + break; + + case 'd': + case 'e': + if (_Modifier != 'O') { + _State |= _Get_int(_First, _Width == 0 ? 2u : _Width, _Val, _Ctype_fac); + } else { + tm _Tp; + _Tp.tm_mday = _Invalid_time_field; + _First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'd', 'O'); + _Val = _Tp.tm_mday; + } + + if (!_Update(_Day, _Val)) { + _State |= ios_base::failbit; + } + break; + + case 'D': + _First = _Parse_time_field_restricted(_First, _Iosbase, _State, "%m/%d/%y"); + break; + + case 'F': + { + // If modified with a width N, the width is applied to only %Y. + _State |= _Get_int(_First, _Width == 0 ? 4u : _Width, _Val, _Ctype_fac); + const auto _Year_parts = _Decompose_year(_Val); + if (_Update(_Century, _Year_parts.first) && _Update(_Two_dig_year, _Year_parts.second)) { + _First = _Parse_time_field_restricted(_First, _Iosbase, _State, "-%m-%d"); + } else { + _State |= ios_base::failbit; + } + break; + } + + case 'g': + _State |= _Get_int(_First, _Width == 0 ? 2u : _Width, _Val, _Ctype_fac); + if (!_Update(_Two_dig_iso_year, _Val)) { + _State |= ios_base::failbit; + } + break; + + case 'G': + { + _State |= _Get_int(_First, _Width == 0 ? 4u : _Width, _Val, _Ctype_fac); + const auto _Year_parts = _Decompose_year(_Val); + if (!_Update(_Iso_year, _Val) || !_Update(_Two_dig_iso_year, _Year_parts.second)) { + _State |= ios_base::failbit; + } + break; + } + + case 'H': + if (_Modifier != 'O') { + _State |= _Get_int(_First, _Width == 0 ? 2u : _Width, _Val, _Ctype_fac); + } else { + tm _Tp; + _Tp.tm_hour = _Invalid_time_field; + _First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'H', 'O'); + _Val = _Tp.tm_hour; + } + + if (!_Update(_Hour_24, _Val) + || (_Val < 24 + && (!_Update(_Ampm, _Val >= 12 ? 1 : 0) + || !_Update(_Hour_12, _CHRONO make12(hours{_Val}).count())))) { + _State |= ios_base::failbit; + } + break; + + case 'I': + if (_Modifier != 'O') { + _State |= _Get_int(_First, _Width == 0 ? 2u : _Width, _Val, _Ctype_fac); + } else { + tm _Tp; + _Tp.tm_hour = _Invalid_time_field; + _First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'I', 'O'); + _Val = (_Tp.tm_hour == 0) ? 12 : _Tp.tm_hour; + } + + if (!_Update(_Hour_12, _Val)) { + _State |= ios_base::failbit; + } + break; + + case 'j': + _State |= _Get_int(_First, _Width == 0 ? 3u : _Width, _Val, _Ctype_fac); + if (!_Update(_Day_of_year, _Val)) { + _State |= ios_base::failbit; + } + break; + + case 'M': + if (_Modifier != 'O') { + _State |= _Get_int(_First, _Width == 0 ? 2u : _Width, _Val, _Ctype_fac); + } else { + tm _Tp; + _Tp.tm_min = _Invalid_time_field; + _First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'M', 'O'); + _Val = _Tp.tm_min; + } + + if (!_Update(_Minute, _Val)) { + _State |= ios_base::failbit; + } + break; + + case 'm': + if (_Modifier != 'O') { + _State |= _Get_int(_First, _Width == 0 ? 2u : _Width, _Val, _Ctype_fac); + } else { + tm _Tp; + _Initialize_time_point(_Tp); + _First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'm', 'O'); + _Val = _Tp.tm_mon; + if (_Tp.tm_mon != _Invalid_time_field) { + ++_Val; + } + } + + if (!_Update(_Month, _Val)) { + _State |= ios_base::failbit; + } + break; + + case 'p': + { + tm _Tp; + _Tp.tm_hour = 0; + _First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'p'); + if (!_Update(_Ampm, _Tp.tm_hour == 0 ? 0 : 1)) { + _State |= ios_base::failbit; + } + break; + } + + case 'c': + case 'r': + case 'x': + case 'X': + { + tm _Tp; + _Initialize_time_point(_Tp); + const bool _Full_year = (_Flag == 'c'); // 'x' reads two-digit year, 'r' and 'X' read times + _First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, _Flag, _Modifier); + if (!_Update_if_valid(_Tp, _Full_year)) { + _State |= ios_base::failbit; + } + break; + } + + case 'R': + _First = _Parse_time_field_restricted(_First, _Iosbase, _State, "%H:%M"); + break; + + case 'T': + _First = _Parse_time_field_restricted(_First, _Iosbase, _State, "%H:%M:%S", _Subsecond_precision); + break; + + case 'S': + if (_Subsecond_precision == 0) { + _State |= _Get_int(_First, _Width == 0 ? 2u : _Width, _Val, _Ctype_fac); + if (!_Update(_Second, _Val)) { + _State |= ios_base::failbit; + } + } else { + const auto& _Numpunct_fac = _STD use_facet>(_Iosbase.getloc()); + _State |= + _Get_fixed(_First, _Width == 0 ? 3 + _Subsecond_precision : _Width, _Ctype_fac, _Numpunct_fac); + } + break; + + case 'u': + case 'w': + if (_Flag == 'w' && _Modifier == 'O') { + tm _Tp; + _Tp.tm_wday = _Invalid_time_field; + _First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'w', 'O'); + _Val = _Tp.tm_wday; + } else { + _State |= _Get_int(_First, _Width == 0 ? 1u : _Width, _Val, _Ctype_fac); + if (_Flag == 'u') { + // ISO weekday: [1,7], 7 == Sunday + if (_Val == 7) { + _Val = 0; + } else if (_Val == 0) { + _Val = 7; // out of range + } + } + } + + if (!_Update(_Weekday, _Val)) { + _State |= ios_base::failbit; + } + break; + + case 'U': + case 'V': + case 'W': + { + _State |= _Get_int(_First, _Width == 0 ? 2u : _Width, _Val, _Ctype_fac); + auto& _Field{(_Flag == 'U') ? _Week_u : (_Flag == 'W' ? _Week_w : _Iso_week)}; + if (!_Update(_Field, _Val)) { + _State |= ios_base::failbit; + } + break; + } + + case 'y': + if (_Modifier == '\0') { + _State |= _Get_int(_First, _Width == 0 ? 2u : _Width, _Val, _Ctype_fac); + } else { + tm _Tp; + _Tp.tm_year = _Invalid_time_field; + _First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'y', _Modifier); + if (_Modifier == 'E') { + _Val = _Tp.tm_year + 1900; // offset from %EC base year + } else { + const auto _Year_parts = _Decompose_year(_Tp.tm_year); + _Val = _Year_parts.second; + } + } + + if (!_Update(_Two_dig_year, _Val)) { + _State |= ios_base::failbit; + } + break; + + case 'Y': + { + if (_Modifier == 'E') { + tm _Tp; + _Tp.tm_year = _Invalid_time_field; + _First = _Time_fac.get(_First, _Last, _Iosbase, _State, &_Tp, 'Y', 'E'); + _Val = _Tp.tm_year + 1900; + } else { + _State |= _Get_int(_First, _Width == 0 ? 4u : _Width, _Val, _Ctype_fac); + } + + const auto _Year_parts = _Decompose_year(_Val); + if (!_Update(_Century, _Year_parts.first) || !_Update(_Two_dig_year, _Year_parts.second)) { + _State |= ios_base::failbit; + } + break; + } + + case 'z': + _State |= _Get_tz_offset(_First, _Ctype_fac, _Modifier == 'E' || _Modifier == 'O', _Val); + if (!_Update(_Utc_offset, _Val)) { + _State |= ios_base::failbit; + } + break; + + case 'Z': + { + string _Name; + _State |= _Get_tz_name(_First, _Ctype_fac, _Name); + if (!_Update(_Tz_name, _Name)) { + _State |= ios_base::failbit; + } + break; + } + + default: + // Invalid flag + _State |= ios_base::failbit; + break; + } + + return _First; + } + + template + _NODISCARD _InIt _Parse_time_field_restricted(_InIt _First, ios_base& _Iosbase, ios_base::iostate& _State, + const char* _Fmt, const unsigned int _Subsecond_precision = 0) { + using _Ctype = ctype; + // Parses a restricted format string. It generally doesn't handle anything parsed outside of + // _Parse_time_field: + // (a) any whitespace (' ', %n, %t) + // (b) %% literal (other literals are parsed, however) + // (c) E or O modifiers + // (d) width parameter + // It also assumes a valid format string, specifically that '%' is always followed by a flag. + const _Ctype& _Ctype_fac{_STD use_facet<_Ctype>(_Iosbase.getloc())}; + constexpr _InIt _Last{}; + + while (*_Fmt != '\0' && _First != _Last && _State == ios_base::goodbit) { + if (*_Fmt == '%') { + _First = _Parse_time_field(_First, _Iosbase, _State, *++_Fmt, '\0', 0, _Subsecond_precision); + } else if (_Ctype_fac.narrow(*_First++) != *_Fmt) { + _State |= ios_base::failbit; + } + ++_Fmt; + } + return _First; + } + + template + _NODISCARD ios_base::iostate _Get_fixed(_InIt& _First, unsigned int _Width, + const ctype& _Ctype_fac, + const numpunct& _Numpunct_fac) { + constexpr _InIt _Last{}; + + while (_First != _Last && _Ctype_fac.is(ctype_base::space, *_First) && _Width > 0) { + ++_First; + --_Width; + } + + int _Second_ = 0; + int64_t _Subsecond_ = 0; + int64_t _Multiplier = _SubsecondType::period::den; + bool _Found_point = false; + bool _Found_digit = false; + + while (_First != _Last && _Width > 0 && _Multiplier >= 10) { + const auto _Ch = *_First; + if (_Ch == _Numpunct_fac.decimal_point() && !_Found_point) { + _Found_point = true; + } else if (_Ctype_fac.is(ctype_base::digit, _Ch)) { + _Found_digit = true; + const auto _Digit = _Ctype_fac.narrow(_Ch) - '0'; + if (_Found_point) { + _Multiplier /= 10; + _Subsecond_ += _Digit * _Multiplier; + } else { + if (_Second_ > ((numeric_limits::max)() - _Digit) / 10) { + return ios_base::failbit; + } + + _Second_ = _Second_ * 10 + _Digit; + } + } else { + break; + } + ++_First; + --_Width; + } + + ios_base::iostate _State = ios_base::goodbit; + if (_First == _Last) { + _State |= ios_base::eofbit; + } + + if (!(_Found_digit && _Update(_Second, _Second_) && _Update(_Subsecond, _Subsecond_))) { + _State |= ios_base::failbit; + } + + return _State; + } + + template + _NODISCARD ios_base::iostate _Get_int( + _InIt& _First, unsigned int _Width, int& _Val, const ctype& _Ctype_fac) { + constexpr _InIt _Last{}; + + while (_First != _Last && _Ctype_fac.is(ctype_base::space, *_First) && _Width > 0) { + ++_First; + --_Width; + } + + char _Ac[_MAX_INT_DIG]; + char* _Ptr = _Ac; + if (_First != _Last && _Width > 0) { + const char _Ch = _Ctype_fac.narrow(*_First); + if (_Ch == '+' || _Ch == '-') { // copy sign + *_Ptr++ = _Ch; + ++_First; + --_Width; + } + } + + bool _Has_leading_zero = false; + while (_First != _Last && _Width > 0 && _Ctype_fac.narrow(*_First) == '0') { // strip leading zeros + ++_First; + --_Width; + _Has_leading_zero = true; + } + + if (_Has_leading_zero) { + *_Ptr++ = '0'; + } + + char _Ch; + while (_First != _Last && _Width > 0 && '0' <= (_Ch = _Ctype_fac.narrow(*_First)) + && _Ch <= '9') { // copy digits + *_Ptr = _Ch; + if (_Ptr < _STD cend(_Ac)) { + ++_Ptr; // drop trailing digits if already too large + } + ++_First; + --_Width; + } + + *_Ptr = '\0'; + int _Errno = 0; + char* _Ep; + const long _Ans = _CSTD _Stolx(_Ac, &_Ep, 10, &_Errno); + ios_base::iostate _State = ios_base::goodbit; + + if (_First == _Last) { + _State |= ios_base::eofbit; + } + + if (_Ep == _Ac || _Errno != 0) { + _State |= ios_base::failbit; // bad conversion + } else { + _Val = _Ans; // store valid result + } + + return _State; + } + + template + _NODISCARD ios_base::iostate _Get_tz_offset( + _InIt& _First, const ctype& _Ctype_fac, const bool _Is_modified, int& _Offset) { + constexpr _InIt _Last{}; + if (_First == _Last) { + return ios_base::eofbit; + } + + bool _Negative = false; + switch (_Ctype_fac.narrow(*_First)) { + case '-': + _Negative = true; + [[fallthrough]]; + case '+': + ++_First; + break; + } + + // For a regular offset hh[mm], simply read four digits, with the option of an EOF or non-digit after + // reading two. The modified form h[h][:mm] is similar, except for the following points: + // (a) an EOF or non-digit is allowable after reading *one* digit, not two. + // (b) after reading one digit, another optional digit keeps us in the same state, except for decrementing + // the number of optional digits allowed. In this state, reading a ':' allows parsing to continue. + + int _Tz_hours = 0; + int _Tz_minutes = 0; + int _Optional_digits = 1; + for (int _Count = 0; _Count < 4; ++_Count) { + const bool _Allow_match_fail{_Count == (_Is_modified ? 1 : 2)}; + + if (_First == _Last) { + if (_Allow_match_fail) { + break; + } else { + return ios_base::eofbit | ios_base::failbit; + } + } + + const char _Ch = _Ctype_fac.narrow(*_First++); + const bool _Is_digit = ('0' <= _Ch && _Ch <= '9'); + if (_Is_modified && _Count == 1) { + if (_Ch == ':') { + continue; + } else if (_Is_digit && _Optional_digits > 0) { + _Tz_hours = 10 * _Tz_hours + (_Ch - '0'); + --_Optional_digits; + --_Count; + } else { + if (_Allow_match_fail) { + break; + } else { + return ios_base::failbit; + } + } + } else if (_Is_digit) { + auto& _Part = _Count < 2 ? _Tz_hours : _Tz_minutes; + _Part = 10 * _Part + (_Ch - '0'); + } else { + if (_Allow_match_fail) { + break; + } else { + return ios_base::failbit; + } + } + } + + _Offset = 60 * _Tz_hours + _Tz_minutes; + if (_Negative) { + _Offset = -_Offset; + } + return ios_base::goodbit; + } + + template + _NODISCARD ios_base::iostate _Get_tz_name( + _InIt& _First, const ctype& _Ctype_fac, string& _Tz_name) { + constexpr _InIt _Last{}; + _Tz_name.clear(); + while (_First != _Last) { + const char _Ch{_Ctype_fac.narrow(*_First)}; + if (_STD isalnum(static_cast(_Ch)) || _Ch == '_' || _Ch == '/' || _Ch == '-' + || _Ch == '+') { + _Tz_name.push_back(_Ch); + ++_First; + } else { + break; + } + } + return _First == _Last ? ios_base::eofbit : ios_base::goodbit; + } + + template > + _Time_parse_fields(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _FmtFirst, + basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, minutes* _Offset = nullptr, + unsigned int _Subsecond_precision = 0) { + + using _Myis = basic_istream<_CharT, _Traits>; + + const auto& _Ctype_fac = _STD use_facet>(_Istr.getloc()); + ios_base::iostate _State = ios_base::goodbit; + const _CharT* const _FmtLast = _FmtFirst + _Traits::length(_FmtFirst); + const typename _Myis::sentry _Ok{_Istr, true}; + + istreambuf_iterator _First{_Istr}; + constexpr decltype(_First) _Last{}; + + if (_Ok) { + _TRY_IO_BEGIN + for (; _FmtFirst != _FmtLast && _State == ios_base::goodbit; ++_FmtFirst) { + if (_First == _Last) { + // EOF is not an error if the remaining flags can match zero characters. + for (; _FmtFirst != _FmtLast; ++_FmtFirst) { + char _Flag{}; + if (_Ctype_fac.is(ctype_base::space, *_FmtFirst)) { + _Flag = ' '; + } else { + if (_Ctype_fac.narrow(*_FmtFirst) == '%' && ++_FmtFirst != _FmtLast) { + _Flag = _Ctype_fac.narrow(*_FmtFirst); + } + } + + if (_Flag != ' ' && _Flag != 't') { + _State |= ios_base::failbit | ios_base::eofbit; + break; + } + } + break; + } else if (_Ctype_fac.narrow(*_FmtFirst) != '%') { // match literal element + if (_Ctype_fac.is(ctype_base::space, *_FmtFirst)) { + while (_First != _Last && _Ctype_fac.is(ctype_base::space, *_First)) { + ++_First; + } + } else if (*_First == *_FmtFirst) { + ++_First; + } else { + _State |= ios_base::failbit; // bad literal match + } + } else if (++_FmtFirst == _FmtLast) { // not enough for a valid flag + _State |= ios_base::failbit; + } else { // get flag after % + char _Flag = _Ctype_fac.narrow(*_FmtFirst); + char _Modifier = '\0'; + unsigned int _Width = 0; + + if (_Flag == 'E' || _Flag == 'O') { + if (++_FmtFirst == _FmtLast) { + _State |= ios_base::failbit; + break; + } + _Modifier = _STD exchange(_Flag, _Ctype_fac.narrow(*_FmtFirst)); + } else if ('0' <= _Flag && _Flag <= '9') { + _Width = static_cast(_Flag - '0'); + while (++_FmtFirst != _FmtLast && _Ctype_fac.is(ctype_base::digit, *_FmtFirst)) { + const auto _Digit = static_cast(_Ctype_fac.narrow(*_FmtFirst) - '0'); + if (_Width > ((numeric_limits::max)() - _Digit) / 10) { + _State |= ios_base::failbit; + break; + } + _Width = 10 * _Width + _Digit; + } + if (_FmtFirst == _FmtLast) { + _State |= ios_base::failbit; + break; + } + _Flag = _Ctype_fac.narrow(*_FmtFirst); + } + + switch (_Flag) { + case 'n': // exactly one space + if (!_Ctype_fac.is(ctype_base::space, *_First++)) { + _State |= ios_base::failbit; + } + break; + + case 't': // zero or one space + if (_Ctype_fac.is(ctype_base::space, *_First)) { + ++_First; + } + break; + + case '%': + if (_Ctype_fac.narrow(*_First++) != '%') { + _State |= ios_base::failbit; + } + break; + + default: + _First = _Parse_time_field(_First, _Istr, _State, _Flag, _Modifier, _Width, + _Subsecond_precision); // convert a single field + break; + } + } + } + + _CATCH_IO_(_Myis, _Istr) + } + + if (!_Is_complete()) { + _State |= ios_base::failbit; + } + + if (!(_State & ios_base::failbit)) { + if (_Offset != nullptr && _Utc_offset) { + *_Offset = minutes{*_Utc_offset}; + } + + if (_Abbrev != nullptr && _Tz_name) { + if constexpr (is_same_v) { + *_Abbrev = _STD move(*_Tz_name); + } else { + _Abbrev->clear(); + for (const char& _Ch : *_Tz_name) { + _Abbrev->push_back(_Ctype_fac.widen(_Ch)); + } + } + } + } + + _Istr.setstate(_State); + } + }; + + // FUNCTION TEMPLATE from_stream + template > + basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt, + duration<_Rep, _Period>& _Duration, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, + minutes* _Offset = nullptr) { + _Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset, hh_mm_ss>::fractional_width}; + if (_Istr && !_Time._Make_duration(_Duration)) { + _Istr.setstate(ios_base::failbit); + } + return _Istr; + } + + template > + basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt, weekday& _Wd, + basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, minutes* _Offset = nullptr) { + _Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset}; + if (_Istr && !_Time._Make_weekday(_Wd)) { + _Istr.setstate(ios_base::failbit); + } + return _Istr; + } + + template > + basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt, day& _Day, + basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, minutes* _Offset = nullptr) { + _Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset}; + if (_Istr && !_Time._Make_day(_Day)) { + _Istr.setstate(ios_base::failbit); + } + return _Istr; + } + + template > + basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt, + month& _Month, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, minutes* _Offset = nullptr) { + _Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset}; + if (_Istr && !_Time._Make_month(_Month)) { + _Istr.setstate(ios_base::failbit); + } + return _Istr; + } + + template > + basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt, + month_day& _Md, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, minutes* _Offset = nullptr) { + _Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset}; + if (_Istr && !_Time._Make_month_day(_Md)) { + _Istr.setstate(ios_base::failbit); + } + return _Istr; + } + + template > + basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt, year& _Year, + basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, minutes* _Offset = nullptr) { + _Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset}; + if (_Istr && !_Time._Make_year(_Year)) { + _Istr.setstate(ios_base::failbit); + } + return _Istr; + } + + template > + basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt, + year_month& _Ym, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, minutes* _Offset = nullptr) { + _Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset}; + if (_Istr && !_Time._Make_year_month(_Ym)) { + _Istr.setstate(ios_base::failbit); + } + return _Istr; + } + + template > + basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt, + year_month_day& _Ymd, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, minutes* _Offset = nullptr) { + _Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset}; + if (_Istr && !_Time._Make_year_month_day(_Ymd)) { + _Istr.setstate(ios_base::failbit); + } + return _Istr; + } + + template > + basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt, + utc_time<_Duration>& _Tp, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, + minutes* _Offset = nullptr) { + _Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset}; + _Duration _Dur; + if (_Istr && _Time._Make_time_point(_Dur, _Time_parse_fields::_LeapSecondRep::_All)) { + _Tp = utc_time<_Duration>{_Dur}; + } else { + _Istr.setstate(ios_base::failbit); + } + return _Istr; + } + + template > + basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt, + sys_time<_Duration>& _Tp, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, + minutes* _Offset = nullptr) { + _Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset}; + _Duration _Dur; + if (_Istr && _Time._Make_time_point(_Dur, _Time_parse_fields::_LeapSecondRep::_Negative)) { + _Tp = sys_time<_Duration>{_Dur}; + } else { + _Istr.setstate(ios_base::failbit); + } + return _Istr; + } + + template > + basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt, + tai_time<_Duration>& _Tp, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, + minutes* _Offset = nullptr) { + _Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset}; + _Duration _Dur; + if (_Istr && _Time._Make_time_point(_Dur, _Time_parse_fields::_LeapSecondRep::_None)) { + _Tp = tai_time<_Duration>{_Dur + _CHRONO duration_cast<_Duration>(days{4383})}; + } else { + _Istr.setstate(ios_base::failbit); + } + return _Istr; + } + + template > + basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt, + gps_time<_Duration>& _Tp, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, + chrono::minutes* _Offset = nullptr) { + _Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset}; + _Duration _Dur; + if (_Istr && _Time._Make_time_point(_Dur, _Time_parse_fields::_LeapSecondRep::_None)) { + _Tp = gps_time<_Duration>{_Dur - _CHRONO duration_cast<_Duration>(days{3657})}; + } else { + _Istr.setstate(ios_base::failbit); + } + return _Istr; + } + + template > + basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt, + file_time<_Duration>& _Tp, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, + chrono::minutes* _Offset = nullptr) { + _Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset}; + _Duration _Dur; + if (_Istr && _Time._Make_time_point(_Dur, _Time_parse_fields::_LeapSecondRep::_File_time)) { + constexpr auto _File_clock_adj{_CHRONO duration_cast<_Duration>( + filesystem::_File_time_clock::duration{filesystem::__std_fs_file_time_epoch_adjustment})}; + _Tp = file_time<_Duration>{_Dur} + _File_clock_adj; + } else { + _Istr.setstate(ios_base::failbit); + } + return _Istr; + } + + template > + basic_istream<_CharT, _Traits>& from_stream(basic_istream<_CharT, _Traits>& _Istr, const _CharT* _Fmt, + local_time<_Duration>& _Tp, basic_string<_CharT, _Traits, _Alloc>* _Abbrev = nullptr, + minutes* _Offset = nullptr) { + _Time_parse_fields _Time{_Istr, _Fmt, _Abbrev, _Offset}; + _Duration _Dur; + if (_Istr && _Time._Make_time_point(_Dur, _Time_parse_fields::_LeapSecondRep::_None)) { + _Tp = local_time<_Duration>{_Dur}; + } else { + _Istr.setstate(ios_base::failbit); + } + return _Istr; + } + + template + struct _Time_parse_iomanip { + _Time_parse_iomanip(const basic_string<_CharT, _Traits, _Alloc>& _Fmt_, _Parsable& _Tp_, + basic_string<_CharT, _Traits, _Alloc>* _Abbrev_ = nullptr, minutes* _Offset_ = nullptr) + : _Fmt{_Fmt_}, _Tp{_Tp_}, _Abbrev{_Abbrev_}, _Offset{_Offset_} {} + + const basic_string<_CharT, _Traits, _Alloc>& _Fmt; + _Parsable& _Tp; + basic_string<_CharT, _Traits, _Alloc>* _Abbrev; + minutes* _Offset; + }; + + // FUNCTION TEMPLATE parse + template &>(), + _STD declval(), _STD declval<_Parsable&>()))>> + _NODISCARD auto parse(const basic_string<_CharT, _Traits, _Alloc>& _Fmt, _Parsable& _Tp) { + return _Time_parse_iomanip{_Fmt, _Tp}; + } + + template &>(), _STD declval(), + _STD declval<_Parsable&>(), _STD declval*>()))>> + _NODISCARD auto parse(const basic_string<_CharT, _Traits, _Alloc>& _Fmt, _Parsable& _Tp, + basic_string<_CharT, _Traits, _Alloc>& _Abbrev) { + return _Time_parse_iomanip{_Fmt, _Tp, _STD addressof(_Abbrev)}; + } + + template &>(), + _STD declval(), _STD declval<_Parsable&>(), + _STD declval*>(), _STD declval()))>> + _NODISCARD auto parse(const basic_string<_CharT, _Traits, _Alloc>& _Fmt, _Parsable& _Tp, minutes& _Offset) { + return _Time_parse_iomanip{_Fmt, _Tp, static_cast*>(nullptr), &_Offset}; + } + + template &>(), + _STD declval(), _STD declval<_Parsable&>(), + _STD declval*>(), _STD declval()))>> + _NODISCARD auto parse(const basic_string<_CharT, _Traits, _Alloc>& _Fmt, _Parsable& _Tp, + basic_string<_CharT, _Traits, _Alloc>& _Abbrev, minutes& _Offset) { + return _Time_parse_iomanip{_Fmt, _Tp, _STD addressof(_Abbrev), &_Offset}; + } + + template + basic_istream<_CharT, _Traits>& operator>>( + basic_istream<_CharT, _Traits>& _Is, const _Time_parse_iomanip<_CharT, _Traits, _Alloc, _Parsable>& _Tpi) { + return _CHRONO from_stream(_Is, _Tpi._Fmt.c_str(), _Tpi._Tp, _Tpi._Abbrev, _Tpi._Offset); + } #endif // _HAS_CXX20 } // namespace chrono diff --git a/stl/inc/complex b/stl/inc/complex index 5e935cf52d0..897d16463ea 100644 --- a/stl/inc/complex +++ b/stl/inc/complex @@ -20,16 +20,16 @@ // no intrinsics for /clr:pure #elif defined(__clang__) // TRANSITION, not using FMA intrinsics for Clang yet -#elif defined(_M_IX86) || defined(_M_X64) +#elif defined(_M_IX86) || (defined(_M_X64) && !defined(_M_ARM64EC)) #define _FMP_USING_X86_X64_INTRINSICS #include #include extern "C" int __isa_available; extern "C" __m128d __cdecl _mm_fmsub_sd(__m128d, __m128d, __m128d); -#elif defined(_M_ARM64) +#elif defined(_M_ARM64) || defined(_M_ARM64EC) #define _FMP_USING_ARM64_INTRINSICS #include -#endif // ^^^ defined(_M_ARM64) ^^^ +#endif // ^^^ defined(_M_ARM64) || defined(_M_ARM64EC) ^^^ #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) @@ -342,7 +342,8 @@ namespace _Math_algorithms { const _Ty _Bv_scaled_sqr = _Bv_scaled * _Bv_scaled; const _Ty _Norm_scaled = _Av_scaled * _Av_scaled + _Bv_scaled_sqr; const _Ty _Real_shifted = _STD log(_Norm_scaled) * _Ty{0.5}; - return (_Real_shifted + _Exponent * _Cl) + _Exponent * _Cm; + const auto _Fexponent = static_cast<_Ty>(_Exponent); + return (_Real_shifted + _Fexponent * _Cl) + _Fexponent * _Cm; } } } // namespace _Math_algorithms @@ -539,7 +540,7 @@ public: } static bool _Isinf(_Ty _Left) { // test for infinity -#if defined(__INTEL_COMPILER) && defined(__LONG_DOUBLE_SIZE__) && __LONG_DOUBLE_SIZE__ == 80 +#if defined(__LDBL_DIG__) && __LDBL_DIG__ == 18 return _CSTD _LDtest(&_Left) == _INFCODE; #else // ^^^ 80-bit long double (not supported by MSVC in general, see GH-1316) / 64-bit long double vvv const auto _Uint = _Bit_cast(_Left); @@ -548,7 +549,7 @@ public: } static _CONSTEXPR20 bool _Isnan(_Ty _Left) { -#if defined(__INTEL_COMPILER) && defined(__LONG_DOUBLE_SIZE__) && __LONG_DOUBLE_SIZE__ == 80 +#if defined(__LDBL_DIG__) && __LDBL_DIG__ == 18 return _CSTD _LDtest(&_Left) == _NANCODE; #else // ^^^ 80-bit long double (not supported by MSVC in general, see GH-1316) / 64-bit long double vvv const auto _Uint = _Bit_cast(_Left); diff --git a/stl/inc/coroutine b/stl/inc/coroutine index ad2c2f30dd8..a9f8f729737 100644 --- a/stl/inc/coroutine +++ b/stl/inc/coroutine @@ -11,16 +11,19 @@ #ifdef _RESUMABLE_FUNCTIONS_SUPPORTED #pragma message("The contents of are not available with /await.") -#pragma message("Remove /await for standard coroutines or use for legacy /await support.") +#pragma message("Remove /await or use /await:strict for standard coroutines.") +#pragma message("Use for legacy /await support.") #else // ^^^ /await ^^^ / vvv no /await vvv #ifndef __cpp_lib_coroutine -#pragma message("The contents of are available only with C++20 or later.") -#else // ^^^ __cpp_lib_coroutine not defined / __cpp_lib_coroutine defined vvv +#pragma message("The contents of are available only with C++20 or later or /await:strict.") +#else // ^^^ is not available / is available vvv #ifndef _ALLOW_COROUTINE_ABI_MISMATCH #pragma detect_mismatch("_COROUTINE_ABI", "2") #endif // _ALLOW_COROUTINE_ABI_MISMATCH +#if _HAS_CXX20 #include +#endif // _HAS_CXX20 #include #pragma pack(push, _CRT_PACKING) @@ -156,6 +159,7 @@ _NODISCARD constexpr bool operator==(const coroutine_handle<> _Left, const corou return _Left.address() == _Right.address(); } +#if _HAS_CXX20 _NODISCARD constexpr strong_ordering operator<=>( const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { #ifdef __cpp_lib_concepts @@ -164,6 +168,27 @@ _NODISCARD constexpr strong_ordering operator<=>( return _Left.address() <=> _Right.address(); #endif // __cpp_lib_concepts } +#else // ^^^ <=> exists / <=> does not exist vvv +_NODISCARD constexpr bool operator!=(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { + return !(_Left == _Right); +} + +_NODISCARD constexpr bool operator<(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { + return less{}(_Left.address(), _Right.address()); +} + +_NODISCARD constexpr bool operator>(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { + return _Right < _Left; +} + +_NODISCARD constexpr bool operator<=(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { + return !(_Left > _Right); +} + +_NODISCARD constexpr bool operator>=(const coroutine_handle<> _Left, const coroutine_handle<> _Right) noexcept { + return !(_Left < _Right); +} +#endif // _HAS_CXX20 template struct hash> { @@ -246,7 +271,7 @@ _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) #pragma pack(pop) -#endif // __cpp_lib_coroutine +#endif // is available #endif // _RESUMABLE_FUNCTIONS_SUPPORTED #endif // _STL_COMPILER_PREPROCESSOR #endif // _COROUTINE_ diff --git a/stl/inc/filesystem b/stl/inc/filesystem index 05341d65c4c..559220566ae 100644 --- a/stl/inc/filesystem +++ b/stl/inc/filesystem @@ -2122,20 +2122,11 @@ namespace filesystem { __std_fs_stats_flags::_Attributes | __std_fs_stats_flags::_Reparse_tag; // ALIAS file_time_type - struct _File_time_clock { // Implementation of trivial-clock - using rep = long long; - using period = chrono::system_clock::period; - using duration = chrono::duration; - using time_point = chrono::time_point<_File_time_clock>; - - static constexpr bool is_steady = false; - - _NODISCARD static time_point now() noexcept { // get current time; undo epoch adjustment - return time_point(duration(_Xtime_get_ticks() + __std_fs_file_time_epoch_adjustment)); // TRANSITION, ABI - } - }; - - using file_time_type = chrono::time_point<_File_time_clock>; +#if _HAS_CXX20 + using file_time_type = _CHRONO time_point<_CHRONO file_clock>; +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv + using file_time_type = _CHRONO time_point; +#endif // ^^^ !_HAS_CXX20 // CLASS directory_entry class directory_entry { diff --git a/stl/inc/functional b/stl/inc/functional index a8aee539270..649ab6d2a97 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -2229,20 +2229,6 @@ namespace ranges { using is_transparent = int; }; - // STRUCT ranges::greater - struct greater { - // clang-format off - template - requires totally_ordered_with<_Ty1, _Ty2> // TRANSITION, GH-489 - _NODISCARD constexpr bool operator()(_Ty1&& _Left, _Ty2&& _Right) const noexcept(noexcept( - static_cast(static_cast<_Ty2&&>(_Right) < static_cast<_Ty1&&>(_Left)))) /* strengthened */ { - return static_cast(static_cast<_Ty2&&>(_Right) < static_cast<_Ty1&&>(_Left)); - } - // clang-format on - - using is_transparent = int; - }; - // STRUCT ranges::greater_equal struct greater_equal { // clang-format off diff --git a/stl/inc/header-units.json b/stl/inc/header-units.json index d33b9abc629..f4c76b822dc 100644 --- a/stl/inc/header-units.json +++ b/stl/inc/header-units.json @@ -6,6 +6,7 @@ "BuildAsHeaderUnits": [ // "__msvc_all_public_headers.hpp", // for testing, not production "__msvc_system_error_abi.hpp", + "__msvc_tzdb.hpp", "algorithm", "any", "array", @@ -111,7 +112,7 @@ "valarray", "variant", "vector", - "version", + // "version", // importable, but provides feature-test macros only, which can control header inclusion "xatomic.h", "xatomic_wait.h", "xbit_ops.h", @@ -144,8 +145,8 @@ "xtr1common", "xtree", "xutility", - "ymath.h", - "yvals.h", - "yvals_core.h" + "ymath.h" + // "yvals.h", // internal header, provides macros that control header inclusion + // "yvals_core.h" // internal header, provides macros that control header inclusion ] } diff --git a/stl/inc/iterator b/stl/inc/iterator index 6fd2233e372..195a8dd61b0 100644 --- a/stl/inc/iterator +++ b/stl/inc/iterator @@ -1084,7 +1084,7 @@ public: } // [counted.iter.access] - _NODISCARD constexpr const _Iter& base() const& noexcept /* strengthened */ { // Per LWG-3391 + _NODISCARD constexpr const _Iter& base() const& noexcept /* strengthened */ { return _Current; } diff --git a/stl/inc/memory b/stl/inc/memory index 4fe7830d5df..9eca0290cee 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -71,15 +71,17 @@ namespace ranges { private: template _NODISCARD static uninitialized_copy_result<_It, _Out> _Uninitialized_copy_unchecked( - _It _IFirst, const _Se _ILast, _Out _OFirst, const _OSe _OLast) { + _It _IFirst, _Se _ILast, _Out _OFirst, _OSe _OLast) { _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); _STL_INTERNAL_STATIC_ASSERT(_No_throw_forward_iterator<_Out>); _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> && is_same_v<_OSe, _Out> && _Ptr_copy_cat<_It, _Out>::_Really_trivial) { - return _Copy_memcpy_common(_IFirst, _ILast, _OFirst, _OLast); + if constexpr (_Ptr_copy_cat<_It, _Out>::_Really_trivial + && sized_sentinel_for<_Se, _It> && sized_sentinel_for<_OSe, _Out>) { + return _Copy_memcpy_common(_IFirst, _RANGES next(_IFirst, _STD move(_ILast)), _OFirst, + _RANGES next(_OFirst, _STD move(_OLast))); } else { _Uninitialized_backout _Backout{_STD move(_OFirst)}; @@ -506,10 +508,14 @@ namespace ranges { // clang-format off template <_No_throw_input_iterator _It, _No_throw_sentinel_for<_It> _Se> requires destructible> - _NODISCARD _CONSTEXPR20_DYNALLOC _It _Destroy_unchecked(_It _First, const _Se _Last) noexcept { + _NODISCARD _CONSTEXPR20_DYNALLOC _It _Destroy_unchecked(_It _First, _Se _Last) noexcept { // clang-format on - for (; _First != _Last; ++_First) { - _RANGES destroy_at(_STD addressof(*_First)); + if constexpr (is_trivially_destructible_v>) { + _RANGES advance(_First, _STD move(_Last)); + } else { + for (; _First != _Last; ++_First) { + _RANGES destroy_at(_STD addressof(*_First)); + } } return _First; @@ -1145,6 +1151,12 @@ public: _Ref_count_resource(_Resource _Px, _Dx _Dt) : _Ref_count_base(), _Mypair(_One_then_variadic_args_t{}, _STD move(_Dt), _Px) {} +#ifdef __EDG__ // TRANSITION, VSO-1292293 + virtual ~_Ref_count_resource() noexcept override {} // TRANSITION, should be non-virtual +#else // ^^^ workaround / no workaround vvv + virtual ~_Ref_count_resource() noexcept override = default; // TRANSITION, should be non-virtual +#endif // ^^^ no workaround ^^^ + virtual void* _Get_deleter(const type_info& _Typeid) const noexcept override { #if _HAS_STATIC_RTTI if (_Typeid == typeid(_Dx)) { @@ -1178,6 +1190,12 @@ public: : _Ref_count_base(), _Mypair(_One_then_variadic_args_t{}, _STD move(_Dt), _One_then_variadic_args_t{}, _Ax, _Px) {} +#ifdef __EDG__ // TRANSITION, VSO-1292293 + virtual ~_Ref_count_resource_alloc() noexcept override {} // TRANSITION, should be non-virtual +#else // ^^^ workaround / no workaround vvv + virtual ~_Ref_count_resource_alloc() noexcept override = default; // TRANSITION, should be non-virtual +#endif // ^^^ no workaround ^^^ + virtual void* _Get_deleter(const type_info& _Typeid) const noexcept override { #if _HAS_STATIC_RTTI if (_Typeid == typeid(_Dx)) { @@ -2012,7 +2030,7 @@ public: } } - ~_Ref_count_obj2() { + virtual ~_Ref_count_obj2() noexcept override { // TRANSITION, should be non-virtual // nothing to do, _Storage._Value was already destroyed in _Destroy // N4849 [class.dtor]/7: @@ -2276,7 +2294,7 @@ private: _Wrap<_Element_type> _Storage; // flexible array must be last member }; - ~_Ref_count_unbounded_array() { + virtual ~_Ref_count_unbounded_array() noexcept override { // TRANSITION, should be non-virtual // nothing to do, _Ty is trivially destructible // See N4849 [class.dtor]/7. @@ -2324,7 +2342,7 @@ private: _Wrap<_Element_type> _Storage; // flexible array must be last member }; - ~_Ref_count_unbounded_array() { + virtual ~_Ref_count_unbounded_array() noexcept override { // TRANSITION, should be non-virtual // nothing to do, _Storage was already destroyed in _Destroy // See N4849 [class.dtor]/7. @@ -2363,7 +2381,7 @@ public: }; private: - ~_Ref_count_bounded_array() { + virtual ~_Ref_count_bounded_array() noexcept override { // TRANSITION, should be non-virtual // nothing to do, _Storage was already destroyed in _Destroy // See N4849 [class.dtor]/7. @@ -2449,7 +2467,7 @@ public: }; private: - ~_Ref_count_obj_alloc3() { + virtual ~_Ref_count_obj_alloc3() noexcept override { // TRANSITION, should be non-virtual // nothing to do; _Storage._Value already destroyed by _Destroy() // See N4849 [class.dtor]/7. @@ -2637,7 +2655,7 @@ private: _Wrap<_Element_type> _Storage; // flexible array must be last member }; - ~_Ref_count_unbounded_array_alloc() { + virtual ~_Ref_count_unbounded_array_alloc() noexcept override { // TRANSITION, should be non-virtual // nothing to do; _Storage._Value already destroyed by _Destroy() // See N4849 [class.dtor]/7. @@ -2698,7 +2716,7 @@ public: }; private: - ~_Ref_count_bounded_array_alloc() { + virtual ~_Ref_count_bounded_array_alloc() noexcept override { // TRANSITION, should be non-virtual // nothing to do; _Storage._Value already destroyed by _Destroy() // See N4849 [class.dtor]/7. diff --git a/stl/inc/optional b/stl/inc/optional index 81d2639a164..d54a2f1d56b 100644 --- a/stl/inc/optional +++ b/stl/inc/optional @@ -418,7 +418,7 @@ public: _NODISCARD constexpr remove_cv_t<_Ty> value_or(_Ty2&& _Right) const& { static_assert(is_convertible_v>, "The const overload of optional::value_or requires const T& to be convertible to remove_cv_t " - "([optional.observe]/18 as modified by LWG-XXXX)."); + "(N4885 [optional.observe]/17 as modified by LWG-3424)."); static_assert(is_convertible_v<_Ty2, _Ty>, "optional::value_or(U) requires U to be convertible to T (N4828 [optional.observe]/18)."); @@ -432,7 +432,7 @@ public: _NODISCARD constexpr remove_cv_t<_Ty> value_or(_Ty2&& _Right) && { static_assert(is_convertible_v<_Ty, remove_cv_t<_Ty>>, "The rvalue overload of optional::value_or requires T to be convertible to remove_cv_t " - "([optional.observe]/20 as modified by LWG-XXXX)."); + "(N4885 [optional.observe]/19 as modified by LWG-3424)."); static_assert(is_convertible_v<_Ty2, _Ty>, "optional::value_or(U) requires U to be convertible to T (N4828 [optional.observe]/20)."); diff --git a/stl/inc/random b/stl/inc/random index 4bef1ed35fa..54ad1c4a4d9 100644 --- a/stl/inc/random +++ b/stl/inc/random @@ -4376,12 +4376,12 @@ private: template result_type _Eval(_Engine& _Eng, const param_type& _Par0) const { double _Vx1; - gamma_distribution dist1( + gamma_distribution _Dist1( static_cast(_Par0._Kx), static_cast((_Ty{1} - _Par0._Px) / _Par0._Px)); - _Vx1 = dist1(_Eng); - poisson_distribution<_Ty> dist2(_Vx1); + _Vx1 = _Dist1(_Eng); + poisson_distribution<_Ty> _Dist2(_Vx1); - return dist2(_Eng); + return _Dist2(_Eng); } param_type _Par; diff --git a/stl/inc/ranges b/stl/inc/ranges index 367648ffe80..08e1bfc39a5 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -1334,7 +1334,6 @@ namespace ranges { template <_Copy_constructible_object _Pr> _NODISCARD constexpr auto operator()(_Pr _Pred) const noexcept(is_nothrow_move_constructible_v<_Pr>) { - // clang-format on return _Partial<_Pr>{._Pred = {in_place, _STD move(_Pred)}}; } }; @@ -1634,7 +1633,7 @@ namespace ranges { } friend constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) _NOEXCEPT_IDL0(noexcept( - ranges::iter_swap(_Left._Current, _Right._Current))) requires indirectly_swappable> { + _RANGES iter_swap(_Left._Current, _Right._Current))) requires indirectly_swappable> { #if _ITERATOR_DEBUG_LEVEL != 0 _Left._Check_dereference(); _Right._Check_dereference(); @@ -2490,51 +2489,6 @@ namespace ranges { }; inline constexpr _Drop_fn drop; - - // VARIABLE views::counted - class _Counted_fn { - private: - enum class _St { _Span, _Subrange, _Subrange_counted }; - - template - _NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept { - _STL_INTERNAL_STATIC_ASSERT(input_or_output_iterator<_It>); - if constexpr (contiguous_iterator<_It>) { - return {_St::_Span, noexcept(span{_STD to_address(_STD declval<_It>()), iter_difference_t<_It>{}})}; - } else if constexpr (random_access_iterator<_It>) { - return {_St::_Subrange, - noexcept(subrange{_STD declval<_It>(), _STD declval<_It>() + iter_difference_t<_It>{}})}; - } else { - return {_St::_Subrange_counted, - noexcept(subrange{ - counted_iterator{_STD declval<_It>(), iter_difference_t<_It>{}}, default_sentinel})}; - } - } - - template - static constexpr _Choice_t<_St> _Choice = _Choose<_It>(); - - public: - // clang-format off - template - requires input_or_output_iterator> - _NODISCARD constexpr auto operator()(_It&& _First, const iter_difference_t> _Count) const - noexcept(_Choice>._No_throw) { - // clang-format on - _STL_ASSERT(_Count >= 0, "The size passed to views::counted must be non-negative"); - constexpr _St _Strat = _Choice>._Strategy; - - if constexpr (_Strat == _St::_Span) { - return span{_STD to_address(_STD forward<_It>(_First)), static_cast(_Count)}; - } else if constexpr (_Strat == _St::_Subrange) { - return subrange{_First, _First + _Count}; - } else if constexpr (_Strat == _St::_Subrange_counted) { - return subrange{counted_iterator{_STD forward<_It>(_First), _Count}, default_sentinel}; - } - } - }; - - inline constexpr _Counted_fn counted; } // namespace views // CLASS TEMPLATE ranges::drop_while_view @@ -2571,7 +2525,8 @@ namespace ranges { _NODISCARD constexpr auto begin() { #if _CONTAINER_DEBUG_LEVEL > 0 - _STL_VERIFY(_Pred, "LWG-3490 forbids calling begin on a drop_while_view with no predicate"); + _STL_VERIFY( + _Pred, "N4885 [range.drop.while.view] forbids calling begin on a drop_while_view with no predicate"); #endif // _CONTAINER_DEBUG_LEVEL > 0 if constexpr (forward_range<_Vw>) { if (this->_Has_cache()) { @@ -2635,6 +2590,848 @@ namespace ranges { inline constexpr _Drop_while_fn drop_while; } // namespace views + // CLASS TEMPLATE ranges::join_view + // clang-format off + template + requires view<_Vw> && input_range> + && (is_reference_v> || view>) + class join_view; + // clang-format on + + template + class _Join_view_base : public view_interface> { + protected: + /* [[no_unique_address]] */ views::all_t> _Inner{}; + }; + + // clang-format off + template + requires is_reference_v> + class _Join_view_base<_Vw> : public view_interface> {}; + + template + requires view<_Vw> && input_range> + && (is_reference_v> || view>) + class join_view : public _Join_view_base<_Vw> { + // clang-format on + private: + template + using _InnerRng = range_reference_t<_Maybe_const<_Const, _Vw>>; + /* [[no_unique_address]] */ _Vw _Range{}; + + template + class _Sentinel; + + template // TRANSITION, LWG-3289 + struct _Category_base {}; + + // clang-format off + template + requires _Has_member_iterator_category<_OuterTraits> && _Has_member_iterator_category<_InnerTraits> + struct _Category_base<_OuterTraits, _InnerTraits, _Deref_is_glvalue, _Inner_common> { + using iterator_category = + conditional_t<_Deref_is_glvalue && _Inner_common // per LWG issue unnumbered as of 2021-03-16 + && derived_from + && derived_from, + bidirectional_iterator_tag, + conditional_t<_Deref_is_glvalue + && derived_from + && derived_from, + forward_iterator_tag, + conditional_t + && derived_from, + input_iterator_tag, + output_iterator_tag>>>; + }; + // clang-format on + + template + class _Iterator : public _Category_base>>, + iterator_traits>>, is_reference_v<_InnerRng<_Const>>, + common_range<_InnerRng<_Const>>> { + private: + template + friend class _Iterator; + template + friend class _Sentinel; + + using _Parent_t = _Maybe_const<_Const, join_view>; + using _Base = _Maybe_const<_Const, _Vw>; + using _OuterIter = iterator_t<_Base>; + using _InnerIter = iterator_t<_InnerRng<_Const>>; + + // True if and only if the expression *i, where i is an iterator from the outer range, is a glvalue: + static constexpr bool _Deref_is_glvalue = is_reference_v<_InnerRng<_Const>>; + + /* [[no_unique_address]] */ _OuterIter _Outer{}; + /* [[no_unique_address]] */ _InnerIter _Inner{}; + _Parent_t* _Parent{}; + + constexpr auto& _Update_inner(_InnerRng<_Const> _Range) { + if constexpr (_Deref_is_glvalue) { + return _Range; + } else { + _Parent->_Inner = views::all(_STD move(_Range)); + return _Parent->_Inner; + } + } + + constexpr void _Satisfy() { + const auto _Last = _RANGES end(_Parent->_Range); + for (; _Outer != _Last; ++_Outer) { + auto& _Tmp = _Update_inner(*_Outer); + _Inner = _RANGES begin(_Tmp); + if (_Inner != _RANGES end(_Tmp)) { + return; + } + } + if constexpr (_Deref_is_glvalue) { + _Inner = _InnerIter{}; + } + } + +#if _ITERATOR_DEBUG_LEVEL != 0 + constexpr void _Check_dereference() const noexcept { + _STL_VERIFY(_Parent != nullptr, "cannot dereference value-initialized join_view iterator"); + _STL_VERIFY(_Outer != _RANGES end(_Parent->_Range), "cannot dereference join_view end iterator"); + sentinel_t<_InnerRng> _Last; + if constexpr (_Deref_is_glvalue) { + _Last = _RANGES end(*_Outer); + } else { + _Last = _RANGES end(_Parent->_Inner); + } + _STL_VERIFY(_Inner != _Last, "cannot dereference join_view end iterator"); + } + + constexpr void _Same_range(const _Iterator& _Right) const noexcept { + _STL_VERIFY(_Parent == _Right._Parent, "cannot compare incompatible join_view iterators"); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + + public: + // clang-format off + // Per LWG issue unnumbered as of 2021-03-16 + using iterator_concept = conditional_t<_Deref_is_glvalue + && bidirectional_range<_Base> && bidirectional_range<_InnerRng<_Const>> + && common_range<_InnerRng<_Const>>, bidirectional_iterator_tag, + conditional_t<_Deref_is_glvalue && forward_range<_Base> && forward_range<_InnerRng<_Const>>, + forward_iterator_tag, input_iterator_tag>>; + // clang-format on + using value_type = range_value_t<_InnerRng<_Const>>; + using difference_type = common_type_t, range_difference_t<_InnerRng<_Const>>>; + + _Iterator() = default; + + constexpr _Iterator(_Parent_t& _Parent_, _OuterIter _Outer_) + : _Outer{_STD move(_Outer_)}, _Parent{_STD addressof(_Parent_)} { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Adl_verify_range(_Outer, _RANGES end(_Parent_._Range)); + if constexpr (forward_range<_Base>) { + _Adl_verify_range(_RANGES begin(_Parent_._Range), _Outer); + } +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _Satisfy(); + } + + // clang-format off + constexpr _Iterator(_Iterator _It) + requires _Const && convertible_to, _OuterIter> + && convertible_to>, _InnerIter> + : _Outer{_STD move(_It._Outer)}, _Inner{_STD move(_It._Inner)}, _Parent{_It._Parent} {} + // clang-format on + + _NODISCARD constexpr decltype(auto) operator*() const noexcept(noexcept(*_Inner)) /* strengthened */ { +#if _ITERATOR_DEBUG_LEVEL != 0 + _Check_dereference(); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return *_Inner; + } + + // clang-format off + _NODISCARD constexpr _InnerIter operator->() const noexcept(is_nothrow_copy_constructible_v<_InnerIter>) + /* strengthened */ requires _Has_arrow<_InnerIter> && copyable<_InnerIter> { + // clang-format on +#if _ITERATOR_DEBUG_LEVEL != 0 + _Check_dereference(); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _Inner; + } + + constexpr _Iterator& operator++() { + if constexpr (_Deref_is_glvalue) { + if (++_Inner == _RANGES end(*_Outer)) { + ++_Outer; + _Satisfy(); + } + } else { + if (++_Inner == _RANGES end(_Parent->_Inner)) { + ++_Outer; + _Satisfy(); + } + } + return *this; + } + + constexpr decltype(auto) operator++(int) { + if constexpr (_Deref_is_glvalue && forward_range<_Base> && forward_range<_InnerRng<_Const>>) { + auto _Tmp = *this; + ++*this; + return _Tmp; + } else { + ++*this; + } + } + + // clang-format off + constexpr _Iterator& operator--() + requires _Deref_is_glvalue && bidirectional_range<_Base> && bidirectional_range<_InnerRng<_Const>> + && common_range<_InnerRng<_Const>> { + // clang-format on + if (_Outer == _RANGES end(_Parent->_Range)) { + --_Outer; + _Inner = _RANGES end(*_Outer); + } + while (_Inner == _RANGES begin(*_Outer)) { + --_Outer; + _Inner = _RANGES end(*_Outer); + } + --_Inner; + return *this; + } + + // clang-format off + constexpr _Iterator operator--(int) + requires _Deref_is_glvalue && bidirectional_range<_Base> && bidirectional_range<_InnerRng<_Const>> + && common_range<_InnerRng<_Const>> { + // clang-format on + auto _Tmp = *this; + --*this; + return _Tmp; + } + + // clang-format off + _NODISCARD friend constexpr bool operator==(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_Implicitly_convert_to(_Left._Outer == _Right._Outer + && _Left._Inner == _Right._Inner))) /* strengthened */ + requires _Deref_is_glvalue && equality_comparable<_OuterIter> && equality_comparable<_InnerIter> { + // clang-format on +#if _ITERATOR_DEBUG_LEVEL != 0 + _Left._Same_range(_Right); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _Left._Outer == _Right._Outer && _Left._Inner == _Right._Inner; + } + + _NODISCARD friend constexpr decltype(auto) iter_move(const _Iterator& _It) noexcept( + noexcept(_RANGES iter_move(_It._Inner))) { +#if _ITERATOR_DEBUG_LEVEL != 0 + _It._Check_dereference(); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + return _RANGES iter_move(_It._Inner); + } + + // clang-format off + friend constexpr void iter_swap(const _Iterator& _Left, const _Iterator& _Right) noexcept( + noexcept(_RANGES iter_swap(_Left._Inner, _Right._Inner))) + requires indirectly_swappable<_InnerIter> { // per LWG-3517 + // clang-format on +#if _ITERATOR_DEBUG_LEVEL != 0 + _Left._Check_dereference(); + _Right._Check_dereference(); +#endif // _ITERATOR_DEBUG_LEVEL != 0 + _RANGES iter_swap(_Left._Inner, _Right._Inner); + } + }; + + template + class _Sentinel { + private: + template + friend class _Iterator; + template + friend class _Sentinel; + + using _Parent_t = _Maybe_const<_Const, join_view>; + using _Base = _Maybe_const<_Const, _Vw>; + + template + using _Maybe_const_iter = iterator_t<_Maybe_const<_OtherConst, _Vw>>; + + /* [[no_unique_address]] */ sentinel_t<_Base> _Last{}; + + // clang-format off + template + requires sentinel_for, _Maybe_const_iter<_OtherConst>> + _NODISCARD constexpr bool _Equal(const _Iterator<_OtherConst>& _It) const noexcept( + noexcept(_Implicitly_convert_to(_It._Outer == _Last))) { + // clang-format on + return _It._Outer == _Last; + } + + public: + _Sentinel() = default; + constexpr explicit _Sentinel(_Parent_t& _Parent) noexcept( + noexcept(_RANGES end(_Parent._Range)) + && is_nothrow_move_constructible_v>) // strengthened + : _Last(_RANGES end(_Parent._Range)) {} + + // clang-format off + constexpr _Sentinel(_Sentinel _Se) + noexcept(is_nothrow_constructible_v, sentinel_t<_Vw>>) // strengthened + requires _Const && convertible_to, sentinel_t<_Base>> + : _Last(_STD move(_Se._Last)) {} + + template + requires sentinel_for, _Maybe_const_iter<_OtherConst>> + _NODISCARD friend constexpr bool operator==(const _Iterator<_OtherConst>& _Left, + const _Sentinel& _Right) noexcept(noexcept(_Right._Equal(_Left))) /* strengthened */ { + // clang-format on + return _Right._Equal(_Left); + } + }; + + public: + join_view() = default; + constexpr explicit join_view(_Vw _Range_) noexcept(is_nothrow_move_constructible_v<_Vw>) // strengthened + : _Range(_STD move(_Range_)) {} + + _NODISCARD constexpr _Vw base() const& noexcept( + is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ requires copy_constructible<_Vw> { + return _Range; + } + _NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { + return _STD move(_Range); + } + + _NODISCARD constexpr auto begin() { + constexpr bool _Use_const = _Simple_view<_Vw> && is_reference_v<_InnerRng>; + return _Iterator<_Use_const>{*this, _RANGES begin(_Range)}; + } + + // clang-format off + _NODISCARD constexpr _Iterator begin() const + requires input_range && is_reference_v<_InnerRng> { + // clang-format on + return _Iterator{*this, _RANGES begin(_Range)}; + } + + // clang-format off + _NODISCARD constexpr auto end() { + if constexpr (forward_range<_Vw> && is_reference_v<_InnerRng> + && forward_range<_InnerRng> && common_range<_Vw> && common_range<_InnerRng>) { + // clang-format on + return _Iterator<_Simple_view<_Vw>>{*this, _RANGES end(_Range)}; + } else { + return _Sentinel<_Simple_view<_Vw>>{*this}; + } + } + + // clang-format off + _NODISCARD constexpr auto end() const + requires input_range && is_reference_v<_InnerRng> { + if constexpr (forward_range && is_reference_v<_InnerRng> + && forward_range<_InnerRng> && common_range + && common_range<_InnerRng>) { + // clang-format on + return _Iterator{*this, _RANGES end(_Range)}; + } else { + return _Sentinel{*this}; + } + } + }; + + template + explicit join_view(_Rng&&) -> join_view>; + + namespace views { + // VARIABLE views::join + class _Join_fn : public _Pipe::_Base<_Join_fn> { + public: + // clang-format off + template + _NODISCARD constexpr auto operator()(_Rng&& _Range) const noexcept(noexcept( + join_view>{_STD forward<_Rng>(_Range)})) requires requires { + join_view>{static_cast<_Rng&&>(_Range)}; + } { + // clang-format on + return join_view>{_STD forward<_Rng>(_Range)}; + } + }; + + inline constexpr _Join_fn join; + } // namespace views + + // CLASS TEMPLATE ranges::split_view + // clang-format off + template + concept _Tiny_range = sized_range<_Ty> + && requires { typename _Require_constant::size()>; } + && (remove_reference_t<_Ty>::size() <= 1); + + template + requires view<_Vw> && view<_Pat> + && indirectly_comparable, iterator_t<_Pat>, _RANGES equal_to> + && (forward_range<_Vw> || _Tiny_range<_Pat>) + class split_view; + // clang-format on + + template + class _Split_view_base : public view_interface> { + protected: + /* [[no_unique_address]] */ iterator_t<_Vw> _Current{}; + }; + + template + class _Split_view_base<_Vw, _Pat> : public view_interface> {}; + + // clang-format off + template + requires view<_Vw> && view<_Pat> + && indirectly_comparable, iterator_t<_Pat>, _RANGES equal_to> + && (forward_range<_Vw> || _Tiny_range<_Pat>) + class split_view : public _Split_view_base<_Vw, _Pat> { + // clang-format on + private: + /* [[no_unique_address]] */ _Vw _Range{}; + /* [[no_unique_address]] */ _Pat _Pattern{}; + + template + class _Inner_iter; + + template + class _Outer_iter_base {}; + + // clang-format off + template + class _Outer_iter_base<_Iter> { + // clang-format on + protected: + _Iter _Current{}; + + public: + using iterator_category = input_iterator_tag; + + _Outer_iter_base() = default; + constexpr explicit _Outer_iter_base(_Iter _Current_) noexcept(is_nothrow_move_constructible_v<_Iter>) + : _Current{_STD move(_Current_)} {} + }; + + template + class _Outer_iter : public _Outer_iter_base>> { + private: + template + friend class _Inner_iter; + friend _Outer_iter; + + using _Mybase = _Outer_iter_base>>; + using _ParentTy = _Maybe_const<_Const, split_view>; + using _BaseTy = _Maybe_const<_Const, _Vw>; + + _ParentTy* _Parent = nullptr; + + _NODISCARD constexpr iterator_t<_BaseTy>& _Get_current() noexcept { + if constexpr (forward_range<_BaseTy>) { + return this->_Current; + } else { + return _Parent->_Current; + } + } + + _NODISCARD constexpr const iterator_t<_BaseTy>& _Get_current() const noexcept { + if constexpr (forward_range<_BaseTy>) { + return this->_Current; + } else { + return _Parent->_Current; + } + } + + _NODISCARD constexpr bool _At_end() const + noexcept(noexcept(_Implicitly_convert_to(_Get_current() == _RANGES end(_Parent->_Range)))) { + return _Get_current() == _RANGES end(_Parent->_Range); + } + + public: + using iterator_concept = conditional_t, forward_iterator_tag, input_iterator_tag>; + using difference_type = range_difference_t<_BaseTy>; + + class value_type : public view_interface { + private: + /* [[no_unique_address]] */ _Outer_iter _First{}; + + public: + value_type() = default; + constexpr explicit value_type(_Outer_iter _First_) noexcept( + is_nothrow_move_constructible_v<_Outer_iter>) // strengthened + : _First{_STD move(_First_)} {} + + _NODISCARD constexpr auto begin() const requires copyable<_Outer_iter> { + return _Inner_iter<_Const>{_First}; + } + + // clang-format off + _NODISCARD constexpr auto begin() requires (!copyable<_Outer_iter>) { + return _Inner_iter<_Const>{_STD move(_First)}; + } + // clang-format on + + _NODISCARD constexpr default_sentinel_t end() const noexcept { + return default_sentinel; + } + }; + + _Outer_iter() = default; + + // clang-format off + constexpr explicit _Outer_iter(_ParentTy& _Parent_) noexcept // strengthened + requires (!forward_range<_BaseTy>) : _Parent{_STD addressof(_Parent_)} {} + // clang-format on + + constexpr _Outer_iter(_ParentTy& _Parent_, iterator_t<_BaseTy> _Current_) noexcept( + is_nothrow_move_constructible_v>) // strengthened + requires forward_range<_BaseTy> + : _Mybase{_STD move(_Current_)}, _Parent{_STD addressof(_Parent_)} {} + + // clang-format off + constexpr _Outer_iter(_Outer_iter _It) + requires _Const && convertible_to, iterator_t<_BaseTy>> + : _Mybase{_STD move(_It._Current)}, _Parent{_It._Parent} {} + // clang-format on + + _NODISCARD constexpr auto operator*() const noexcept(noexcept(value_type{*this})) /* strengthened */ { + return value_type{*this}; + } + + constexpr _Outer_iter& operator++() { + const auto _End = _RANGES end(_Parent->_Range); + auto& _Cur = _Get_current(); + if (_Cur == _End) { + return *this; + } + + const auto _Pat_first = _RANGES begin(_Parent->_Pattern); + const auto _Pat_last = _RANGES end(_Parent->_Pattern); + if (_Pat_first == _Pat_last) { + ++_Cur; + } else if constexpr (_Tiny_range<_Pat>) { + _Cur = _RANGES _Find_unchecked(_STD move(_Cur), _End, *_Pat_first); + if (_Cur != _End) { + ++_Cur; + } + } else { + do { + auto _Result = _RANGES mismatch(_Cur, _End, _Pat_first, _Pat_last); + if (_Result.in2 == _Pat_last) { // pattern matches + _Cur = _STD move(_Result.in1); + break; + } + } while (++_Cur != _End); + } + return *this; + } + + constexpr decltype(auto) operator++(int) { + if constexpr (forward_range<_BaseTy>) { + auto _Tmp = *this; + ++*this; + return _Tmp; + } else { + ++*this; + } + } + + _NODISCARD friend constexpr bool operator==(const _Outer_iter& _Left, const _Outer_iter& _Right) noexcept( + noexcept(_Left._Current == _Right._Current)) /* strengthened */ requires forward_range<_BaseTy> { + return _Left._Current == _Right._Current; + } + _NODISCARD friend constexpr bool operator==(const _Outer_iter& _Left, default_sentinel_t) noexcept( + noexcept(_Left._At_end())) /* strengthened */ { + return _Left._At_end(); + } + }; + + template + class _Inner_iter_base {}; + + template + class _Inner_iter_base<_BaseTy> { + private: + using _BaseCategory = typename iterator_traits>::iterator_category; + + public: + using iterator_category = + conditional_t, forward_iterator_tag, _BaseCategory>; + }; + + template + class _Inner_iter : public _Inner_iter_base<_Maybe_const<_Const, _Vw>> { + private: + using _BaseTy = _Maybe_const<_Const, _Vw>; + + _Outer_iter<_Const> _It{}; + bool _Incremented = false; + + _NODISCARD constexpr bool _Equal(const _Inner_iter& _Right) const { + return _It._Get_current() == _Right._It._Get_current(); + } + + _NODISCARD constexpr iterator_t<_BaseTy>& _Get_current() noexcept { + return _It._Get_current(); + } + + _NODISCARD constexpr const iterator_t<_BaseTy>& _Get_current() const noexcept { + return _It._Get_current(); + } + + _NODISCARD constexpr bool _At_end() const { + auto _Pat_pos = _RANGES begin(_It._Parent->_Pattern); + const auto _Pat_end = _RANGES end(_It._Parent->_Pattern); + auto _Last = _RANGES end(_It._Parent->_Range); + if constexpr (_Tiny_range<_Pat>) { + const auto& _Cur = _It._Get_current(); // Intentionally a reference. Since _Pat is tiny, this could + // be a move-only iterator type. + if (_Cur == _Last) { + return true; + } + + if (_Pat_pos == _Pat_end) { + return _Incremented; + } + return *_Cur == *_Pat_pos; + } else { + auto _Cur = _It._Get_current(); // Intentionally not a reference. _Pat is not tiny, so this is a + // forward (copyable) iterator. + if (_Cur == _Last) { + return true; + } + + if (_Pat_pos == _Pat_end) { + return _Incremented; + } + + do { + if (*_Cur != *_Pat_pos) { + return false; + } + + if (++_Pat_pos == _Pat_end) { + return true; + } + } while (++_Cur != _Last); + return false; + } + } + + + public: + using iterator_concept = typename _Outer_iter<_Const>::iterator_concept; + using value_type = range_value_t<_BaseTy>; + using difference_type = range_difference_t<_BaseTy>; + + _Inner_iter() = default; + constexpr explicit _Inner_iter(_Outer_iter<_Const> _It_) noexcept( + is_nothrow_move_constructible_v<_Outer_iter<_Const>>) // strengthened + : _It{_STD move(_It_)} {} + + _NODISCARD constexpr decltype(auto) operator*() const { + return *_It._Get_current(); + } + + constexpr _Inner_iter& operator++() { + _Incremented = true; + if constexpr (!forward_range<_BaseTy>) { + if constexpr (_Pat::size() == 0) { + return *this; + } + } + ++_It._Get_current(); + return *this; + } + + constexpr decltype(auto) operator++(int) { + if constexpr (forward_range<_BaseTy>) { // per LWG-3532 + auto _Tmp = *this; + ++*this; + return _Tmp; + } else { + ++*this; + } + } + + _NODISCARD friend constexpr bool operator==( + const _Inner_iter& _Left, const _Inner_iter& _Right) requires forward_range<_BaseTy> { + return _Left._Equal(_Right); + } + + _NODISCARD friend constexpr bool operator==(const _Inner_iter& _Left, default_sentinel_t) { + return _Left._At_end(); + } + + _NODISCARD friend constexpr decltype(auto) iter_move(const _Inner_iter& _Iter) noexcept( + noexcept(_RANGES iter_move(_Iter._Get_current()))) { + return _RANGES iter_move(_Iter._Get_current()); + } + + // clang-format off + friend constexpr void iter_swap(const _Inner_iter& _Left, const _Inner_iter& _Right) + noexcept(noexcept(_RANGES iter_swap(_Left._Get_current(), _Right._Get_current()))) + requires indirectly_swappable> { + // clang-format on + _RANGES iter_swap(_Left._Get_current(), _Right._Get_current()); + } + }; + + public: + split_view() = default; + constexpr split_view(_Vw _Range_, _Pat _Pattern_) noexcept( + is_nothrow_move_constructible_v<_Vw>&& is_nothrow_move_constructible_v<_Pat>) // strengthened + : _Range(_STD move(_Range_)), _Pattern(_STD move(_Pattern_)) {} + + // clang-format off + template + requires constructible_from<_Vw, views::all_t<_Rng>> + && constructible_from<_Pat, single_view>> + constexpr split_view(_Rng&& _Range_, range_value_t<_Rng> _Elem) + noexcept(noexcept(_Vw(views::all(_STD forward<_Rng>(_Range_)))) + && noexcept(_Pat(single_view{_STD move(_Elem)}))) // strengthened + : _Range(views::all(_STD forward<_Rng>(_Range_))), _Pattern(single_view{_STD move(_Elem)}) {} + // clang-format on + + _NODISCARD constexpr _Vw base() const& noexcept(is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ + requires copy_constructible<_Vw> { + return _Range; + } + _NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { + return _STD move(_Range); + } + + _NODISCARD constexpr auto begin() { + if constexpr (forward_range<_Vw>) { + return _Outer_iter<_Simple_view<_Vw>>{*this, _RANGES begin(_Range)}; + } else { + this->_Current = _RANGES begin(_Range); + return _Outer_iter{*this}; + } + } + + // clang-format off + _NODISCARD constexpr auto begin() const requires forward_range<_Vw> && forward_range { + // clang-format on + return _Outer_iter{*this, _RANGES begin(_Range)}; + } + + // clang-format off + _NODISCARD constexpr auto end() requires forward_range<_Vw> && common_range<_Vw> { + // clang-format on + return _Outer_iter<_Simple_view<_Vw>>{*this, _RANGES end(_Range)}; + } + + _NODISCARD constexpr auto end() const { + if constexpr (forward_range<_Vw> && forward_range && common_range) { + return _Outer_iter{*this, _RANGES end(_Range)}; + } else { + return default_sentinel; + } + } + }; + + template + split_view(_Rng&&, _Pat&&) -> split_view, views::all_t<_Pat>>; + + template + split_view(_Rng&&, range_value_t<_Rng>) -> split_view, single_view>>; + + namespace views { + // VARIABLE views::split + class _Split_fn { + private: + template + struct _Partial : _Pipe::_Base<_Partial<_Delim>> { + /* [[no_unique_address]] */ _Delim _Delimiter; + + template + _NODISCARD constexpr auto operator()(_Rng&& _Range) const& noexcept( + noexcept(split_view{_STD forward<_Rng>(_Range), _Delimiter})) requires requires { + split_view{static_cast<_Rng&&>(_Range), _Delimiter}; + } + { return split_view{_STD forward<_Rng>(_Range), _Delimiter}; } + + template + _NODISCARD constexpr auto operator()(_Rng&& _Range) && noexcept(noexcept( + split_view{_STD forward<_Rng>(_Range), _STD forward<_Delim>(_Delimiter)})) requires requires { + split_view{static_cast<_Rng&&>(_Range), static_cast<_Delim&&>(_Delimiter)}; + } + { return split_view{_STD forward<_Rng>(_Range), _STD forward<_Delim>(_Delimiter)}; } + }; + + public: + // clang-format off + template + _NODISCARD constexpr auto operator()(_Rng&& _Range, _Pat&& _Pattern) const noexcept(noexcept( + split_view{_STD forward<_Rng>(_Range), _STD forward<_Pat>(_Pattern)})) requires requires { + split_view{static_cast<_Rng&&>(_Range), static_cast<_Pat&&>(_Pattern)}; + } { + // clang-format on + return split_view{_STD forward<_Rng>(_Range), _STD forward<_Pat>(_Pattern)}; + } + + // clang-format off + template + requires is_lvalue_reference_v<_Delim> || move_constructible<_Delim> + _NODISCARD constexpr auto operator()(_Delim&& _Delimiter) const + noexcept(is_lvalue_reference_v<_Delim> || is_nothrow_move_constructible_v<_Delim>) { + // clang-format on + return _Partial<_Delim>{._Delimiter = _STD forward<_Delim>(_Delimiter)}; + } + }; + + inline constexpr _Split_fn split; + + // VARIABLE views::counted + class _Counted_fn { + private: + enum class _St { _Span, _Subrange, _Subrange_counted }; + + template + _NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept { + _STL_INTERNAL_STATIC_ASSERT(input_or_output_iterator<_It>); + if constexpr (contiguous_iterator<_It>) { + return {_St::_Span, noexcept(span{_STD to_address(_STD declval<_It>()), iter_difference_t<_It>{}})}; + } else if constexpr (random_access_iterator<_It>) { + return {_St::_Subrange, + noexcept(subrange{_STD declval<_It>(), _STD declval<_It>() + iter_difference_t<_It>{}})}; + } else { + return {_St::_Subrange_counted, + noexcept(subrange{ + counted_iterator{_STD declval<_It>(), iter_difference_t<_It>{}}, default_sentinel})}; + } + } + + template + static constexpr _Choice_t<_St> _Choice = _Choose<_It>(); + + public: + // clang-format off + template + requires input_or_output_iterator> + _NODISCARD constexpr auto operator()(_It&& _First, const iter_difference_t> _Count) const + noexcept(_Choice>._No_throw) { + // clang-format on + _STL_ASSERT(_Count >= 0, "The size passed to views::counted must be non-negative"); + constexpr _St _Strat = _Choice>._Strategy; + + if constexpr (_Strat == _St::_Span) { + return span{_STD to_address(_STD forward<_It>(_First)), static_cast(_Count)}; + } else if constexpr (_Strat == _St::_Subrange) { + return subrange{_First, _First + _Count}; + } else if constexpr (_Strat == _St::_Subrange_counted) { + return subrange{counted_iterator{_STD forward<_It>(_First), _Count}, default_sentinel}; + } + } + }; + + inline constexpr _Counted_fn counted; + } // namespace views + // CLASS TEMPLATE ranges::common_view // clang-format off template @@ -2980,7 +3777,6 @@ namespace ranges { } constexpr void operator++(int) noexcept(noexcept(++_Current)) /* strengthened */ { - // Constraint removed per LWG-3492 ++_Current; } diff --git a/stl/inc/sstream b/stl/inc/sstream index 90ce15f8ea0..51763b89b6e 100644 --- a/stl/inc/sstream +++ b/stl/inc/sstream @@ -168,7 +168,7 @@ public: } #if _HAS_CXX20 - template <_Allocator _Alloc2> + template ::value, int> = 0> _NODISCARD basic_string<_Elem, _Traits, _Alloc2> str(const _Alloc2& _Al) const { return basic_string<_Elem, _Traits, _Alloc2>{view(), _Al}; } @@ -632,7 +632,7 @@ public: } #if _HAS_CXX20 - template <_Allocator _Alloc2> + template ::value, int> = 0> _NODISCARD basic_string<_Elem, _Traits, _Alloc2> str(const _Alloc2& _Al) const { return _Stringbuffer.str(_Al); } @@ -752,7 +752,7 @@ public: } #if _HAS_CXX20 - template <_Allocator _Alloc2> + template ::value, int> = 0> _NODISCARD basic_string<_Elem, _Traits, _Alloc2> str(const _Alloc2& _Al) const { return _Stringbuffer.str(_Al); } @@ -878,7 +878,7 @@ public: } #if _HAS_CXX20 - template <_Allocator _Alloc2> + template ::value, int> = 0> _NODISCARD basic_string<_Elem, _Traits, _Alloc2> str(const _Alloc2& _Al) const { return _Stringbuffer.str(_Al); } diff --git a/stl/inc/strstream b/stl/inc/strstream index edda1c01ca2..d90ed93ca24 100644 --- a/stl/inc/strstream +++ b/stl/inc/strstream @@ -251,7 +251,7 @@ protected: const auto _Seeklow = eback(); const auto _Seekdist = _Seekhigh - _Seeklow; - // [depr.strstreambuf.virtuals]/15 effectively says check that the result will be in range + // N4727 [depr.strstreambuf.virtuals]/15 effectively says check that the result will be in range // [_Seeklow, _Seekhigh]; but we want to calculate this without potential integer overflow switch (_Way) { case ios_base::beg: diff --git a/stl/inc/xfilesystem_abi.h b/stl/inc/xfilesystem_abi.h index 52f8c8f7404..052af6cc934 100644 --- a/stl/inc/xfilesystem_abi.h +++ b/stl/inc/xfilesystem_abi.h @@ -19,9 +19,8 @@ _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new -inline constexpr size_t __std_fs_max_path = 260; // #define MAX_PATH 260 -inline constexpr size_t __std_fs_temp_path_max = __std_fs_max_path + 1; -inline constexpr long long __std_fs_file_time_epoch_adjustment = 0x19DB1DED53E8000LL; // TRANSITION, ABI +inline constexpr size_t __std_fs_max_path = 260; // #define MAX_PATH 260 +inline constexpr size_t __std_fs_temp_path_max = __std_fs_max_path + 1; enum class __std_win_error : unsigned long { _Success = 0, // #define ERROR_SUCCESS 0L diff --git a/stl/inc/xmemory b/stl/inc/xmemory index ca3bb9bc9bf..49828d83b0e 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1483,10 +1483,6 @@ struct _NODISCARD _Uninitialized_backout { #ifdef __cpp_lib_concepts namespace ranges { - // CONCEPT _Convertible_from - template - concept _Convertible_from = convertible_to<_From, _To>; - // STRUCT TEMPLATE in_out_result template struct in_out_result { @@ -1561,13 +1557,28 @@ namespace ranges { template in_out_result<_InIt, _OutIt> _Copy_memcpy_common( _InIt _IFirst, _InIt _ILast, _OutIt _OFirst, _OutIt _OLast) noexcept { - const auto _IFirst_ch = const_cast(reinterpret_cast(_IFirst)); - const auto _ILast_ch = const_cast(reinterpret_cast(_ILast)); - const auto _OFirst_ch = const_cast(reinterpret_cast(_OFirst)); - const auto _OLast_ch = const_cast(reinterpret_cast(_OLast)); + const auto _IFirstPtr = _To_address(_IFirst); + const auto _ILastPtr = _To_address(_ILast); + const auto _OFirstPtr = _To_address(_OFirst); + const auto _OLastPtr = _To_address(_OLast); + const auto _IFirst_ch = const_cast(reinterpret_cast(_IFirstPtr)); + const auto _ILast_ch = const_cast(reinterpret_cast(_ILastPtr)); + const auto _OFirst_ch = const_cast(reinterpret_cast(_OFirstPtr)); + const auto _OLast_ch = const_cast(reinterpret_cast(_OLastPtr)); const auto _Count = static_cast((_STD min)(_ILast_ch - _IFirst_ch, _OLast_ch - _OFirst_ch)); _CSTD memcpy(_OFirst_ch, _IFirst_ch, _Count); - return {reinterpret_cast<_InIt>(_IFirst_ch + _Count), reinterpret_cast<_OutIt>(_OFirst_ch + _Count)}; + if constexpr (is_pointer_v<_InIt>) { + _IFirst = reinterpret_cast<_InIt>(_IFirst_ch + _Count); + } else { + _IFirst += _Count / sizeof(iter_value_t<_InIt>); + } + + if constexpr (is_pointer_v<_OutIt>) { + _OFirst = reinterpret_cast<_OutIt>(_OFirst_ch + _Count); + } else { + _OFirst += _Count / sizeof(iter_value_t<_OutIt>); + } + return {_STD move(_IFirst), _STD move(_OFirst)}; } // ALIAS TEMPLATE uninitialized_move_result @@ -1580,10 +1591,12 @@ namespace ranges { _No_throw_sentinel_for<_Out> _OSe> requires constructible_from, iter_rvalue_reference_t<_It>> uninitialized_move_result<_It, _Out> _Uninitialized_move_unchecked( - _It _IFirst, const _Se _ILast, _Out _OFirst, const _OSe _OLast) { + _It _IFirst, _Se _ILast, _Out _OFirst, _OSe _OLast) { // clang-format on - 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); + if constexpr (_Ptr_move_cat<_It, _Out>::_Really_trivial + && sized_sentinel_for<_Se, _It> && sized_sentinel_for<_OSe, _Out>) { + return _Copy_memcpy_common( + _IFirst, _RANGES next(_IFirst, _STD move(_ILast)), _OFirst, _RANGES next(_OFirst, _STD move(_OLast))); } else { _Uninitialized_backout _Backout{_STD move(_OFirst)}; @@ -1785,14 +1798,15 @@ void uninitialized_fill(const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last, c // FUNCTION TEMPLATE _Uninitialized_value_construct_n WITH ALLOCATOR template -_INLINE_VAR constexpr bool _Use_memset_value_construct_v = conjunction_v, - is_scalar<_Iter_value_t<_NoThrowFwdIt>>, negation>>>, - negation>>>; +_INLINE_VAR constexpr bool _Use_memset_value_construct_v = + conjunction_v>, is_scalar<_Iter_value_t<_NoThrowFwdIt>>, + negation>>>, + negation>>>; template _Ptr _Zero_range(const _Ptr _First, const _Ptr _Last) { // fill [_First, _Last) with zeroes - char* const _First_ch = reinterpret_cast(_First); - char* const _Last_ch = reinterpret_cast(_Last); + char* const _First_ch = reinterpret_cast(_To_address(_First)); + char* const _Last_ch = reinterpret_cast(_To_address(_Last)); _CSTD memset(_First_ch, 0, static_cast(_Last_ch - _First_ch)); return _Last; } @@ -1870,7 +1884,7 @@ struct _In_place_key_extract_set<_Key, _Key> { // STRUCT TEMPLATE _In_place_key_extract_map // assumes _Args have already been _Remove_cvref_t'd -template +template struct _In_place_key_extract_map { // by default we can't extract the key in the emplace family and must construct a node we might not use static constexpr bool _Extractable = false; diff --git a/stl/inc/xutility b/stl/inc/xutility index 2179e1251fd..728c332e48e 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -21,11 +21,17 @@ _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new -#if (defined(_M_IX86) || defined(_M_X64)) && !defined(_M_CEE_PURE) && !defined(_M_HYBRID) && !defined(_M_ARM64EC) +#if (defined(_M_IX86) || defined(_M_X64)) && !defined(_M_CEE_PURE) && !defined(_M_HYBRID) +#ifndef _USE_STD_VECTOR_ALGORITHMS #define _USE_STD_VECTOR_ALGORITHMS 1 -#else +#endif // _USE_STD_VECTOR_ALGORITHMS +#else // ^^^ arch supports vector algorithms / no support for vector algorithms vvv +#ifndef _USE_STD_VECTOR_ALGORITHMS #define _USE_STD_VECTOR_ALGORITHMS 0 -#endif +#elif _USE_STD_VECTOR_ALGORITHMS +#error Vector algorithms are not supported on this architecture, but _USE_STD_VECTOR_ALGORITHMS is set. +#endif // _USE_STD_VECTOR_ALGORITHMS +#endif // ^^^ no support for vector algorithms ^^^ #ifdef __CUDACC__ #define _CONSTEXPR_BIT_CAST inline @@ -1459,11 +1465,6 @@ 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) { @@ -3173,6 +3174,20 @@ namespace ranges { using is_transparent = int; }; + // STRUCT ranges::greater + struct greater { + // clang-format off + template + requires totally_ordered_with<_Ty1, _Ty2> // TRANSITION, GH-489 + _NODISCARD constexpr bool operator()(_Ty1&& _Left, _Ty2&& _Right) const noexcept(noexcept( + static_cast(static_cast<_Ty2&&>(_Right) < static_cast<_Ty1&&>(_Left)))) /* strengthened */ { + return static_cast(static_cast<_Ty2&&>(_Right) < static_cast<_Ty1&&>(_Left)); + } + // clang-format on + + using is_transparent = int; + }; + // CONCEPT ranges::common_range // clang-format off template @@ -3583,7 +3598,6 @@ namespace ranges { } constexpr subrange& advance(const iter_difference_t<_It> _Count) { - // Per LWG-3413, this has defined behavior when _Count < 0. if constexpr (bidirectional_iterator<_It>) { if (_Count < 0) { _RANGES advance(_First, _Count); @@ -3747,7 +3761,7 @@ public: // clang-format on #ifdef __cpp_lib_concepts - _NODISCARD constexpr const iterator_type& base() const& noexcept /* strengthened */ { // Per LWG-3391 + _NODISCARD constexpr const iterator_type& base() const& noexcept /* strengthened */ { return _Current; } _NODISCARD constexpr iterator_type base() && noexcept(is_nothrow_move_constructible_v<_Iter>) /* strengthened */ { @@ -4032,6 +4046,37 @@ struct unreachable_sentinel_t : _Unreachable_sentinel_detail::_Base {}; // TRANS inline constexpr unreachable_sentinel_t unreachable_sentinel{}; #endif // __cpp_lib_concepts +// _Iterator_is_contiguous<_Iter> reports whether an iterator is known to be contiguous. +// (Without concepts, this detection is limited, which will limit when we can activate optimizations.) + +#ifdef __cpp_lib_concepts +// When concepts are available, we can detect arbitrary contiguous iterators. +template +inline constexpr bool _Iterator_is_contiguous = contiguous_iterator<_Iter>; + +template +_NODISCARD constexpr auto _To_address(const _Iter& _Val) noexcept { + _STL_INTERNAL_STATIC_ASSERT(contiguous_iterator<_Iter>); + return _STD to_address(_Val); +} +#else // ^^^ defined(__cpp_lib_concepts) ^^^ / vvv !defined(__cpp_lib_concepts) vvv +// When concepts aren't available, we can detect pointers. (Iterators should be unwrapped before using this.) +template +_INLINE_VAR constexpr bool _Iterator_is_contiguous = is_pointer_v<_Iter>; + +template +_NODISCARD constexpr auto _To_address(const _Iter& _Val) noexcept { + _STL_INTERNAL_STATIC_ASSERT(is_pointer_v<_Iter>); + return _Val; +} +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ + +// _Iterators_are_contiguous<_Iter1, _Iter2> reports whether both iterators are known to be contiguous. + +template +_INLINE_VAR constexpr bool _Iterators_are_contiguous = + _Iterator_is_contiguous<_Iter1>&& _Iterator_is_contiguous<_Iter2>; + // FUNCTION TEMPLATE copy template struct _Ptr_cat_helper { @@ -4075,36 +4120,44 @@ struct _False_copy_cat { static constexpr bool _Trivially_copyable = false; }; -template -struct _Ptr_copy_cat : _False_copy_cat {}; // unwrap the pointer-like type and dispatch to _Ptr_cat_helper for copy +// NOTE: pointer is not a contiguous iterator if it points to volatile type +template > +struct _Ptr_move_cat : _False_copy_cat {}; template -struct _Ptr_copy_cat<_Source*, _Dest*> - : conditional_t, - _Ptr_cat_helper, remove_cv_t<_Dest>>, _False_copy_cat> {}; +struct _Ptr_move_cat<_Source, _Dest, false> + : conditional_t, remove_reference_t<_Iter_ref_t<_Source>>>, + _Ptr_cat_helper<_Iter_value_t<_Source>, _Iter_value_t<_Dest>>, _False_copy_cat> {}; template -struct _Ptr_copy_cat, _Dest*> : _Ptr_copy_cat<_Source*, _Dest*> {}; +struct _Ptr_move_cat, _Dest, true> : _Ptr_move_cat<_Source, _Dest> {}; -template -struct _Ptr_move_cat : _False_copy_cat {}; // unwrap the pointer-like type and dispatch to _Ptr_cat_helper for move +template > +struct _Ptr_copy_cat : _False_copy_cat {}; template -struct _Ptr_move_cat<_Source*, _Dest*> - : conditional_t, - _Ptr_cat_helper, remove_cv_t<_Dest>>, _False_copy_cat> {}; +struct _Ptr_copy_cat<_Source, _Dest, false> + : conditional_t, _Iter_ref_t<_Source>>, + _Ptr_cat_helper<_Iter_value_t<_Source>, _Iter_value_t<_Dest>>, _False_copy_cat> {}; template -struct _Ptr_move_cat, _Dest*> : _Ptr_move_cat<_Source*, _Dest*> {}; - -template -_OutIt _Copy_memmove(_InIt _First, _InIt _Last, _OutIt _Dest) { - const char* const _First_ch = const_cast(reinterpret_cast(_First)); - const char* const _Last_ch = const_cast(reinterpret_cast(_Last)); - char* const _Dest_ch = const_cast(reinterpret_cast(_Dest)); +struct _Ptr_copy_cat, _Dest, true> : _Ptr_move_cat<_Source, _Dest> {}; + +template +_OutCtgIt _Copy_memmove(_CtgIt _First, _CtgIt _Last, _OutCtgIt _Dest) { + auto _FirstPtr = _To_address(_First); + auto _LastPtr = _To_address(_Last); + auto _DestPtr = _To_address(_Dest); + const char* const _First_ch = const_cast(reinterpret_cast(_FirstPtr)); + const char* const _Last_ch = const_cast(reinterpret_cast(_LastPtr)); + char* const _Dest_ch = const_cast(reinterpret_cast(_DestPtr)); const auto _Count = static_cast(_Last_ch - _First_ch); _CSTD memmove(_Dest_ch, _First_ch, _Count); - return reinterpret_cast<_OutIt>(_Dest_ch + _Count); + if constexpr (is_pointer_v<_OutCtgIt>) { + return reinterpret_cast<_OutCtgIt>(_Dest_ch + _Count); + } else { + return _Dest + (_LastPtr - _FirstPtr); + } } template @@ -4206,14 +4259,22 @@ _FwdIt2 copy_n(_ExPo&&, _FwdIt1 _First, _Diff _Count_raw, _FwdIt2 _Dest) noexcep #endif // _HAS_CXX17 // FUNCTION TEMPLATE copy_backward -template -_BidIt2 _Copy_backward_memmove(_BidIt1 _First, _BidIt1 _Last, _BidIt2 _Dest) { +template +_CtgIt2 _Copy_backward_memmove(_CtgIt1 _First, _CtgIt1 _Last, _CtgIt2 _Dest) { // implement copy_backward-like function as memmove - const char* const _First_ch = const_cast(reinterpret_cast(_First)); - const char* const _Last_ch = const_cast(reinterpret_cast(_Last)); - char* const _Dest_ch = const_cast(reinterpret_cast(_Dest)); + auto _FirstPtr = _To_address(_First); + auto _LastPtr = _To_address(_Last); + auto _DestPtr = _To_address(_Dest); + const char* const _First_ch = const_cast(reinterpret_cast(_FirstPtr)); + const char* const _Last_ch = const_cast(reinterpret_cast(_LastPtr)); + char* const _Dest_ch = const_cast(reinterpret_cast(_DestPtr)); const auto _Count = static_cast(_Last_ch - _First_ch); - return static_cast<_BidIt2>(_CSTD memmove(_Dest_ch - _Count, _First_ch, _Count)); + auto _Result = _CSTD memmove(_Dest_ch - _Count, _First_ch, _Count); + if constexpr (is_pointer_v<_CtgIt2>) { + return static_cast<_CtgIt2>(_Result); + } else { + return _Dest - (_LastPtr - _FirstPtr); + } } template @@ -4345,6 +4406,7 @@ _BidIt2 move_backward(_ExPo&&, _BidIt1 _First, _BidIt1 _Last, _BidIt2 _Dest) noe #endif // _HAS_CXX17 // FUNCTION TEMPLATE fill + template struct _Is_character : false_type {}; // by default, not a character type @@ -4363,19 +4425,22 @@ struct _Is_character : true_type {}; // UTF-8 code units are sort-of ch #endif // __cpp_char8_t template -struct _Is_character_or_byte_or_bool : _Is_character<_Ty>::type {}; +struct _Is_character_or_bool : _Is_character<_Ty>::type {}; + +template <> +struct _Is_character_or_bool : true_type {}; + +template +struct _Is_character_or_byte_or_bool : _Is_character_or_bool<_Ty>::type {}; #ifdef __cpp_lib_byte template <> struct _Is_character_or_byte_or_bool : true_type {}; #endif // __cpp_lib_byte -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. // Need to explicitly test for volatile because _Unwrap_enum_t discards qualifiers. -template > +template > _INLINE_VAR constexpr bool _Fill_memset_is_safe = conjunction_v, _Is_character_or_byte_or_bool<_Unwrap_enum_t>>>, negation>>>, is_assignable<_Iter_ref_t<_FwdIt>, const _Ty&>>; @@ -4383,7 +4448,7 @@ _INLINE_VAR constexpr bool _Fill_memset_is_safe = conjunction_v, template _INLINE_VAR constexpr bool _Fill_memset_is_safe<_FwdIt, _Ty, false> = false; -template > +template > _INLINE_VAR constexpr bool _Fill_zero_memset_is_safe = conjunction_v, is_scalar<_Iter_value_t<_FwdIt>>, negation>>, negation>>>, is_assignable<_Iter_ref_t<_FwdIt>, const _Ty&>>; @@ -4391,15 +4456,16 @@ _INLINE_VAR constexpr bool _Fill_zero_memset_is_safe = template _INLINE_VAR constexpr bool _Fill_zero_memset_is_safe<_FwdIt, _Ty, false> = false; -template -void _Fill_memset(_DestTy* const _Dest, const _Ty _Val, const size_t _Count) { - _DestTy _Dest_val = _Val; // implicitly convert (a cast would suppress warnings); also handles _DestTy being bool - _CSTD memset(_Dest, static_cast(_Dest_val), _Count); +template +void _Fill_memset(_CtgIt _Dest, const _Ty _Val, const size_t _Count) { + // implicitly convert (a cast would suppress warnings); also handles _Iter_value_t<_CtgIt> being bool + _Iter_value_t<_CtgIt> _Dest_val = _Val; + _CSTD memset(_To_address(_Dest), static_cast(_Dest_val), _Count); } -template -void _Fill_zero_memset(_DestTy* const _Dest, const size_t _Count) { - _CSTD memset(_Dest, 0, _Count * sizeof(_DestTy)); +template +void _Fill_zero_memset(_CtgIt _Dest, const size_t _Count) { + _CSTD memset(_To_address(_Dest), 0, _Count * sizeof(_Iter_value_t<_CtgIt>)); } template @@ -4565,32 +4631,6 @@ template _INLINE_VAR constexpr bool _Can_memcmp_elements_with_pred = _Can_memcmp_elements<_Elem1, _Elem2> // && _Pred_is_consistent_with_memcmp<_Elem1, _Elem2, _Pr>; -// _Iterators_are_contiguous<_Iter1, _Iter2> reports whether both iterators are known to be contiguous. -// (Without concepts, this detection is limited, which will limit when we can activate the memcmp optimization.) - -#ifdef __cpp_lib_concepts -// When concepts are available, we can detect arbitrary contiguous iterators. -template -_INLINE_VAR constexpr bool _Iterators_are_contiguous = contiguous_iterator<_Iter1> // - && contiguous_iterator<_Iter2>; - -template -_NODISCARD constexpr _Target* _To_pointer(const _Iter& _It) noexcept { - _STL_INTERNAL_STATIC_ASSERT(contiguous_iterator<_Iter>); - return reinterpret_cast<_Target*>(_STD to_address(_It)); -} -#else // ^^^ defined(__cpp_lib_concepts) ^^^ / vvv !defined(__cpp_lib_concepts) vvv -// When concepts aren't available, we can detect pointers. (Iterators should be unwrapped before using this.) -template -_INLINE_VAR constexpr bool _Iterators_are_contiguous = conjunction_v, is_pointer<_Iter2>>; - -template -_NODISCARD constexpr _Target* _To_pointer(const _Iter& _It) noexcept { - _STL_INTERNAL_STATIC_ASSERT(is_pointer_v<_Iter>); - return reinterpret_cast<_Target*>(_It); -} -#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ - // _Equal_memcmp_is_safe<_Iter1, _Iter2, _Pr> reports whether we can activate the memcmp optimization // for arbitrary iterators and predicates. // It ignores top-level constness on the iterators and on the elements. @@ -4603,6 +4643,23 @@ template _INLINE_VAR constexpr bool _Equal_memcmp_is_safe = _Equal_memcmp_is_safe_helper, remove_const_t<_Iter2>, _Pr>; +template +_NODISCARD int _Memcmp_ranges(_CtgIt1 _First1, _CtgIt1 _Last1, _CtgIt2 _First2) { + _STL_INTERNAL_STATIC_ASSERT(sizeof(_Iter_value_t<_CtgIt1>) == sizeof(_Iter_value_t<_CtgIt2>)); + const auto _First1_ch = reinterpret_cast(_To_address(_First1)); + const auto _Last1_ch = reinterpret_cast(_To_address(_Last1)); + const auto _First2_ch = reinterpret_cast(_To_address(_First2)); + return _CSTD memcmp(_First1_ch, _First2_ch, static_cast(_Last1_ch - _First1_ch)); +} + +template +_NODISCARD int _Memcmp_count(_CtgIt1 _First1, _CtgIt2 _First2, const size_t _Count) { + _STL_INTERNAL_STATIC_ASSERT(sizeof(_Iter_value_t<_CtgIt1>) == sizeof(_Iter_value_t<_CtgIt2>)); + const auto _First1_ch = reinterpret_cast(_To_address(_First1)); + const auto _First2_ch = reinterpret_cast(_To_address(_First2)); + return _CSTD memcmp(_First1_ch, _First2_ch, _Count * sizeof(_Iter_value_t<_CtgIt1>)); +} + template _NODISCARD _CONSTEXPR20 bool equal(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _First2, _Pr _Pred) { // compare [_First1, _Last1) to [_First2, ...) @@ -4615,10 +4672,7 @@ _NODISCARD _CONSTEXPR20 bool equal(const _InIt1 _First1, const _InIt1 _Last1, co if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - const auto _First1_ch = _To_pointer(_UFirst1); - const auto _First2_ch = _To_pointer(_UFirst2); - const auto _Count = static_cast(_To_pointer(_ULast1) - _First1_ch); - return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; + return _Memcmp_ranges(_UFirst1, _ULast1, _UFirst2) == 0; } } @@ -4710,6 +4764,126 @@ _NODISCARD bool equal(_ExPo&& _Exec, const _FwdIt1 _First1, const _FwdIt1 _Last1 } #endif // _HAS_CXX17 +#ifdef __cpp_lib_concepts +namespace ranges { + // CONCEPT _Convertible_from + template + concept _Convertible_from = convertible_to<_From, _To>; + + // STRUCT TEMPLATE in_in_result + template + struct in_in_result { + /* [[no_unique_address]] */ _In1 in1; + /* [[no_unique_address]] */ _In2 in2; + + template <_Convertible_from _IIn1, _Convertible_from _IIn2> + constexpr operator in_in_result<_IIn1, _IIn2>() const& { + return {in1, in2}; + } + + template <_Convertible_from<_In1> _IIn1, _Convertible_from<_In2> _IIn2> + constexpr operator in_in_result<_IIn1, _IIn2>() && { + return {_STD move(in1), _STD move(in2)}; + } + }; + + // ALIAS TEMPLATE mismatch_result + template + using mismatch_result = in_in_result<_In1, _In2>; + + // VARIABLE ranges::mismatch + class _Mismatch_fn : private _Not_quite_object { + private: + template + _NODISCARD static constexpr mismatch_result<_It1, _It2> _Mismatch_n( + _It1 _First1, _It2 _First2, iter_difference_t<_It1> _Count, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + auto _UFirst1 = _Get_unwrapped(_STD move(_First1)); + auto _UFirst2 = _Get_unwrapped(_STD move(_First2)); + + for (; _Count != 0; ++_UFirst1, (void) ++_UFirst2, --_Count) { + if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_UFirst1), _STD invoke(_Proj2, *_UFirst2))) { + break; + } + } + + _Seek_wrapped(_First1, _STD move(_UFirst1)); + _Seek_wrapped(_First2, _STD move(_UFirst2)); + return {_STD move(_First1), _STD move(_First2)}; + } + + template + _NODISCARD static constexpr mismatch_result<_It1, _It2> _Mismatch_4( + _It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + auto _UFirst1 = _Get_unwrapped(_STD move(_First1)); + const auto _ULast1 = _Get_unwrapped(_STD move(_Last1)); + auto _UFirst2 = _Get_unwrapped(_STD move(_First2)); + const auto _ULast2 = _Get_unwrapped(_STD move(_Last2)); + + for (; _UFirst1 != _ULast1 && _UFirst2 != _ULast2; ++_UFirst1, (void) ++_UFirst2) { + if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_UFirst1), _STD invoke(_Proj2, *_UFirst2))) { + break; + } + } + + _Seek_wrapped(_First1, _STD move(_UFirst1)); + _Seek_wrapped(_First2, _STD move(_UFirst2)); + return {_STD move(_First1), _STD move(_First2)}; + } + + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se1, input_iterator _It2, sentinel_for<_It2> _Se2, + class _Pr = ranges::equal_to, class _Pj1 = identity, class _Pj2 = identity> + requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2> + _NODISCARD constexpr mismatch_result<_It1, _It2> operator()(_It1 _First1, _Se1 _Last1, + _It2 _First2, _Se2 _Last2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + _Adl_verify_range(_First1, _Last1); + _Adl_verify_range(_First2, _Last2); + + if constexpr (sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2>) { + iter_difference_t<_It1> _Count1 = _Last1 - _First1; + const iter_difference_t<_It2> _Count2 = _Last2 - _First2; + if (_Count1 > _Count2) { + _Count1 = static_cast(_Count2); + } + + return _Mismatch_n(_STD move(_First1), _STD move(_First2), _Count1, + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } else { + return _Mismatch_4(_STD move(_First1), _STD move(_Last1), _STD move(_First2), _STD move(_Last2), + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } + } + + template + requires indirectly_comparable, iterator_t<_Rng2>, _Pr, _Pj1, _Pj2> + _NODISCARD constexpr mismatch_result, borrowed_iterator_t<_Rng2>> operator()( + _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + if constexpr (sized_range<_Rng1> && sized_range<_Rng2>) { + range_difference_t<_Rng1> _Count1 = _RANGES distance(_Range1); + const range_difference_t<_Rng2> _Count2 = _RANGES distance(_Range2); + if (_Count1 > _Count2) { + _Count1 = static_cast>(_Count2); + } + + return _Mismatch_n(_RANGES begin(_Range1), _RANGES begin(_Range2), _Count1, + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } else { + return _Mismatch_4(_RANGES begin(_Range1), _RANGES end(_Range1), + _RANGES begin(_Range2), _RANGES end(_Range2), + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } + } + // clang-format on + }; + + inline constexpr _Mismatch_fn mismatch{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE lexicographical_compare template struct _Lex_compare_check_element_types_helper @@ -4740,6 +4914,8 @@ struct _Lex_compare_check_element_types_helper : true_type { template struct _Lex_compare_optimize { explicit _Lex_compare_optimize() = default; + + using _Pred = _Memcmp_pr; }; // optimization tag for lexicographical_compare template @@ -4753,17 +4929,35 @@ constexpr auto _Lex_compare_memcmp_classify(const _InIt1&, const _InIt2&, const return _Lex_compare_optimize{}; } -template -constexpr auto _Lex_compare_memcmp_classify(_Obj1* const&, _Obj2* const&, const less<_FTy>&) { - // return lex_compare optimization category for pointer iterators and less<_FTy> - return _Lex_compare_check_element_types, _Obj1, _Obj2, _FTy>{}; +template , int> = 0> +constexpr auto _Lex_compare_memcmp_classify(const _CtgIt1&, const _CtgIt2&, const less<_FTy>&) { + // return lex_compare optimization category for contiguous iterators and less<_FTy> + return _Lex_compare_check_element_types, remove_reference_t<_Iter_ref_t<_CtgIt1>>, + remove_reference_t<_Iter_ref_t<_CtgIt2>>, _FTy>{}; +} + +template , int> = 0> +constexpr auto _Lex_compare_memcmp_classify(const _CtgIt1&, const _CtgIt2&, const greater<_FTy>&) { + // return lex_compare optimization category for contiguous iterators and greater<_FTy> + return _Lex_compare_check_element_types, remove_reference_t<_Iter_ref_t<_CtgIt1>>, + remove_reference_t<_Iter_ref_t<_CtgIt2>>, _FTy>{}; +} + +#ifdef __cpp_lib_concepts +template , int> = 0> +constexpr auto _Lex_compare_memcmp_classify(const _CtgIt1&, const _CtgIt2&, const _RANGES less&) { + // return lex_compare optimization category for contiguous iterators and ranges::less + return _Lex_compare_check_element_types, remove_reference_t<_Iter_ref_t<_CtgIt1>>, + remove_reference_t<_Iter_ref_t<_CtgIt2>>, void>{}; } -template -constexpr auto _Lex_compare_memcmp_classify(_Obj1* const&, _Obj2* const&, const greater<_FTy>&) { - // return lex_compare optimization category for pointer iterators and greater<_FTy> - return _Lex_compare_check_element_types, _Obj1, _Obj2, _FTy>{}; +template , int> = 0> +constexpr auto _Lex_compare_memcmp_classify(const _CtgIt1&, const _CtgIt2&, const _RANGES greater&) { + // return lex_compare optimization category for contiguous iterators and ranges::greater + return _Lex_compare_check_element_types, remove_reference_t<_Iter_ref_t<_CtgIt1>>, + remove_reference_t<_Iter_ref_t<_CtgIt2>>, void>{}; } +#endif // __cpp_lib_concepts template _NODISCARD constexpr bool _Lex_compare_unchecked( @@ -4780,9 +4974,9 @@ _NODISCARD constexpr bool _Lex_compare_unchecked( return _First1 == _Last1 && _First2 != _Last2; } -template +template _NODISCARD _CONSTEXPR20 bool _Lex_compare_unchecked( - _InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2, _Pr _Pred, _Lex_compare_optimize<_Memcmp_pr>) { + _CtgIt1 _First1, _CtgIt1 _Last1, _CtgIt2 _First2, _CtgIt2 _Last2, _Pr _Pred, _Lex_compare_optimize<_Memcmp_pr>) { // order [_First1, _Last1) vs. [_First2, _Last2) memcmp optimization #ifdef __cpp_lib_is_constant_evaluated if (_STD is_constant_evaluated()) { @@ -4792,7 +4986,7 @@ _NODISCARD _CONSTEXPR20 bool _Lex_compare_unchecked( (void) _Pred; const auto _Num1 = static_cast(_Last1 - _First1); const auto _Num2 = static_cast(_Last2 - _First2); - const int _Ans = _CSTD memcmp(_First1, _First2, _Num1 < _Num2 ? _Num1 : _Num2); + const int _Ans = _Memcmp_count(_First1, _First2, (_STD min)(_Num1, _Num2)); return _Memcmp_pr{}(_Ans, 0) || (_Ans == 0 && _Num1 < _Num2); } @@ -4854,17 +5048,17 @@ _NODISCARD constexpr auto lexicographical_compare_three_way( using _Ty1 = remove_const_t>; using _Ty2 = remove_const_t>; - if constexpr (conjunction_v, is_pointer<_UIt1>, is_pointer<_UIt2>, - disjunction< + if constexpr ( + conjunction_v, bool_constant<_Iterators_are_contiguous<_UIt1, _UIt2>>, + disjunction< #ifdef __cpp_lib_byte - conjunction, is_same<_Ty2, byte>>, + conjunction, is_same<_Ty2, byte>>, #endif // __cpp_lib_byte - conjunction<_Is_character<_Ty1>, is_unsigned<_Ty1>, _Is_character<_Ty2>, - is_unsigned<_Ty2>>>>) { + conjunction<_Is_character<_Ty1>, is_unsigned<_Ty1>, _Is_character<_Ty2>, is_unsigned<_Ty2>>>>) { if (!_STD is_constant_evaluated()) { const auto _Num1 = static_cast(_ULast1 - _UFirst1); const auto _Num2 = static_cast(_ULast2 - _UFirst2); - const int _Ans = _CSTD memcmp(_UFirst1, _UFirst2, (_STD min)(_Num1, _Num2)); + const int _Ans = _Memcmp_count(_UFirst1, _UFirst2, (_STD min)(_Num1, _Num2)); if (_Ans == 0) { return _Num1 <=> _Num2; } else { @@ -4900,46 +5094,71 @@ _NODISCARD constexpr auto lexicographical_compare_three_way( // FUNCTION TEMPLATE find template -_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, true_type, true_type, _Any_tag) { // signed _Elem, signed _Ty +_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, true_type, true_type, _Any_tag, false_type) { + // signed _Elem, signed _Ty return SCHAR_MIN <= _Val && _Val <= SCHAR_MAX; } template -_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, true_type, false_type, true_type) { +_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, true_type, false_type, true_type, false_type) { // signed _Elem, unsigned _Ty, -1 == static_cast<_Ty>(-1) return _Val <= SCHAR_MAX || static_cast<_Ty>(SCHAR_MIN) <= _Val; } template -_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, true_type, false_type, false_type) { +_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, true_type, false_type, false_type, false_type) { // signed _Elem, unsigned _Ty, -1 != static_cast<_Ty>(-1) return _Val <= SCHAR_MAX; } template -_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, false_type, true_type, _Any_tag) { +_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, false_type, true_type, _Any_tag, false_type) { // unsigned _Elem, signed _Ty return 0 <= _Val && _Val <= UCHAR_MAX; } template -_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, false_type, false_type, _Any_tag) { +_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, false_type, false_type, _Any_tag, false_type) { // unsigned _Elem, unsigned _Ty return _Val <= UCHAR_MAX; } +template +_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, _Any_tag, _Any_tag, _Any_tag, true_type) { + // bool _Elem + return _Val == true || _Val == false; +} + template -_NODISCARD constexpr bool _Within_limits(_InIt, const _Ty& _Val) { // check whether _Val is within the limits of _Elem - using _Elem = remove_pointer_t<_InIt>; +_NODISCARD constexpr bool _Within_limits(const _InIt&, const _Ty& _Val) { + // check whether _Val is within the limits of _Elem + using _Elem = _Iter_value_t<_InIt>; return _Within_limits(_Val, bool_constant>{}, bool_constant>{}, - bool_constant<-1 == static_cast<_Ty>(-1)>{}); + bool_constant<-1 == static_cast<_Ty>(-1)>{}, bool_constant>{}); } template -_NODISCARD constexpr bool _Within_limits(_InIt, const bool&) { // bools are always within the limits of _Elem +_NODISCARD constexpr bool _Within_limits(const _InIt&, const bool&) { // bools are always within the limits of _Elem return true; } +#ifdef __cpp_lib_byte +template +_NODISCARD constexpr bool _Within_limits(const _InIt&, const byte&) { // bytes are only comparable with other bytes + return true; +} +#endif // __cpp_lib_byte + +template +_INLINE_VAR constexpr bool _Memchr_in_find_is_safe = + _Iterator_is_contiguous<_Iter>&& + disjunction_v, _Is_character_or_bool<_Iter_value_t<_Iter>>> +#ifdef __cpp_lib_byte + , + conjunction, is_same<_Iter_value_t<_Iter>, byte>> +#endif // __cpp_lib_byte + > && !is_volatile_v>>; + template _NODISCARD constexpr _InIt _Find_unchecked1(_InIt _First, const _InIt _Last, const _Ty& _Val, false_type) { // find first matching _Val @@ -4961,24 +5180,25 @@ _NODISCARD _CONSTEXPR20 _InIt _Find_unchecked1(_InIt _First, const _InIt _Last, #ifdef __cpp_lib_is_constant_evaluated if (_STD is_constant_evaluated()) { - using _Elem = remove_pointer_t<_InIt>; + using _Elem = _Iter_value_t<_InIt>; return _Find_unchecked1(_First, _Last, static_cast<_Elem>(_Val), false_type{}); } #endif // __cpp_lib_is_constant_evaluated - _First = - static_cast<_InIt>(_CSTD memchr(_First, static_cast(_Val), static_cast(_Last - _First))); - return _First ? _First : _Last; + const auto _First_ptr = _To_address(_First); + const auto _Result = static_cast>*>( + _CSTD memchr(_First_ptr, static_cast(_Val), static_cast(_Last - _First))); + if constexpr (is_pointer_v<_InIt>) { + return _Result ? _Result : _Last; + } else { + return _Result ? _First + (_Result - _First_ptr) : _Last; + } } template _NODISCARD _CONSTEXPR20 _InIt _Find_unchecked(const _InIt _First, const _InIt _Last, const _Ty& _Val) { // find first matching _Val; choose optimization - // activate optimization for pointers to (const) bytes and integral values - using _Memchr_opt = bool_constant< - is_integral_v<_Ty> && _Is_any_of_v<_InIt, char*, signed char*, unsigned char*, // - const char*, const signed char*, const unsigned char*>>; - - return _Find_unchecked1(_First, _Last, _Val, _Memchr_opt{}); + // activate optimization for contiguous iterators to (const) bytes and integral values + return _Find_unchecked1(_First, _Last, _Val, bool_constant<_Memchr_in_find_is_safe<_InIt, _Ty>>{}); } template @@ -4993,6 +5213,46 @@ template _Se, class _Ty, class _Pj = identity> + requires indirect_binary_predicate, const _Ty*> + _NODISCARD constexpr _It _Find_unchecked(_It _First, const _Se _Last, const _Ty& _Val, _Pj _Proj = {}) { + if constexpr (_Memchr_in_find_is_safe<_It, _Ty> && sized_sentinel_for<_Se, _It> && same_as<_Pj, identity>) { + if (!_STD is_constant_evaluated()) { + if (!_Within_limits(_First, _Val)) { + return _RANGES next(_STD move(_First), _Last); + } + + const auto _First_ptr = _STD to_address(_First); + const auto _Result = static_cast>*>(_CSTD memchr(_First_ptr, + static_cast(_Val), static_cast(_Last - _First))); + if (_Result) { + if constexpr (is_pointer_v<_It>) { + return _Result; + } else { + return _RANGES next(_STD move(_First), _Result - _First_ptr); + } + } else { + return _RANGES next(_STD move(_First), _Last); + } + } + } + + for (; _First != _Last; ++_First) { + if (_STD invoke(_Proj, *_First) == _Val) { + break; + } + } + + return _First; + } + // clang-format on +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE count template _NODISCARD _CONSTEXPR20 _Iter_diff_t<_InIt> count(const _InIt _First, const _InIt _Last, const _Ty& _Val) { @@ -5082,10 +5342,10 @@ _CONSTEXPR20 void reverse(const _BidIt _First, const _BidIt _Last) { // reverse auto _UFirst = _Get_unwrapped(_First); auto _ULast = _Get_unwrapped(_Last); #if _USE_STD_VECTOR_ALGORITHMS - using _Elem = remove_pointer_t; - constexpr bool _Allow_vectorization = - conjunction_v, _Is_trivially_swappable<_Elem>, negation>>; - constexpr size_t _Nx = sizeof(_Elem); + using _Elem = remove_reference_t<_Iter_ref_t>; + constexpr bool _Allow_vectorization = conjunction_v>, + _Is_trivially_swappable<_Elem>, negation>>; + constexpr size_t _Nx = sizeof(_Elem); #pragma warning(suppress : 6326) // Potential comparison of a constant with another constant if constexpr (_Allow_vectorization && _Nx <= 8 && (_Nx & (_Nx - 1)) == 0) { @@ -5094,13 +5354,13 @@ _CONSTEXPR20 void reverse(const _BidIt _First, const _BidIt _Last) { // reverse #endif // __cpp_lib_is_constant_evaluated { if constexpr (_Nx == 1) { - __std_reverse_trivially_swappable_1(_UFirst, _ULast); + __std_reverse_trivially_swappable_1(_To_address(_UFirst), _To_address(_ULast)); } else if constexpr (_Nx == 2) { - __std_reverse_trivially_swappable_2(_UFirst, _ULast); + __std_reverse_trivially_swappable_2(_To_address(_UFirst), _To_address(_ULast)); } else if constexpr (_Nx == 4) { - __std_reverse_trivially_swappable_4(_UFirst, _ULast); + __std_reverse_trivially_swappable_4(_To_address(_UFirst), _To_address(_ULast)); } else { - __std_reverse_trivially_swappable_8(_UFirst, _ULast); + __std_reverse_trivially_swappable_8(_To_address(_UFirst), _To_address(_ULast)); } return; @@ -5307,6 +5567,11 @@ namespace ranges { inline constexpr _Find_if_not_fn find_if_not{_Not_quite_object::_Construct_tag{}}; } // namespace ranges + +// STRUCT TEMPLATE _Require_constant +template +struct _Require_constant; // _Require_constant is a valid template-id iff E is a constant expression of structural + // type #endif // __cpp_lib_concepts // FUNCTION TEMPLATE lower_bound @@ -5456,6 +5721,7 @@ _INLINE_VAR constexpr allocator_arg_t allocator_arg{}; [[noreturn]] _CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _Xout_of_range(_In_z_ const char*); [[noreturn]] _CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _Xoverflow_error(_In_z_ const char*); [[noreturn]] _CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _Xruntime_error(_In_z_ const char*); +[[noreturn]] _CRTIMP2_PURE void __CLRCALL_PURE_OR_CDECL _XGetLastError(); // STRUCT TEMPLATE uses_allocator template diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index a4d0d1a1aaf..fd88225e9db 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -180,7 +180,6 @@ // P0879R0 constexpr For Swapping Functions // P0887R1 type_identity // P0896R4 Ranges -// (partially implemented) // P0898R3 Standard Library Concepts // P0912R5 Library Support For Coroutines // P0919R3 Heterogeneous Lookup For Unordered Containers @@ -194,6 +193,7 @@ // P1023R0 constexpr For std::array Comparisons // P1024R3 Enhancing span Usability // P1032R1 Miscellaneous constexpr +// P1035R7 Input Range Adaptors // P1065R2 constexpr INVOKE // (except the std::invoke function which is implemented in C++17) // P1085R2 Removing span Comparisons @@ -201,28 +201,31 @@ // P1123R0 Atomic Compare-And-Exchange With Padding Bits For atomic_ref // 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 -// (partially implemented) // P1248R1 Fixing Relations +// P1252R2 Ranges Design Cleanup // P1357R1 is_bounded_array, is_unbounded_array // P1391R4 Range Constructor For string_view // P1394R4 Range Constructor For span // P1423R3 char8_t Backward Compatibility Remediation // P1456R1 Move-Only Views // P1474R1 Helpful Pointers For contiguous_iterator +// P1522R1 Iterator Difference Type And Integer Overflow +// (technically conforming, but it would be nice to implement a 65-bit integer-like type) +// P1523R1 Views And Size Types // P1612R1 Relocating endian To // P1614R2 Adding Spaceship <=> To The Library +// P1638R1 basic_istream_view::iterator Should Not Be Copyable // P1645R1 constexpr For Algorithms // P1651R0 bind_front() Should Not Unwrap reference_wrapper // P1690R1 Refining Heterogeneous Lookup For Unordered Containers // P1716R3 Range Comparison Algorithms Are Over-Constrained // P1739R4 Avoiding Template Bloat For Ranges -// (partially implemented) // P1754R1 Rename Concepts To standard_case +// P1862R1 Range Adaptors For Non-Copyable Iterators // P1865R1 Adding max() To latch And barrier // P1870R1 Rename forwarding-range To borrowed_range (Was safe_range before LWG-3379) // P1871R1 disable_sized_sentinel_for @@ -235,8 +238,11 @@ // P1964R2 Replacing boolean With boolean-testable // P1973R1 Renaming default_init To for_overwrite // P1976R2 Explicit Constructors For Fixed-Extent span From Dynamic-Extent Ranges +// P1983R0 Fixing Minor Ranges Issues +// P1994R1 elements_view Needs Its Own sentinel // P2091R0 Fixing Issues With Range Access CPOs // P2102R0 Making "Implicit Expression Variations" More Explicit +// P2106R0 Range Algorithm Result Types // P2116R0 Removing tuple-Like Protocol Support From Fixed-Extent span // P????R? directory_entry::clear_cache() @@ -515,7 +521,7 @@ #define _CPPLIB_VER 650 #define _MSVC_STL_VERSION 142 -#define _MSVC_STL_UPDATE 202103L +#define _MSVC_STL_UPDATE 202104L #ifndef _ALLOW_COMPILER_AND_STL_VERSION_MISMATCH #ifdef __CUDACC__ @@ -1220,10 +1226,6 @@ #define __cpp_lib_constexpr_vector 201907L #endif // defined(__cpp_constexpr_dynamic_alloc) && !defined(__clang__) -#ifdef __cpp_impl_coroutine // TRANSITION, Clang coroutine support -#define __cpp_lib_coroutine 201902L -#endif // __cpp_impl_coroutine - #define __cpp_lib_destroying_delete 201806L #define __cpp_lib_endian 201907L #define __cpp_lib_erase_if 202002L @@ -1252,6 +1254,11 @@ #define __cpp_lib_list_remove_return_type 201806L #define __cpp_lib_math_constants 201907L #define __cpp_lib_polymorphic_allocator 201902L + +#ifdef __cpp_lib_concepts // TRANSITION, GH-395 +#define __cpp_lib_ranges 201911L +#endif // __cpp_lib_concepts + #define __cpp_lib_remove_cvref 201711L #define __cpp_lib_semaphore 201907L #define __cpp_lib_shift 201806L @@ -1296,6 +1303,10 @@ #define __cpp_lib_shared_ptr_arrays 201611L // P0497R0 Fixing shared_ptr For Arrays #endif // _HAS_CXX20 +#if defined(__cpp_impl_coroutine) || defined(_DOWNLEVEL_COROUTINES_SUPPORTED) // TRANSITION, Clang coroutine support +#define __cpp_lib_coroutine 201902L +#endif // __cpp_impl_coroutine + // EXPERIMENTAL #define __cpp_lib_experimental_erase_if 201411L #define __cpp_lib_experimental_filesystem 201406L diff --git a/stl/msbuild/stl_1/msvcp_1.settings.targets b/stl/msbuild/stl_1/msvcp_1.settings.targets index 4db7cb3f625..eb1652c4c44 100644 --- a/stl/msbuild/stl_1/msvcp_1.settings.targets +++ b/stl/msbuild/stl_1/msvcp_1.settings.targets @@ -33,8 +33,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception false true $(IntermediateOutputDirectory) - $(CrtBuildDirNative)\msvcprt_1$(BuildSuffix).$(MsvcpFlavor).import_only.lib - $(CrtBuildDir)\msvcprt_1$(BuildSuffix).$(MsvcpFlavor).import_only.lib + $(CrtBuildDir)\msvcprt_1$(BuildSuffix).$(MsvcpFlavor).import_only.lib $(LibOutputFileName).$(MsvcpFlavor) $(IntermediateOutputDirectory)\$(DllDefName).def diff --git a/stl/msbuild/stl_2/msvcp_2.settings.targets b/stl/msbuild/stl_2/msvcp_2.settings.targets index cfca58c4b92..79540664211 100644 --- a/stl/msbuild/stl_2/msvcp_2.settings.targets +++ b/stl/msbuild/stl_2/msvcp_2.settings.targets @@ -33,8 +33,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception false true $(IntermediateOutputDirectory) - $(CrtBuildDirNative)\msvcprt_2$(BuildSuffix).$(MsvcpFlavor).import_only.lib - $(CrtBuildDir)\msvcprt_2$(BuildSuffix).$(MsvcpFlavor).import_only.lib + $(CrtBuildDir)\msvcprt_2$(BuildSuffix).$(MsvcpFlavor).import_only.lib $(LibOutputFileName).$(MsvcpFlavor) $(IntermediateOutputDirectory)\$(DllDefName).def diff --git a/stl/msbuild/stl_atomic_wait/msvcp_atomic_wait.settings.targets b/stl/msbuild/stl_atomic_wait/msvcp_atomic_wait.settings.targets index f85149981f9..32c3ccbe96d 100644 --- a/stl/msbuild/stl_atomic_wait/msvcp_atomic_wait.settings.targets +++ b/stl/msbuild/stl_atomic_wait/msvcp_atomic_wait.settings.targets @@ -33,8 +33,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception false true $(IntermediateOutputDirectory) - $(CrtBuildDirNative)\msvcprt_atomic_wait$(BuildSuffix).$(MsvcpFlavor).import_only.lib - $(CrtBuildDir)\msvcprt_atomic_wait$(BuildSuffix).$(MsvcpFlavor).import_only.lib + $(CrtBuildDir)\msvcprt_atomic_wait$(BuildSuffix).$(MsvcpFlavor).import_only.lib $(LibOutputFileName).$(MsvcpFlavor) $(IntermediateOutputDirectory)\$(DllDefName).def @@ -69,6 +68,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + diff --git a/stl/msbuild/stl_atomic_wait/stl_atomic_wait.files.settings.targets b/stl/msbuild/stl_atomic_wait/stl_atomic_wait.files.settings.targets index 05ebeab8862..6146998be61 100644 --- a/stl/msbuild/stl_atomic_wait/stl_atomic_wait.files.settings.targets +++ b/stl/msbuild/stl_atomic_wait/stl_atomic_wait.files.settings.targets @@ -9,6 +9,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception $(CrtRoot)\github\stl\src\atomic_wait.cpp; $(CrtRoot)\github\stl\src\parallel_algorithms.cpp; $(CrtRoot)\github\stl\src\syncstream.cpp; + $(CrtRoot)\github\stl\src\tzdb.cpp; "> nativecpp diff --git a/stl/msbuild/stl_base/msvcp.settings.targets b/stl/msbuild/stl_base/msvcp.settings.targets index 3fbaa0f17ac..3dec66549c7 100644 --- a/stl/msbuild/stl_base/msvcp.settings.targets +++ b/stl/msbuild/stl_base/msvcp.settings.targets @@ -36,8 +36,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception false true $(IntermediateOutputDirectory) - $(CrtBuildDirNative)\msvcprt_base$(BuildSuffix).$(MsvcpFlavor).import_only.lib - $(CrtBuildDir)\msvcprt_base$(BuildSuffix).$(MsvcpFlavor).import_only.lib + $(CrtBuildDir)\msvcprt_base$(BuildSuffix).$(MsvcpFlavor).import_only.lib $(LibOutputFileName).$(MsvcpFlavor) $(IntermediateOutputDirectory)\$(DllDefName).def $(ClDefines);_STL_CONCRT_SUPPORT diff --git a/stl/msbuild/stl_base/stl.files.settings.targets b/stl/msbuild/stl_base/stl.files.settings.targets index d14a8bc528d..05ff1f5ddbb 100644 --- a/stl/msbuild/stl_base/stl.files.settings.targets +++ b/stl/msbuild/stl_base/stl.files.settings.targets @@ -13,6 +13,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception $(CrtRoot)\github\stl\src\parallel_algorithms.cpp; $(CrtRoot)\github\stl\src\special_math.cpp; $(CrtRoot)\github\stl\src\syncstream.cpp; + $(CrtRoot)\github\stl\src\tzdb.cpp; $(CrtRoot)\github\stl\src\ulocale.cpp; "> nativecpp diff --git a/stl/msbuild/stl_codecvt_ids/msvcp_codecvt_ids.settings.targets b/stl/msbuild/stl_codecvt_ids/msvcp_codecvt_ids.settings.targets index d913adbc158..2116ad9a688 100644 --- a/stl/msbuild/stl_codecvt_ids/msvcp_codecvt_ids.settings.targets +++ b/stl/msbuild/stl_codecvt_ids/msvcp_codecvt_ids.settings.targets @@ -33,8 +33,7 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception false true $(IntermediateOutputDirectory) - $(CrtBuildDirNative)\msvcprt$(BuildSuffix)_codecvt_ids.$(MsvcpFlavor).import_only.lib - $(CrtBuildDir)\msvcprt$(BuildSuffix)_codecvt_ids.$(MsvcpFlavor).import_only.lib + $(CrtBuildDir)\msvcprt$(BuildSuffix)_codecvt_ids.$(MsvcpFlavor).import_only.lib $(LibOutputFileName).$(MsvcpFlavor) $(IntermediateOutputDirectory)\$(DllDefName).def diff --git a/stl/src/msvcp_atomic_wait.src b/stl/src/msvcp_atomic_wait.src index 240916023ea..0ea7194ccc4 100644 --- a/stl/src/msvcp_atomic_wait.src +++ b/stl/src/msvcp_atomic_wait.src @@ -20,11 +20,21 @@ EXPORTS __std_atomic_wait_get_remaining_timeout __std_atomic_wait_indirect __std_bulk_submit_threadpool_work + __std_calloc_crt __std_close_threadpool_work __std_create_threadpool_work __std_execution_wait_on_uchar __std_execution_wake_by_address_all + __std_free_crt __std_parallel_algorithms_hw_threads __std_release_shared_mutex_for_instance __std_submit_threadpool_work + __std_tzdb_delete_current_zone + __std_tzdb_delete_leap_seconds + __std_tzdb_delete_sys_info + __std_tzdb_delete_time_zones + __std_tzdb_get_current_zone + __std_tzdb_get_leap_seconds + __std_tzdb_get_sys_info + __std_tzdb_get_time_zones __std_wait_for_threadpool_work_callbacks diff --git a/stl/src/syncstream.cpp b/stl/src/syncstream.cpp index 77e7df373e9..2b16b316549 100644 --- a/stl/src/syncstream.cpp +++ b/stl/src/syncstream.cpp @@ -3,13 +3,12 @@ // initialize syncstream mutex map +#include <__msvc_tzdb.hpp> #include -#include #include #include #include #include -#include #include #pragma warning(disable : 4074) @@ -23,33 +22,7 @@ namespace { size_t _Ref_count = 0; }; - template - class _Crt_allocator { - public: - using value_type = _Ty; - using propagate_on_container_move_assignment = _STD true_type; - using is_always_equal = _STD true_type; - - constexpr _Crt_allocator() noexcept = default; - - constexpr _Crt_allocator(const _Crt_allocator&) noexcept = default; - template - constexpr _Crt_allocator(const _Crt_allocator<_Other>&) noexcept {} - - _NODISCARD __declspec(allocator) _Ty* allocate(_CRT_GUARDOVERFLOW const size_t _Count) { - const auto _Ptr = _calloc_crt(_Count, sizeof(_Ty)); - if (!_Ptr) { - throw _STD bad_alloc{}; - } - return static_cast<_Ty*>(_Ptr); - } - - void deallocate(_Ty* const _Ptr, size_t) noexcept { - _free_crt(_Ptr); - } - }; - - using _Map_alloc = _Crt_allocator<_STD pair>; + using _Map_alloc = _STD _Crt_allocator<_STD pair>; using _Map_type = _STD map, _Map_alloc>; _Map_type _Lookup_map; diff --git a/stl/src/tzdb.cpp b/stl/src/tzdb.cpp new file mode 100644 index 00000000000..f18642fa9c8 --- /dev/null +++ b/stl/src/tzdb.cpp @@ -0,0 +1,614 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include <__msvc_tzdb.hpp> +#include +#include +#include +#include +#include +#include +#include + +#include + +#pragma comment(lib, "Advapi32") + +namespace { + enum class _Icu_api_level : unsigned long { + _Not_set, + _Detecting, + _Has_failed, + _Has_icu_addresses, + }; + + struct _Icu_functions_table { + _STD atomic _Pfn_ucal_close{nullptr}; + _STD atomic _Pfn_ucal_get{nullptr}; + _STD atomic _Pfn_ucal_getCanonicalTimeZoneID{nullptr}; + _STD atomic _Pfn_ucal_getDefaultTimeZone{nullptr}; + _STD atomic _Pfn_ucal_getTimeZoneDisplayName{nullptr}; + _STD atomic _Pfn_ucal_getTimeZoneTransitionDate{nullptr}; + _STD atomic _Pfn_ucal_getTZDataVersion{nullptr}; + _STD atomic _Pfn_ucal_inDaylightTime{nullptr}; + _STD atomic _Pfn_ucal_open{nullptr}; + _STD atomic _Pfn_ucal_openTimeZoneIDEnumeration{nullptr}; + _STD atomic _Pfn_ucal_setMillis{nullptr}; + _STD atomic _Pfn_uenum_close{nullptr}; + _STD atomic _Pfn_uenum_count{nullptr}; + _STD atomic _Pfn_uenum_unext{nullptr}; + _STD atomic<_Icu_api_level> _Api_level{_Icu_api_level::_Not_set}; + }; + + _Icu_functions_table _Icu_functions; + + template + void _Load_address( + const HMODULE _Module, _STD atomic<_Ty>& _Stored_Pfn, LPCSTR _Fn_name, DWORD& _Last_error) noexcept { + const auto _Pfn = reinterpret_cast<_Ty>(GetProcAddress(_Module, _Fn_name)); + if (_Pfn != nullptr) { + _Stored_Pfn.store(_Pfn, _STD memory_order_relaxed); + } else { + _Last_error = GetLastError(); + } + } + + _NODISCARD _Icu_api_level _Init_icu_functions(_Icu_api_level _Level) noexcept { + while (!_Icu_functions._Api_level.compare_exchange_weak( + _Level, _Icu_api_level::_Detecting, _STD memory_order_acq_rel)) { + if (_Level > _Icu_api_level::_Detecting) { + return _Level; + } + } + + _Level = _Icu_api_level::_Has_failed; + + const HMODULE _Icu_module = LoadLibraryExW(L"icu.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32); + if (_Icu_module != nullptr) { + // collect at least one error if any GetProcAddress call fails + DWORD _Last_error{ERROR_SUCCESS}; + _Load_address(_Icu_module, _Icu_functions._Pfn_ucal_close, "ucal_close", _Last_error); + _Load_address(_Icu_module, _Icu_functions._Pfn_ucal_get, "ucal_get", _Last_error); + _Load_address(_Icu_module, _Icu_functions._Pfn_ucal_getCanonicalTimeZoneID, "ucal_getCanonicalTimeZoneID", + _Last_error); + _Load_address( + _Icu_module, _Icu_functions._Pfn_ucal_getDefaultTimeZone, "ucal_getDefaultTimeZone", _Last_error); + _Load_address(_Icu_module, _Icu_functions._Pfn_ucal_getTimeZoneDisplayName, "ucal_getTimeZoneDisplayName", + _Last_error); + _Load_address(_Icu_module, _Icu_functions._Pfn_ucal_getTimeZoneTransitionDate, + "ucal_getTimeZoneTransitionDate", _Last_error); + _Load_address(_Icu_module, _Icu_functions._Pfn_ucal_getTZDataVersion, "ucal_getTZDataVersion", _Last_error); + _Load_address(_Icu_module, _Icu_functions._Pfn_ucal_inDaylightTime, "ucal_inDaylightTime", _Last_error); + _Load_address(_Icu_module, _Icu_functions._Pfn_ucal_open, "ucal_open", _Last_error); + _Load_address(_Icu_module, _Icu_functions._Pfn_ucal_openTimeZoneIDEnumeration, + "ucal_openTimeZoneIDEnumeration", _Last_error); + _Load_address(_Icu_module, _Icu_functions._Pfn_ucal_setMillis, "ucal_setMillis", _Last_error); + _Load_address(_Icu_module, _Icu_functions._Pfn_uenum_close, "uenum_close", _Last_error); + _Load_address(_Icu_module, _Icu_functions._Pfn_uenum_count, "uenum_count", _Last_error); + _Load_address(_Icu_module, _Icu_functions._Pfn_uenum_unext, "uenum_unext", _Last_error); + if (_Last_error == ERROR_SUCCESS) { + _Level = _Icu_api_level::_Has_icu_addresses; + } else { + // reset last error code for thread in case a later GetProcAddress resets it + SetLastError(_Last_error); + } + } + + _Icu_functions._Api_level.store(_Level, _STD memory_order_release); + return _Level; + } + + _NODISCARD _Icu_api_level _Acquire_icu_functions() noexcept { + auto _Level = _Icu_functions._Api_level.load(_STD memory_order_acquire); + if (_Level <= _Icu_api_level::_Detecting) { + _Level = _Init_icu_functions(_Level); + } + + return _Level; + } + + void __icu_ucal_close(UCalendar* cal) noexcept { + const auto _Fun = _Icu_functions._Pfn_ucal_close.load(_STD memory_order_relaxed); + _Fun(cal); + } + + _NODISCARD int32_t __icu_ucal_get(const UCalendar* cal, UCalendarDateFields field, UErrorCode* status) noexcept { + const auto _Fun = _Icu_functions._Pfn_ucal_get.load(_STD memory_order_relaxed); + return _Fun(cal, field, status); + } + + _NODISCARD int32_t __icu_ucal_getCanonicalTimeZoneID(const UChar* id, int32_t len, UChar* result, + int32_t resultCapacity, UBool* isSystemID, UErrorCode* status) noexcept { + const auto _Fun = _Icu_functions._Pfn_ucal_getCanonicalTimeZoneID.load(_STD memory_order_relaxed); + return _Fun(id, len, result, resultCapacity, isSystemID, status); + } + + _NODISCARD int32_t __icu_ucal_getDefaultTimeZone(UChar* result, int32_t resultCapacity, UErrorCode* ec) noexcept { + const auto _Fun = _Icu_functions._Pfn_ucal_getDefaultTimeZone.load(_STD memory_order_relaxed); + return _Fun(result, resultCapacity, ec); + } + + _NODISCARD int32_t __icu_ucal_getTimeZoneDisplayName(const UCalendar* cal, UCalendarDisplayNameType type, + const char* locale, UChar* result, int32_t resultLength, UErrorCode* status) noexcept { + const auto _Fun = _Icu_functions._Pfn_ucal_getTimeZoneDisplayName.load(_STD memory_order_relaxed); + return _Fun(cal, type, locale, result, resultLength, status); + } + + _NODISCARD UBool __icu_ucal_getTimeZoneTransitionDate( + const UCalendar* cal, UTimeZoneTransitionType type, UDate* transition, UErrorCode* status) noexcept { + const auto _Fun = _Icu_functions._Pfn_ucal_getTimeZoneTransitionDate.load(_STD memory_order_relaxed); + return _Fun(cal, type, transition, status); + } + + _NODISCARD const char* __icu_ucal_getTZDataVersion(UErrorCode* status) noexcept { + const auto _Fun = _Icu_functions._Pfn_ucal_getTZDataVersion.load(_STD memory_order_relaxed); + return _Fun(status); + } + + _NODISCARD UBool __icu_ucal_inDaylightTime(const UCalendar* cal, UErrorCode* status) noexcept { + const auto _Fun = _Icu_functions._Pfn_ucal_inDaylightTime.load(_STD memory_order_relaxed); + return _Fun(cal, status); + } + + _NODISCARD UCalendar* __icu_ucal_open( + const UChar* zoneID, int32_t len, const char* locale, UCalendarType type, UErrorCode* status) noexcept { + const auto _Fun = _Icu_functions._Pfn_ucal_open.load(_STD memory_order_relaxed); + return _Fun(zoneID, len, locale, type, status); + } + + _NODISCARD UEnumeration* __icu_ucal_openTimeZoneIDEnumeration( + USystemTimeZoneType zoneType, const char* region, const int32_t* rawOffset, UErrorCode* ec) noexcept { + const auto _Fun = _Icu_functions._Pfn_ucal_openTimeZoneIDEnumeration.load(_STD memory_order_relaxed); + return _Fun(zoneType, region, rawOffset, ec); + } + + void __icu_ucal_setMillis(UCalendar* cal, UDate dateTime, UErrorCode* status) noexcept { + const auto _Fun = _Icu_functions._Pfn_ucal_setMillis.load(_STD memory_order_relaxed); + _Fun(cal, dateTime, status); + } + + void __icu_uenum_close(UEnumeration* en) noexcept { + const auto _Fun = _Icu_functions._Pfn_uenum_close.load(_STD memory_order_relaxed); + _Fun(en); + } + + _NODISCARD int32_t __icu_uenum_count(UEnumeration* en, UErrorCode* ec) noexcept { + const auto _Fun = _Icu_functions._Pfn_uenum_count.load(_STD memory_order_relaxed); + return _Fun(en, ec); + } + + _NODISCARD const UChar* __icu_uenum_unext(UEnumeration* en, int32_t* resultLength, UErrorCode* status) noexcept { + const auto _Fun = _Icu_functions._Pfn_uenum_unext.load(_STD memory_order_relaxed); + return _Fun(en, resultLength, status); + } + + struct _UEnumeration_deleter { + void operator()(UEnumeration* _En) const noexcept { + __icu_uenum_close(_En); + } + }; + + struct _UCalendar_deleter { + void operator()(UCalendar* _Cal) const noexcept { + __icu_ucal_close(_Cal); + } + }; + + _NODISCARD const char* _Allocate_wide_to_narrow( + const char16_t* const _Input, const int _Input_len, __std_tzdb_error& _Err) noexcept { + const auto _Code_page = __std_fs_code_page(); + const auto _Input_as_wchar = reinterpret_cast(_Input); + const auto _Count_result = __std_fs_convert_wide_to_narrow(_Code_page, _Input_as_wchar, _Input_len, nullptr, 0); + if (_Count_result._Err != __std_win_error::_Success) { + _Err = __std_tzdb_error::_Win_error; + return nullptr; + } + + _STD unique_ptr _Data{new (_STD nothrow) char[_Count_result._Len + 1]}; + if (_Data == nullptr) { + return nullptr; + } + + _Data[_Count_result._Len] = '\0'; + + const auto _Result = + __std_fs_convert_wide_to_narrow(_Code_page, _Input_as_wchar, _Input_len, _Data.get(), _Count_result._Len); + if (_Result._Err != __std_win_error::_Success) { + _Err = __std_tzdb_error::_Win_error; + return nullptr; + } + + return _Data.release(); + } + + _NODISCARD _STD unique_ptr _Allocate_narrow_to_wide( + const char* const _Input, const int _Input_len, __std_tzdb_error& _Err) noexcept { + const auto _Code_page = __std_fs_code_page(); + const auto _Count = __std_fs_convert_narrow_to_wide(_Code_page, _Input, _Input_len, nullptr, 0); + if (_Count._Err != __std_win_error::_Success) { + _Err = __std_tzdb_error::_Win_error; + return nullptr; + } + + _STD unique_ptr _Data{new (_STD nothrow) char16_t[_Count._Len + 1]}; + if (_Data == nullptr) { + return nullptr; + } + + _Data[_Count._Len] = u'\0'; + const auto _Output_as_wchar = reinterpret_cast(_Data.get()); + + const auto _Result = + __std_fs_convert_narrow_to_wide(_Code_page, _Input, _Input_len, _Output_as_wchar, _Count._Len); + if (_Result._Err != __std_win_error::_Success) { + _Err = __std_tzdb_error::_Win_error; + return nullptr; + } + + return _Data; + } + + template + _NODISCARD _STD unique_ptr _Get_icu_string_impl(const _Function _Icu_fn, + const int32_t _Initial_buf_len, int32_t& _Result_len, __std_tzdb_error& _Err) noexcept { + _STD unique_ptr _Str_buf{new (_STD nothrow) char16_t[_Initial_buf_len]}; + if (_Str_buf == nullptr) { + return nullptr; + } + + UErrorCode _UErr{U_ZERO_ERROR}; + _Result_len = _Icu_fn(_Str_buf.get(), _Initial_buf_len, &_UErr); + if (_UErr == U_BUFFER_OVERFLOW_ERROR && _Result_len > 0) { + _Str_buf.reset(new (_STD nothrow) char16_t[_Result_len + 1]); + if (_Str_buf == nullptr) { + return nullptr; + } + + _UErr = U_ZERO_ERROR; // reset error. + _Result_len = _Icu_fn(_Str_buf.get(), _Result_len, &_UErr); + if (U_FAILURE(_UErr)) { + _Err = __std_tzdb_error::_Icu_error; + return nullptr; + } + } else if (U_FAILURE(_UErr) || _Result_len <= 0) { + _Err = __std_tzdb_error::_Icu_error; + return nullptr; + } + + return _Str_buf; + } + + _NODISCARD _STD unique_ptr _Get_canonical_id( + const char16_t* _Id, const int32_t _Len, int32_t& _Result_len, __std_tzdb_error& _Err) noexcept { + const auto _Icu_fn = [_Id, _Len](UChar* _Result, int32_t _Result_capacity, UErrorCode* _UErr) { + UBool _Is_system{}; + return __icu_ucal_getCanonicalTimeZoneID(_Id, _Len, _Result, _Result_capacity, &_Is_system, _UErr); + }; + return _Get_icu_string_impl(_Icu_fn, 32, _Result_len, _Err); + } + + _NODISCARD _STD unique_ptr _Get_default_timezone( + int32_t& _Result_len, __std_tzdb_error& _Err) noexcept { + const auto _Icu_fn = [](UChar* _Result, int32_t _Result_capacity, UErrorCode* _UErr) { + return __icu_ucal_getDefaultTimeZone(_Result, _Result_capacity, _UErr); + }; + return _Get_icu_string_impl(_Icu_fn, 32, _Result_len, _Err); + } + + _NODISCARD _STD unique_ptr _Get_timezone_short_id( + UCalendar* const _Cal, const bool _Is_daylight, int32_t& _Result_len, __std_tzdb_error& _Err) noexcept { + const auto _Display_type = + _Is_daylight ? UCalendarDisplayNameType::UCAL_SHORT_DST : UCalendarDisplayNameType::UCAL_SHORT_STANDARD; + const auto _Icu_fn = [_Cal, _Display_type](UChar* _Result, int32_t _Result_capacity, UErrorCode* _UErr) { + return __icu_ucal_getTimeZoneDisplayName(_Cal, _Display_type, nullptr, _Result, _Result_capacity, _UErr); + }; + return _Get_icu_string_impl(_Icu_fn, 12, _Result_len, _Err); + } + + _NODISCARD _STD unique_ptr _Get_cal( + const char* _Tz, const size_t _Tz_len, __std_tzdb_error& _Err) noexcept { + const auto _Tz_name = _Allocate_narrow_to_wide(_Tz, static_cast(_Tz_len), _Err); + if (_Tz_name == nullptr) { + return nullptr; + } + + UErrorCode _UErr{U_ZERO_ERROR}; + _STD unique_ptr _Cal{ + __icu_ucal_open(_Tz_name.get(), -1, nullptr, UCalendarType::UCAL_DEFAULT, &_UErr)}; + if (U_FAILURE(_UErr)) { + _Err = __std_tzdb_error::_Icu_error; + } + + return _Cal; + } + + template + _NODISCARD _Ty* _Report_error(_STD unique_ptr<_Ty, _Dx>& _Info, const __std_tzdb_error _Err) noexcept { + _Info->_Err = _Err; + return _Info.release(); + } + + template + _NODISCARD _Ty* _Propagate_error(_STD unique_ptr<_Ty, _Dx>& _Info) noexcept { + // a bad_alloc returns nullptr and does not set __std_tzdb_error + return _Info->_Err == __std_tzdb_error::_Success ? nullptr : _Info.release(); + } +} // unnamed namespace + +_EXTERN_C + +_NODISCARD __std_tzdb_time_zones_info* __stdcall __std_tzdb_get_time_zones() noexcept { + // On exit--- + // _Info == nullptr --> bad_alloc + // _Info->_Err == _Win_error --> failed, call GetLastError() + // _Info->_Err == _Icu_error --> runtime_error interacting with ICU + _STD unique_ptr<__std_tzdb_time_zones_info, _STD _Tzdb_deleter<__std_tzdb_time_zones_info>> _Info{ + new (_STD nothrow) __std_tzdb_time_zones_info{}}; + if (_Info == nullptr) { + return nullptr; + } + + if (_Acquire_icu_functions() < _Icu_api_level::_Has_icu_addresses) { + return _Report_error(_Info, __std_tzdb_error::_Win_error); + } + + UErrorCode _UErr{U_ZERO_ERROR}; + _Info->_Version = __icu_ucal_getTZDataVersion(&_UErr); + if (U_FAILURE(_UErr)) { + return _Report_error(_Info, __std_tzdb_error::_Icu_error); + } + + _STD unique_ptr _Enum{ + __icu_ucal_openTimeZoneIDEnumeration(USystemTimeZoneType::UCAL_ZONE_TYPE_ANY, nullptr, nullptr, &_UErr)}; + if (U_FAILURE(_UErr)) { + return _Report_error(_Info, __std_tzdb_error::_Icu_error); + } + + // uenum_count may be expensive but is required to pre-allocate arrays. + const int32_t _Num_time_zones = __icu_uenum_count(_Enum.get(), &_UErr); + if (U_FAILURE(_UErr)) { + return _Report_error(_Info, __std_tzdb_error::_Icu_error); + } + + _Info->_Num_time_zones = static_cast(_Num_time_zones); + // value-init to ensure __std_tzdb_delete_time_zones() cleanup is valid + _Info->_Names = new (_STD nothrow) const char* [_Info->_Num_time_zones] {}; + if (_Info->_Names == nullptr) { + return nullptr; + } + + // value-init to ensure __std_tzdb_delete_time_zones() cleanup is valid + _Info->_Links = new (_STD nothrow) const char* [_Info->_Num_time_zones] {}; + if (_Info->_Links == nullptr) { + return nullptr; + } + + for (size_t _Name_idx = 0; _Name_idx < _Info->_Num_time_zones; ++_Name_idx) { + int32_t _Elem_len{}; + const auto* const _Elem = __icu_uenum_unext(_Enum.get(), &_Elem_len, &_UErr); + if (U_FAILURE(_UErr) || _Elem == nullptr) { + return _Report_error(_Info, __std_tzdb_error::_Icu_error); + } + + _Info->_Names[_Name_idx] = _Allocate_wide_to_narrow(_Elem, _Elem_len, _Info->_Err); + if (_Info->_Names[_Name_idx] == nullptr) { + return _Propagate_error(_Info); + } + + int32_t _Link_len{}; + const auto _Link = _Get_canonical_id(_Elem, _Elem_len, _Link_len, _Info->_Err); + if (_Link == nullptr) { + return _Propagate_error(_Info); + } + + if (_STD u16string_view{_Elem, static_cast(_Elem_len)} + != _STD u16string_view{_Link.get(), static_cast(_Link_len)}) { + _Info->_Links[_Name_idx] = _Allocate_wide_to_narrow(_Link.get(), _Link_len, _Info->_Err); + if (_Info->_Links[_Name_idx] == nullptr) { + return _Propagate_error(_Info); + } + } + } + + return _Info.release(); +} + +void __stdcall __std_tzdb_delete_time_zones(__std_tzdb_time_zones_info* const _Info) noexcept { + if (_Info != nullptr) { + if (_Info->_Names != nullptr) { + for (size_t _Idx = 0; _Idx < _Info->_Num_time_zones; ++_Idx) { + delete[] _Info->_Names[_Idx]; + } + + delete[] _Info->_Names; + } + + if (_Info->_Links != nullptr) { + for (size_t _Idx = 0; _Idx < _Info->_Num_time_zones; ++_Idx) { + delete[] _Info->_Links[_Idx]; + } + + delete[] _Info->_Links; + } + + delete _Info; + } +} + +_NODISCARD __std_tzdb_current_zone_info* __stdcall __std_tzdb_get_current_zone() noexcept { + // On exit--- + // _Info == nullptr --> bad_alloc + // _Info->_Err == _Win_error --> failed, call GetLastError() + // _Info->_Err == _Icu_error --> runtime_error interacting with ICU + _STD unique_ptr<__std_tzdb_current_zone_info, _STD _Tzdb_deleter<__std_tzdb_current_zone_info>> _Info{ + new (_STD nothrow) __std_tzdb_current_zone_info{}}; + if (_Info == nullptr) { + return nullptr; + } + + if (_Acquire_icu_functions() < _Icu_api_level::_Has_icu_addresses) { + return _Report_error(_Info, __std_tzdb_error::_Win_error); + } + + int32_t _Id_len{}; + const auto _Id_name = _Get_default_timezone(_Id_len, _Info->_Err); + if (_Id_name == nullptr) { + return _Propagate_error(_Info); + } + + _Info->_Tz_name = _Allocate_wide_to_narrow(_Id_name.get(), _Id_len, _Info->_Err); + if (_Info->_Tz_name == nullptr) { + return _Propagate_error(_Info); + } + + return _Info.release(); +} + +void __stdcall __std_tzdb_delete_current_zone(__std_tzdb_current_zone_info* const _Info) noexcept { + if (_Info) { + delete[] _Info->_Tz_name; + + delete _Info; + } +} + +_NODISCARD __std_tzdb_sys_info* __stdcall __std_tzdb_get_sys_info( + const char* _Tz, const size_t _Tz_len, __std_tzdb_epoch_milli _Sys) noexcept { + // On exit--- + // _Info == nullptr --> bad_alloc + // _Info->_Err == _Win_error --> failed, call GetLastError() + // _Info->_Err == _Icu_error --> runtime_error interacting with ICU + _STD unique_ptr<__std_tzdb_sys_info, _STD _Tzdb_deleter<__std_tzdb_sys_info>> _Info{ + new (_STD nothrow) __std_tzdb_sys_info{}}; + if (_Info == nullptr) { + return nullptr; + } + + if (_Acquire_icu_functions() < _Icu_api_level::_Has_icu_addresses) { + return _Report_error(_Info, __std_tzdb_error::_Win_error); + } + + const auto _Cal = _Get_cal(_Tz, _Tz_len, _Info->_Err); + if (_Cal == nullptr) { + return _Propagate_error(_Info); + } + + UErrorCode _UErr{}; + __icu_ucal_setMillis(_Cal.get(), _Sys, &_UErr); + if (U_FAILURE(_UErr)) { + return _Report_error(_Info, __std_tzdb_error::_Icu_error); + } + + const auto _Is_daylight = __icu_ucal_inDaylightTime(_Cal.get(), &_UErr); + if (U_FAILURE(_UErr)) { + return _Report_error(_Info, __std_tzdb_error::_Icu_error); + } + + _Info->_Save = _Is_daylight ? __icu_ucal_get(_Cal.get(), UCalendarDateFields::UCAL_DST_OFFSET, &_UErr) : 0; + if (U_FAILURE(_UErr)) { + return _Report_error(_Info, __std_tzdb_error::_Icu_error); + } + + _Info->_Offset = __icu_ucal_get(_Cal.get(), UCalendarDateFields::UCAL_ZONE_OFFSET, &_UErr) + _Info->_Save; + if (U_FAILURE(_UErr)) { + return _Report_error(_Info, __std_tzdb_error::_Icu_error); + } + + UDate _Transition{}; + _Info->_Begin = __icu_ucal_getTimeZoneTransitionDate(_Cal.get(), + UTimeZoneTransitionType::UCAL_TZ_TRANSITION_PREVIOUS_INCLUSIVE, &_Transition, &_UErr) + ? _Transition + : U_DATE_MIN; + if (U_FAILURE(_UErr)) { + return _Report_error(_Info, __std_tzdb_error::_Icu_error); + } + + _Info->_End = __icu_ucal_getTimeZoneTransitionDate( + _Cal.get(), UTimeZoneTransitionType::UCAL_TZ_TRANSITION_NEXT, &_Transition, &_UErr) + ? _Transition + : U_DATE_MAX; + if (U_FAILURE(_UErr)) { + return _Report_error(_Info, __std_tzdb_error::_Icu_error); + } + + int32_t _Abbrev_len{}; + const auto _Abbrev = _Get_timezone_short_id(_Cal.get(), _Is_daylight, _Abbrev_len, _Info->_Err); + if (_Abbrev == nullptr) { + return _Propagate_error(_Info); + } + + _Info->_Abbrev = _Allocate_wide_to_narrow(_Abbrev.get(), _Abbrev_len, _Info->_Err); + if (_Info->_Abbrev == nullptr) { + return _Propagate_error(_Info); + } + + return _Info.release(); +} + +void __stdcall __std_tzdb_delete_sys_info(__std_tzdb_sys_info* const _Info) noexcept { + if (_Info) { + delete[] _Info->_Abbrev; + + delete _Info; + } +} + +_NODISCARD __std_tzdb_leap_info* __stdcall __std_tzdb_get_leap_seconds( + const size_t prev_reg_ls_size, size_t* const current_reg_ls_size) noexcept { + // On exit--- + // *current_reg_ls_size <= prev_reg_ls_size, reg_ls_data == nullptr --> no new data + // *current_reg_ls_size > prev_reg_ls_size, reg_ls_data != nullptr --> new data, successfully read + // *current_reg_ls_size == 0, reg_ls_data != nullptr --> new data, failed reading + // *current_reg_ls_size > prev_reg_ls_size, reg_ls_data == nullptr --> new data, failed allocation + + constexpr auto reg_key_name = LR"(SYSTEM\CurrentControlSet\Control\LeapSecondInformation)"; + constexpr auto reg_subkey_name = L"LeapSeconds"; + *current_reg_ls_size = 0; + HKEY leap_sec_key = nullptr; + + LSTATUS status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, reg_key_name, 0, KEY_READ, &leap_sec_key); + if (status != ERROR_SUCCESS) { + // May not exist on older systems. Treat this as equivalent to the key existing but with no data. + return nullptr; + } + + DWORD byte_size = 0; + status = RegQueryValueExW(leap_sec_key, reg_subkey_name, nullptr, nullptr, nullptr, &byte_size); + static_assert(sizeof(__std_tzdb_leap_info) == 12); + const auto ls_size = byte_size / 12; + *current_reg_ls_size = ls_size; + + __std_tzdb_leap_info* reg_ls_data = nullptr; + if ((status == ERROR_SUCCESS || status == ERROR_MORE_DATA) && ls_size > prev_reg_ls_size) { + try { + reg_ls_data = new __std_tzdb_leap_info[ls_size]; + status = RegQueryValueExW( + leap_sec_key, reg_subkey_name, nullptr, nullptr, reinterpret_cast(reg_ls_data), &byte_size); + if (status != ERROR_SUCCESS) { + *current_reg_ls_size = 0; + } + } catch (...) { + } + } + + RegCloseKey(leap_sec_key); + if (status != ERROR_SUCCESS) { + SetLastError(status); + } + + return reg_ls_data; +} + +void __stdcall __std_tzdb_delete_leap_seconds(__std_tzdb_leap_info* _Info) noexcept { + delete[] _Info; +} + +_NODISCARD void* __stdcall __std_calloc_crt(const size_t count, const size_t size) noexcept { + return _calloc_crt(count, size); +} + +void __stdcall __std_free_crt(void* p) noexcept { + _free_crt(p); +} + +_END_EXTERN_C diff --git a/stl/src/vector_algorithms.cpp b/stl/src/vector_algorithms.cpp index 88a5e5649f9..e0de355b2a9 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -10,11 +10,15 @@ #error _M_CEE_PURE should not be defined when compiling vector_algorithms.cpp. #endif -#if (defined(_M_IX86) || defined(_M_X64)) && !defined(_M_ARM64EC) +#if defined(_M_IX86) || defined(_M_X64) +#if defined(_M_ARM64EC) +#include +#else // defined(_M_ARM64EC) #include #include #include +#endif // defined(_M_ARM64EC) #include extern "C" long __isa_enabled; @@ -50,6 +54,7 @@ static void _Advance_bytes(const void*& _Target, ptrdiff_t _Offset) noexcept { extern "C" { __declspec(noalias) void __cdecl __std_swap_ranges_trivially_swappable_noalias( void* _First1, void* _Last1, void* _First2) noexcept { +#if !defined(_M_ARM64EC) constexpr size_t _Mask_32 = ~((static_cast(1) << 5) - 1); if (_Byte_length(_First1, _Last1) >= 32 && _bittest(&__isa_enabled, __ISA_AVAILABLE_AVX2)) { const void* _Stop_at = _First1; @@ -63,6 +68,7 @@ __declspec(noalias) void __cdecl __std_swap_ranges_trivially_swappable_noalias( _Advance_bytes(_First2, 32); } while (_First1 != _Stop_at); } +#endif // !defined(_M_ARM64EC) constexpr size_t _Mask_16 = ~((static_cast(1) << 4) - 1); if (_Byte_length(_First1, _Last1) >= 16 @@ -131,6 +137,7 @@ void* __cdecl __std_swap_ranges_trivially_swappable(void* _First1, void* _Last1, } __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_1(void* _First, void* _Last) noexcept { +#if !defined(_M_ARM64EC) if (_Byte_length(_First, _Last) >= 64 && _bittest(&__isa_enabled, __ISA_AVAILABLE_AVX2)) { const __m256i _Reverse_char_lanes_avx = _mm256_set_epi8( // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // @@ -150,6 +157,7 @@ __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_1(void* _Firs _Advance_bytes(_First, 32); } while (_First != _Stop_at); } +#endif // !defined(_M_ARM64EC) if (_Byte_length(_First, _Last) >= 32 && _bittest(&__isa_enabled, __ISA_AVAILABLE_SSE42)) { const __m128i _Reverse_char_sse = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); @@ -171,6 +179,7 @@ __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_1(void* _Firs } __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_2(void* _First, void* _Last) noexcept { +#if !defined(_M_ARM64EC) if (_Byte_length(_First, _Last) >= 64 && _bittest(&__isa_enabled, __ISA_AVAILABLE_AVX2)) { const __m256i _Reverse_short_lanes_avx = _mm256_set_epi8( // 1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, // @@ -188,6 +197,7 @@ __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_2(void* _Firs _Advance_bytes(_First, 32); } while (_First != _Stop_at); } +#endif // !defined(_M_ARM64EC) if (_Byte_length(_First, _Last) >= 32 && _bittest(&__isa_enabled, __ISA_AVAILABLE_SSE42)) { const __m128i _Reverse_short_sse = _mm_set_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); @@ -209,6 +219,7 @@ __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_2(void* _Firs } __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_4(void* _First, void* _Last) noexcept { +#if !defined(_M_ARM64EC) if (_Byte_length(_First, _Last) >= 64 && _bittest(&__isa_enabled, __ISA_AVAILABLE_AVX2)) { const void* _Stop_at = _First; _Advance_bytes(_Stop_at, _Byte_length(_First, _Last) >> 6 << 5); @@ -223,6 +234,7 @@ __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_4(void* _Firs _Advance_bytes(_First, 32); } while (_First != _Stop_at); } +#endif // !defined(_M_ARM64EC) if (_Byte_length(_First, _Last) >= 32 #ifdef _M_IX86 @@ -247,6 +259,7 @@ __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_4(void* _Firs } __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_8(void* _First, void* _Last) noexcept { +#if !defined(_M_ARM64EC) if (_Byte_length(_First, _Last) >= 64 && _bittest(&__isa_enabled, __ISA_AVAILABLE_AVX2)) { const void* _Stop_at = _First; _Advance_bytes(_Stop_at, _Byte_length(_First, _Last) >> 6 << 5); @@ -261,6 +274,7 @@ __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_8(void* _Firs _Advance_bytes(_First, 32); } while (_First != _Stop_at); } +#endif // !defined(_M_ARM64EC) if (_Byte_length(_First, _Last) >= 32 #ifdef _M_IX86 @@ -286,6 +300,7 @@ __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_8(void* _Firs __declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_1( const void* _First, const void* _Last, void* _Dest) noexcept { +#if !defined(_M_ARM64EC) if (_Byte_length(_First, _Last) >= 32 && _bittest(&__isa_enabled, __ISA_AVAILABLE_AVX2)) { const __m256i _Reverse_char_lanes_avx = _mm256_set_epi8( // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // @@ -300,6 +315,7 @@ __declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_1( _Advance_bytes(_Dest, 32); } while (_Dest != _Stop_at); } +#endif // !defined(_M_ARM64EC) if (_Byte_length(_First, _Last) >= 16 && _bittest(&__isa_enabled, __ISA_AVAILABLE_SSE42)) { const __m128i _Reverse_char_sse = _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); @@ -320,6 +336,7 @@ __declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_1( __declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_2( const void* _First, const void* _Last, void* _Dest) noexcept { +#if !defined(_M_ARM64EC) if (_Byte_length(_First, _Last) >= 32 && _bittest(&__isa_enabled, __ISA_AVAILABLE_AVX2)) { const __m256i _Reverse_short_lanes_avx = _mm256_set_epi8( // 1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, // @@ -334,6 +351,7 @@ __declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_2( _Advance_bytes(_Dest, 32); } while (_Dest != _Stop_at); } +#endif // !defined(_M_ARM64EC) if (_Byte_length(_First, _Last) >= 16 && _bittest(&__isa_enabled, __ISA_AVAILABLE_SSE42)) { const __m128i _Reverse_short_sse = _mm_set_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); @@ -354,6 +372,7 @@ __declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_2( __declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_4( const void* _First, const void* _Last, void* _Dest) noexcept { +#if !defined(_M_ARM64EC) if (_Byte_length(_First, _Last) >= 32 && _bittest(&__isa_enabled, __ISA_AVAILABLE_AVX2)) { const void* _Stop_at = _Dest; _Advance_bytes(_Stop_at, _Byte_length(_First, _Last) >> 5 << 5); @@ -365,6 +384,7 @@ __declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_4( _Advance_bytes(_Dest, 32); } while (_Dest != _Stop_at); } +#endif // !defined(_M_ARM64EC) if (_Byte_length(_First, _Last) >= 16 #ifdef _M_IX86 @@ -388,6 +408,7 @@ __declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_4( __declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_8( const void* _First, const void* _Last, void* _Dest) noexcept { +#if !defined(_M_ARM64EC) if (_Byte_length(_First, _Last) >= 32 && _bittest(&__isa_enabled, __ISA_AVAILABLE_AVX2)) { const void* _Stop_at = _Dest; _Advance_bytes(_Stop_at, _Byte_length(_First, _Last) >> 5 << 5); @@ -399,6 +420,7 @@ __declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_8( _Advance_bytes(_Dest, 32); } while (_Dest != _Stop_at); } +#endif // !defined(_M_ARM64EC) if (_Byte_length(_First, _Last) >= 16 #ifdef _M_IX86 @@ -423,4 +445,4 @@ __declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_8( } // extern "C" -#endif // (defined(_M_IX86) || defined(_M_X64)) && !defined(_M_ARM64EC) +#endif // defined(_M_IX86) || defined(_M_X64) diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index 28873c3423b..425d5b39b61 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -291,10 +291,6 @@ std/utilities/time/time.cal/time.cal.ymd/time.cal.ymd.nonmembers/streaming.pass. std/utilities/time/time.cal/time.cal.ymdlast/time.cal.ymdlast.nonmembers/streaming.pass.cpp FAIL std/utilities/time/time.cal/time.cal.ymwd/time.cal.ymwd.nonmembers/streaming.pass.cpp FAIL std/utilities/time/time.cal/time.cal.ymwdlast/time.cal.ymwdlast.nonmembers/streaming.pass.cpp FAIL -std/utilities/time/time.clock/time.clock.file/consistency.pass.cpp FAIL -std/utilities/time/time.clock/time.clock.file/file_time.pass.cpp FAIL -std/utilities/time/time.clock/time.clock.file/now.pass.cpp FAIL -std/utilities/time/time.clock/time.clock.file/rep_signed.pass.cpp FAIL # C++20 P0466R5 "Layout-Compatibility And Pointer-Interconvertibility Traits" std/language.support/support.limits/support.limits.general/type_traits.version.pass.cpp:1 FAIL diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index a4df867d39e..1b694c48e0d 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -291,10 +291,6 @@ utilities\time\time.cal\time.cal.ymd\time.cal.ymd.nonmembers\streaming.pass.cpp utilities\time\time.cal\time.cal.ymdlast\time.cal.ymdlast.nonmembers\streaming.pass.cpp utilities\time\time.cal\time.cal.ymwd\time.cal.ymwd.nonmembers\streaming.pass.cpp utilities\time\time.cal\time.cal.ymwdlast\time.cal.ymwdlast.nonmembers\streaming.pass.cpp -utilities\time\time.clock\time.clock.file\consistency.pass.cpp -utilities\time\time.clock\time.clock.file\file_time.pass.cpp -utilities\time\time.clock\time.clock.file\now.pass.cpp -utilities\time\time.clock\time.clock.file\rep_signed.pass.cpp # C++20 P0466R5 "Layout-Compatibility And Pointer-Interconvertibility Traits" language.support\support.limits\support.limits.general\type_traits.version.pass.cpp diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index 901f64822b6..3dc819bee4c 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -87,7 +87,7 @@ namespace test { enum class CanDifference : bool { no, yes }; enum class CanCompare : bool { no, yes }; - enum class ProxyRef : bool { no, yes }; + enum class ProxyRef { no, yes, prvalue }; enum class IsWrapped : bool { no, yes }; template @@ -341,14 +341,15 @@ namespace test { // Interact with the STL's iterator unwrapping machinery? IsWrapped Wrapped = IsWrapped::yes> requires (to_bool(Eq) || !derived_from) - && (!to_bool(Proxy) || !derived_from) + && (Proxy == ProxyRef::no || !derived_from) class iterator { Element* ptr_; template static constexpr bool at_least = derived_from; - using ReferenceType = conditional_t, Element&>; + using ReferenceType = conditional_t, + conditional_t, Element&>>; struct post_increment_proxy { Element* ptr_; @@ -590,7 +591,7 @@ template > { using iterator_concept = Category; using iterator_category = conditional_t, // - conditional_t(Proxy), input_iterator_tag, Category>, // + conditional_t, // conditional_t(Eq), Category, void>>; // TRANSITION, LWG-3289 using value_type = remove_cv_t; using difference_type = ptrdiff_t; @@ -670,11 +671,11 @@ namespace test { template class range_base { public: - range_base() = default; + constexpr range_base() = default; constexpr explicit range_base(span elements) noexcept : elements_{elements} {} - range_base(const range_base&) = default; - range_base& operator=(const range_base&) = default; + constexpr range_base(const range_base&) = default; + constexpr range_base& operator=(const range_base&) = default; constexpr range_base(range_base&& that) noexcept : elements_{that.elements_}, moved_from_{that.moved_from_} { @@ -720,7 +721,7 @@ namespace test { Copyability Copy = IsView == CanView::yes ? Copyability::move_only : Copyability::immobile> requires (!to_bool(IsCommon) || to_bool(Eq)) && (to_bool(Eq) || !derived_from) - && (!to_bool(Proxy) || !derived_from) + && (Proxy == ProxyRef::no || !derived_from) && (!to_bool(IsView) || Copy != Copyability::immobile) class range : public detail::range_base { private: diff --git a/tests/std/include/timezone_data.hpp b/tests/std/include/timezone_data.hpp new file mode 100644 index 00000000000..d1a69835b10 --- /dev/null +++ b/tests/std/include/timezone_data.hpp @@ -0,0 +1,176 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace std::chrono; + +class Transition { +public: + constexpr Transition( + string_view name, seconds offset, minutes save, string_view abbrev, sys_seconds sys_begin, sys_seconds sys_end) + : _name(name), _offset(offset), _save(save), _abbrev(abbrev), _begin(sys_begin), _end(sys_end) {} + + constexpr string_view name() const { + return _name; + } + + constexpr seconds offset() const { + return _offset; + } + + constexpr minutes save() const { + return _save; + } + + constexpr string_view abbrev() const { + return _abbrev; + } + + constexpr bool is_daylight() const { + return _save != minutes{0}; + } + + template + constexpr sys_time begin() const { + return sys_time{duration_cast(_begin.time_since_epoch())}; + } + + template + constexpr sys_time end() const { + return sys_time{duration_cast(_end.time_since_epoch())}; + } + + template + constexpr local_time local_begin() const { + return local_time{duration_cast(_begin.time_since_epoch() + _offset)}; + } + + template + constexpr local_time local_end() const { + return local_time{duration_cast(_end.time_since_epoch() + _offset)}; + } + +private: + string_view _name; + seconds _offset; + minutes _save; + string_view _abbrev; + sys_seconds _begin; + sys_seconds _end; +}; + +// start of ambiguous/nonexistent zone between transitions +template +constexpr local_time get_danger_begin(const Transition& first, const Transition& second) { + assert(first.end() == second.begin()); + return first.local_end() - first.save(); +} + +// end of ambiguous/nonexistent zone between transitions +template +constexpr local_time get_danger_end(const Transition& first, const Transition& second) { + assert(first.end() == second.begin()); + return second.local_begin() + first.save(); +} + +// Sydney +// Standard time (AEST : UTC+10) -1 @ 3am +// Daylight time (AEDT : UTC+11) +1 @ 2am +namespace Sydney { + inline constexpr string_view Tz_name{"Australia/Sydney"sv}; + inline constexpr string_view Standard_abbrev{"GMT+10"sv}; // IANA database == "AEST" + inline constexpr string_view Daylight_abbrev{"GMT+11"sv}; // IANA database == "AEDT" + inline constexpr seconds Standard_offset{hours{10}}; + inline constexpr seconds Daylight_offset{hours{11}}; + inline constexpr auto Daylight_begin_2019 = + sys_seconds{sys_days{year{2019} / October / day{6}}} + hours{2} - Standard_offset; + inline constexpr auto Standard_begin_2020 = + sys_seconds{sys_days{year{2020} / April / day{5}}} + hours{3} - Daylight_offset; + inline constexpr auto Daylight_begin_2020 = + sys_seconds{sys_days{year{2020} / October / day{4}}} + hours{2} - Standard_offset; + inline constexpr auto Standard_begin_2021 = + sys_seconds{sys_days{year{2021} / April / day{4}}} + hours{3} - Daylight_offset; + + inline constexpr Transition Day_1{ + Tz_name, Daylight_offset, hours{1}, Daylight_abbrev, Daylight_begin_2019, Standard_begin_2020}; + inline constexpr Transition Std_1{ + Tz_name, Standard_offset, hours{0}, Standard_abbrev, Standard_begin_2020, Daylight_begin_2020}; + inline constexpr Transition Day_2{ + Tz_name, Daylight_offset, hours{1}, Daylight_abbrev, Daylight_begin_2020, Standard_begin_2021}; + + inline constexpr pair Day_to_Std{Day_1, Std_1}; + inline constexpr pair Std_to_Day{Std_1, Day_2}; + +} // namespace Sydney + +// Los Angeles +// Standard time (PST : UTC-8) +1 @ 2am +// Daylight time (PDT : UTC-7) -1 @ 2am +namespace LA { + inline constexpr string_view Tz_name{"America/Los_Angeles"sv}; + inline constexpr string_view Standard_abbrev{"PST"sv}; + inline constexpr string_view Daylight_abbrev{"PDT"sv}; + inline constexpr seconds Standard_offset{hours{-8}}; + inline constexpr seconds Daylight_offset{hours{-7}}; + inline constexpr auto Daylight_begin_2020 = + sys_seconds{sys_days{year{2020} / March / day{8}}} + hours{2} - Standard_offset; + inline constexpr auto Standard_begin_2020 = + sys_seconds{sys_days{year{2020} / November / day{1}}} + hours{2} - Daylight_offset; + inline constexpr auto Daylight_begin_2021 = + sys_seconds{sys_days{year{2021} / March / day{14}}} + hours{2} - Standard_offset; + inline constexpr auto Standard_begin_2021 = + sys_seconds{sys_days{year{2021} / November / day{7}}} + hours{2} - Daylight_offset; + + inline constexpr Transition Day_1{ + Tz_name, Daylight_offset, hours{1}, Daylight_abbrev, Daylight_begin_2020, Standard_begin_2020}; + inline constexpr Transition Std_1{ + Tz_name, Standard_offset, hours{0}, Standard_abbrev, Standard_begin_2020, Daylight_begin_2021}; + inline constexpr Transition Day_2{ + Tz_name, Daylight_offset, hours{1}, Daylight_abbrev, Daylight_begin_2021, Standard_begin_2021}; + + inline constexpr pair Day_to_Std{Day_1, Std_1}; + inline constexpr pair Std_to_Day{Std_1, Day_2}; + +} // namespace LA + +template +void run_tz_test(TestFunction test_function) { + try { +#ifdef MSVC_INTERNAL_TESTING + try { + (void) get_tzdb(); + } catch (const system_error& ex) { + if (ex.code() == error_code{126 /* ERROR_MOD_NOT_FOUND */, system_category()}) { + // Skip testing when we can't load icu.dll on an internal test machine running an older OS. + exit(EXIT_SUCCESS); + } + + throw; // Report any other errors. + } +#endif // MSVC_INTERNAL_TESTING + + test_function(); + + } catch (const system_error& ex) { + cerr << "Test threw system_error: " << ex.what() << "\n"; + cerr << "With error_code: " << ex.code() << "\n"; + assert(false); + } catch (const runtime_error& ex) { + cerr << "Test threw runtime_error: " << ex.what() << "\n"; + assert(false); + } catch (const exception& ex) { + cerr << "Test threw exception: " << ex.what() << "\n"; + assert(false); + } +} diff --git a/tests/std/test.lst b/tests/std/test.lst index 1697cba92da..05572d7cfdc 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -235,6 +235,8 @@ tests\P0355R7_calendars_and_time_zones_dates_literals tests\P0355R7_calendars_and_time_zones_hms tests\P0355R7_calendars_and_time_zones_io tests\P0355R7_calendars_and_time_zones_time_point_and_durations +tests\P0355R7_calendars_and_time_zones_time_zones +tests\P0355R7_calendars_and_time_zones_zoned_time tests\P0356R5_bind_front tests\P0357R3_supporting_incomplete_types_in_reference_wrapper tests\P0408R7_efficient_access_to_stringbuf_buffer @@ -381,8 +383,10 @@ tests\P0896R4_views_empty tests\P0896R4_views_filter tests\P0896R4_views_filter_death tests\P0896R4_views_iota +tests\P0896R4_views_join tests\P0896R4_views_reverse tests\P0896R4_views_single +tests\P0896R4_views_split tests\P0896R4_views_take tests\P0896R4_views_take_while tests\P0896R4_views_take_while_death diff --git a/tests/std/tests/Dev11_0316853_find_memchr_optimization/test.cpp b/tests/std/tests/Dev11_0316853_find_memchr_optimization/test.cpp index e621519332d..39bd5af4eca 100644 --- a/tests/std/tests/Dev11_0316853_find_memchr_optimization/test.cpp +++ b/tests/std/tests/Dev11_0316853_find_memchr_optimization/test.cpp @@ -345,4 +345,11 @@ int main() { assert(find(begin(sc), end(sc), 0xFFFFFFFFFFFFFF7FULL) == end(sc)); assert(find(begin(sc), end(sc), 0xFFFFFFFFFFFFFF00ULL) == end(sc)); } + + { // Test bools + const bool arr[]{true, true, true, false, true, false}; + assert(find(begin(arr), end(arr), false) == begin(arr) + 3); + assert(find(begin(arr), end(arr), true) == begin(arr)); + assert(find(begin(arr), end(arr), 2) == end(arr)); + } } diff --git a/tests/std/tests/P0083R3_splicing_maps_and_sets/test.cpp b/tests/std/tests/P0083R3_splicing_maps_and_sets/test.cpp index 1b0acb023b5..89f2514cc4c 100644 --- a/tests/std/tests/P0083R3_splicing_maps_and_sets/test.cpp +++ b/tests/std/tests/P0083R3_splicing_maps_and_sets/test.cpp @@ -154,6 +154,7 @@ void test_node_handle(NodeHandle& nh1, NodeHandle& nh2, Validator1 v1, Validator static_assert(std::is_nothrow_default_constructible_v); CHECK_EMPTY(NodeHandle{}); #if defined(__cpp_constinit) +#pragma warning(suppress : 4640) // C4640 emitted by MSVC because 'NodeHandle' type has non-trivial dtor { static constinit NodeHandle static_handle{}; } #elif defined(__clang__) { [[clang::require_constant_initialization]] static NodeHandle static_handle{}; } diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.compile.pass.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.compile.pass.cpp deleted file mode 100644 index 26ac91006b0..00000000000 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.compile.pass.cpp +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#include - -using namespace std::chrono; - -struct not_a_clock { - bool rep(); - static char period; - int duration(); - static float time_point; - using is_steady = long; - static int now; -}; - -struct real_fake_clock { - using rep = bool; - using period = char; - using duration = float; - using time_point = int; - static long is_steady; - static short now(); -}; - -struct no_rep { - using period = char; - using duration = float; - using time_point = int; - static long is_steady; - static short now(); -}; - -struct no_period { - using rep = bool; - using duration = float; - using time_point = int; - static long is_steady; - static short now(); -}; - -struct no_duration { - using rep = bool; - using period = char; - using time_point = int; - static long is_steady; - static short now(); -}; - -struct no_time_point { - using rep = bool; - using period = char; - using duration = float; - static long is_steady; - static short now(); -}; - -struct no_steady { - using rep = bool; - using period = char; - using duration = float; - using time_point = int; - static short now(); -}; - -struct no_now { - using rep = bool; - using period = char; - using duration = float; - using time_point = int; - static long is_steady; -}; - -static_assert(is_clock::value, "steady_clock is not a clock"); -static_assert(is_clock_v, "steady_clock is not a clock"); -static_assert(is_clock_v, "real_fake_clock is not a clock"); -static_assert(!is_clock_v, "not_a_clock is a clock"); - -static_assert(!is_clock_v, "no_rep is a clock"); -static_assert(!is_clock_v, "no_period is a clock"); -static_assert(!is_clock_v, "no_duration is a clock"); -static_assert(!is_clock_v, "no_time_point is a clock"); -static_assert(!is_clock_v, "no_steady is a clock"); -static_assert(!is_clock_v, "no_now is a clock"); - -int main() {} // COMPILE-ONLY diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp new file mode 100644 index 00000000000..f1db6f7286c --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_clocks/test.cpp @@ -0,0 +1,481 @@ +// 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; +using namespace std::chrono; + +struct not_a_clock { + bool rep(); + static char period; + int duration(); + static float time_point; + using is_steady = long; + static int now; +}; + +struct real_fake_clock { + using rep = bool; + using period = char; + using duration = float; + using time_point = int; + static long is_steady; + static short now(); +}; + +struct no_rep { + using period = char; + using duration = float; + using time_point = int; + static long is_steady; + static short now(); +}; + +struct no_period { + using rep = bool; + using duration = float; + using time_point = int; + static long is_steady; + static short now(); +}; + +struct no_duration { + using rep = bool; + using period = char; + using time_point = int; + static long is_steady; + static short now(); +}; + +struct no_time_point { + using rep = bool; + using period = char; + using duration = float; + static long is_steady; + static short now(); +}; + +struct no_steady { + using rep = bool; + using period = char; + using duration = float; + using time_point = int; + static short now(); +}; + +struct no_now { + using rep = bool; + using period = char; + using duration = float; + using time_point = int; + static long is_steady; +}; + +static_assert(is_clock::value, "steady_clock is not a clock"); +static_assert(is_clock_v, "steady_clock is not a clock"); +static_assert(is_clock_v, "real_fake_clock is not a clock"); +static_assert(!is_clock_v, "not_a_clock is a clock"); + +static_assert(!is_clock_v, "no_rep is a clock"); +static_assert(!is_clock_v, "no_period is a clock"); +static_assert(!is_clock_v, "no_duration is a clock"); +static_assert(!is_clock_v, "no_time_point is a clock"); +static_assert(!is_clock_v, "no_steady is a clock"); +static_assert(!is_clock_v, "no_now is a clock"); + +void test_is_leap_second(const year_month_day& ymd) { + const sys_days ls{ymd}; + const auto& leap_seconds = get_tzdb().leap_seconds; + assert(find(leap_seconds.begin(), leap_seconds.end(), ls + days{1}) != leap_seconds.end()); + assert(get_leap_second_info(utc_clock::from_sys(ls) + days{1}).is_leap_second); +} + +constexpr bool test_leap_second() { + constexpr int jun_leap_second_years[] = {1972, 1981, 1982, 1983, 1985, 1992, 1993, 1994, 1997, 2012, 2015}; + constexpr int dec_leap_second_years[] = { + 1972, 1973, 1974, 1975, 1976, 1977, 1978, 1979, 1987, 1989, 1990, 1995, 1998, 2005, 2008, 2016}; + static_assert(size(jun_leap_second_years) + size(dec_leap_second_years) == 27); + + if (!is_constant_evaluated()) { + for (const auto& ls_year : jun_leap_second_years) { + test_is_leap_second(30d / June / year{ls_year}); + } + for (const auto& ls_year : dec_leap_second_years) { + test_is_leap_second(31d / December / year{ls_year}); + } + } + + constexpr leap_second leap{sys_seconds{42s}, true, 0s}; + leap_second leap2(leap); // copy construct + leap_second leap3{sys_seconds{41s}, true, 0s}; + leap3 = leap2; // copy assign + + constexpr sys_seconds smaller{41s}; + constexpr sys_seconds equal{42s}; + constexpr sys_seconds larger{43s}; + + assert(equal == leap); + assert(leap == equal); + static_assert(noexcept(equal == leap)); + static_assert(noexcept(leap == equal)); + + assert(leap < larger); + assert(smaller < leap); + static_assert(noexcept(leap < larger)); + static_assert(noexcept(smaller < leap)); + + assert(larger > leap); + assert(leap > smaller); + static_assert(noexcept(larger > leap)); + static_assert(noexcept(leap > smaller)); + + assert(equal <= leap); + assert(smaller <= leap); + assert(leap <= equal); + assert(leap <= larger); + static_assert(noexcept(equal <= leap)); + static_assert(noexcept(leap <= equal)); + + assert(equal >= leap); + assert(larger >= leap); + assert(leap >= equal); + assert(leap >= smaller); + static_assert(noexcept(equal >= leap)); + static_assert(noexcept(leap >= equal)); + +#ifdef __cpp_lib_concepts // TRANSITION, GH-395 + static_assert(is_eq(leap <=> equal)); + static_assert(is_lt(leap <=> larger)); + static_assert(is_gt(leap <=> smaller)); + static_assert(is_lteq(leap <=> larger)); + static_assert(is_gteq(leap <=> smaller)); + static_assert(is_lteq(leap <=> equal)); + static_assert(is_gteq(leap <=> equal)); + static_assert(noexcept(leap <=> equal)); + + static_assert(is_eq(leap <=> leap_second{equal, true, 0s})); + static_assert(is_lt(leap <=> leap_second{larger, true, 0s})); + static_assert(is_gt(leap <=> leap_second{smaller, true, 0s})); + static_assert(is_lteq(leap <=> leap_second{larger, true, 0s})); + static_assert(is_gteq(leap <=> leap_second{smaller, true, 0s})); + static_assert(is_lteq(leap <=> leap_second{equal, true, 0s})); + static_assert(is_gteq(leap <=> leap_second{equal, true, 0s})); + static_assert(noexcept(leap <=> leap_second{equal, true, 0s})); +#endif // __cpp_lib_concepts + + static_assert(noexcept(leap.date())); + static_assert(noexcept(leap.value())); + static_assert(leap_second{sys_seconds{42s}, true, 0s}.date() == sys_seconds{42s}); + static_assert(leap_second{sys_seconds{42s}, true, 0s}.value() == 1s); + static_assert(leap_second{sys_seconds{42s}, false, 0s}.value() == -1s); + + return true; +} + +constexpr bool operator==(const leap_second_info& lhs, const leap_second_info& rhs) { + return lhs.is_leap_second == rhs.is_leap_second && lhs.elapsed == rhs.elapsed; +} + +template +void test_leap_second_info(const leap_second& leap, seconds accum) { + const bool is_positive = (leap.value() == 1s); + // First UTC time when leap is counted, before insertion of a positive leap, after insertion of a negative one. + const utc_time> utc_leap{leap.date().time_since_epoch() + accum + (is_positive ? 0s : -1s)}; + + auto lsi = get_leap_second_info(utc_leap - 1s); + assert(lsi == (leap_second_info{false, accum})); + + lsi = get_leap_second_info(utc_leap - 500ms); + assert(lsi == (leap_second_info{false, accum})); + + accum += leap.value(); + lsi = get_leap_second_info(utc_leap); + assert(lsi == (leap_second_info{is_positive, accum})); + + lsi = get_leap_second_info(utc_leap + 500ms); + assert(lsi == (leap_second_info{is_positive, accum})); + + lsi = get_leap_second_info(utc_leap + 1s); + assert(lsi == (leap_second_info{false, accum})); +} + +template +void test_utc_clock_to_sys(const leap_second& leap) { + sys_time before_leap; + if constexpr (is_integral_v) { + before_leap = leap.date() - Duration{1}; + } else { + before_leap = + sys_time{Duration{nextafter(leap.date().time_since_epoch().count(), typename Duration::rep{0})}}; + } + + auto u = utc_clock::from_sys(before_leap); // just before leap second + assert(utc_clock::from_sys(utc_clock::to_sys(u)) == u); + if (leap.value() == 1s) { + u += Duration{1}; + assert(utc_clock::to_sys(u) == before_leap); // during + } else { + assert(utc_clock::from_sys(utc_clock::to_sys(u)) == u); + } + + u += 1s; + assert(utc_clock::from_sys(utc_clock::to_sys(u)) == u); // just after +} + +template +void test_file_clock_from_utc(const leap_second& leap) { + static_assert(is_same_v); + + const auto file_leap = clock_cast(leap.date()); + + auto u = utc_clock::from_sys(leap.date() - Duration{1}); // just before leap second + assert(file_clock::to_utc(file_clock::from_utc(u)) == u); + + if (leap.value() == 1s && leap.date() <= sys_days{1d / January / 2017y}) { + u += Duration{1}; + assert(file_clock::from_utc(u) == file_leap - Duration{1}); // during + } else { + assert(file_clock::to_utc(file_clock::from_utc(u)) == u); + } + + u += 1s; + assert(file_clock::to_utc(file_clock::from_utc(u)) == u); // just after +} + +void test_utc_clock_from_sys(const leap_second& leap, seconds offset) { + // Generalized from N4885 [time.clock.utc.members]/3 Example 1. + auto t = leap.date() - 2ns; + auto u = utc_clock::from_sys(t); + assert(u.time_since_epoch() - t.time_since_epoch() == offset); + + t += 1ns; + u = utc_clock::from_sys(t); + assert(u.time_since_epoch() - t.time_since_epoch() == offset); + + t += 1ns; + u = utc_clock::from_sys(t); + offset += leap.value(); + assert(u.time_since_epoch() - t.time_since_epoch() == offset); + + t += 1ns; + u = utc_clock::from_sys(t); + assert(u.time_since_epoch() - t.time_since_epoch() == offset); +} + +void test_file_clock_utc() { + const auto file_epoch{utc_clock::from_sys(sys_days{January / 1 / 1601})}; + assert(file_clock::to_utc(file_time{0s}) == file_epoch); + assert(file_clock::from_utc(file_epoch) == file_time{0s}); +} + +void test_file_clock_to_utc(const leap_second& leap, seconds offset) { + // FILETIME counts leap seconds after 1 January 2017, so offset is constant thereafter. + constexpr sys_days file_time_cutoff{1d / January / 2017y}; + auto t = clock_cast(leap.date()) - 2us; + if (leap > file_time_cutoff) { + offset = 27s; + } + + offset -= duration_cast(file_clock::duration{filesystem::__std_fs_file_time_epoch_adjustment}); + + auto u = file_clock::to_utc(t); + assert(u.time_since_epoch() - t.time_since_epoch() == offset); + + t += 1us; + u = file_clock::to_utc(t); + assert(u.time_since_epoch() - t.time_since_epoch() == offset); + + t += 1us; + u = file_clock::to_utc(t); + if (leap.date() <= file_time_cutoff) { + offset += leap.value(); + } + + assert(u.time_since_epoch() - t.time_since_epoch() == offset); + + t += 1us; + u = file_clock::to_utc(t); + assert(u.time_since_epoch() - t.time_since_epoch() == offset); +} + +void test_gps_tai_clocks_utc() { + const auto tai_epoch{utc_clock::from_sys(sys_days{January / 1 / 1958} - seconds{10})}; + const auto gps_epoch{utc_clock::from_sys(sys_days{January / Sunday[1] / 1980})}; + + assert(tai_clock::to_utc(tai_seconds{0s}) == tai_epoch); + assert(tai_clock::from_utc(tai_epoch) == tai_seconds{0s}); + + assert(gps_clock::to_utc(gps_seconds{0s}) == gps_epoch); + assert(gps_clock::from_utc(gps_epoch) == gps_seconds{0s}); +} + +void test_clock_now() { + auto sys_now = system_clock::now(); + auto utc_now = utc_clock::now(); + auto tai_now = tai_clock::now(); + auto gps_now = gps_clock::now(); + auto file_now = file_clock::now(); + + static_assert(is_same_v, decltype(sys_now)>); + static_assert(is_same_v, decltype(utc_now)>); + static_assert(is_same_v, decltype(tai_now)>); + static_assert(is_same_v, decltype(gps_now)>); + static_assert(is_same_v, decltype(file_now)>); +} + +void test_clock_cast() { + sys_days st(2020y / January / 1); + const auto ut = utc_clock::from_sys(st); + const auto tt = tai_clock::from_utc(ut); + const auto gt = gps_clock::from_utc(ut); + const auto ft = file_clock::from_utc(ut); + + assert(clock_cast(ut) == ut); + assert(clock_cast(st) == ut); + assert(clock_cast(tt) == ut); + assert(clock_cast(gt) == ut); + assert(clock_cast(ft) == ut); + + assert(clock_cast(ut) == st); + assert(clock_cast(st) == st); + assert(clock_cast(tt) == st); + assert(clock_cast(gt) == st); + assert(clock_cast(ft) == st); + + assert(clock_cast(ut) == tt); + assert(clock_cast(st) == tt); + assert(clock_cast(tt) == tt); + assert(clock_cast(gt) == tt); + assert(clock_cast(ft) == tt); + + assert(clock_cast(ut) == gt); + assert(clock_cast(st) == gt); + assert(clock_cast(tt) == gt); + assert(clock_cast(gt) == gt); + assert(clock_cast(ft) == gt); + + assert(clock_cast(ut) == ft); + assert(clock_cast(st) == ft); + assert(clock_cast(tt) == ft); + assert(clock_cast(gt) == ft); + assert(clock_cast(ft) == ft); + + // N4885 [time.clock.utc.overview]/1 Example 1 + assert(clock_cast(sys_seconds{sys_days{1970y / January / 1}}).time_since_epoch() == 0s); + assert(clock_cast(sys_seconds{sys_days{2000y / January / 1}}).time_since_epoch() == 946'684'822s); +} + +static_assert(is_clock_v); +static_assert(is_clock_v); +static_assert(is_clock_v); +static_assert(is_clock_v); + +tzdb copy_tzdb() { + const auto& my_tzdb = get_tzdb_list().front(); + vector zones; + vector links; + transform(my_tzdb.zones.begin(), my_tzdb.zones.end(), back_inserter(zones), + [](const auto& tz) { return time_zone{tz.name()}; }); + transform(my_tzdb.links.begin(), my_tzdb.links.end(), back_inserter(links), [](const auto& link) { + return time_zone_link{link.name(), link.target()}; + }); + + return {my_tzdb.version, move(zones), move(links), my_tzdb.leap_seconds, my_tzdb._All_ls_positive}; +} + +void test() { + assert(test_leap_second()); + static_assert(test_leap_second()); + + // This is the only date/time of a leap second insertion that can be represented by a duration. + assert(utc_clock::to_sys(utc_time>{duration{78796800.0f}}).time_since_epoch().count() + == nextafter(78796800.0f, 0.0f)); + + test_clock_now(); + + seconds offset{0}; + for (const auto& leap : get_tzdb().leap_seconds) { + test_leap_second_info(leap, offset); + test_leap_second_info(leap, offset); + test_utc_clock_to_sys(leap); + test_utc_clock_to_sys(leap); + test_utc_clock_to_sys>(leap); + test_file_clock_from_utc(leap); + test_file_clock_from_utc(leap); + test_utc_clock_from_sys(leap, offset); + test_file_clock_to_utc(leap, offset); + offset += leap.value(); + assert(leap._Elapsed() == offset); + } + test_gps_tai_clocks_utc(); + test_file_clock_utc(); + test_clock_cast(); + + // a negative leap second when the accumulated offset is positive + { + auto my_tzdb = copy_tzdb(); + auto& leap_vec = my_tzdb.leap_seconds; + leap_vec.erase(leap_vec.begin() + 27, leap_vec.end()); + leap_vec.emplace_back(sys_days{1d / January / 2020y}, false, leap_vec.back()._Elapsed()); + leap_vec.emplace_back(sys_days{1d / January / 2021y}, true, leap_vec.back()._Elapsed()); + my_tzdb._All_ls_positive = false; + get_tzdb_list()._Emplace_front(move(my_tzdb)); + } + + offset = 0s; + for (const auto& leap : get_tzdb().leap_seconds) { + test_leap_second_info(leap, offset); + test_leap_second_info(leap, offset); + test_utc_clock_to_sys(leap); + test_utc_clock_to_sys(leap); + test_utc_clock_to_sys>(leap); + test_file_clock_from_utc(leap); + test_file_clock_from_utc(leap); + test_utc_clock_from_sys(leap, offset); + test_file_clock_to_utc(leap, offset); + offset += leap.value(); + assert(leap._Elapsed() == offset); + } + + // positive and negative leap seconds when the accumulated offset is negative + { + auto my_tzdb = copy_tzdb(); + auto& leap_vec = my_tzdb.leap_seconds; + leap_vec.erase(leap_vec.begin() + 27, leap_vec.end()); + for (int i = 0; i < 30; ++i) { + leap_vec.emplace_back(sys_days{1d / January / year{i + 2020}}, false, leap_vec.back()._Elapsed()); + } + leap_vec.emplace_back(sys_days{1d / January / 2060y}, true, leap_vec.back()._Elapsed()); + get_tzdb_list()._Emplace_front(move(my_tzdb)); + } + + offset = 0s; + for (const auto& leap : get_tzdb().leap_seconds) { + test_leap_second_info(leap, offset); + test_leap_second_info(leap, offset); + test_utc_clock_to_sys(leap); + test_utc_clock_to_sys(leap); + test_utc_clock_to_sys>(leap); + test_file_clock_from_utc(leap); + test_file_clock_from_utc(leap); + test_utc_clock_from_sys(leap, offset); + test_file_clock_to_utc(leap, offset); + offset += leap.value(); + assert(leap._Elapsed() == offset); + } +} + +int main() { + run_tz_test([] { test(); }); +} diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp index fefbba69291..daf8868690a 100644 --- a/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_io/test.cpp @@ -1,17 +1,25 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +#include #include +#include #include #include +#include +#include #include #include #include #include +#include #include +#include + +#include using namespace std; -using chrono::duration; +using namespace std::chrono; template bool test_duration_basic_out(const duration& d, const CharT* expected) { @@ -21,7 +29,7 @@ bool test_duration_basic_out(const duration& d, const CharT* expect return ss.str() == expected; } -#define WIDEN(TYPE, STR) get(make_pair(STR, L##STR)); +#define WIDEN(TYPE, STR) get(pair{STR, L##STR}); template bool test_duration_locale_out() { @@ -119,7 +127,1018 @@ void test_duration_output() { assert(test_duration_locale_out()); } -int main() { + +template +void test_parse(const CharT* str, const CharT* fmt, Parsable& p, type_identity_t*> abbrev = nullptr, + minutes* offset = nullptr) { + p = Parsable{}; + if (abbrev) { + if constexpr (is_same_v) { + *abbrev = "!"; + } else { + *abbrev = L"!"; + } + } + + basic_stringstream sstr{str}; + if (abbrev) { + if (offset) { + sstr >> parse(basic_string{fmt}, p, *abbrev, *offset); + } else { + sstr >> parse(basic_string{fmt}, p, *abbrev); + } + } else { + if (offset) { + sstr >> parse(basic_string{fmt}, p, *offset); + } else { + sstr >> parse(basic_string{fmt}, p); + } + } + + assert(sstr); +} + +template +void fail_parse(const CharT* str, const CharT* fmt, Parsable& p, type_identity_t*> abbrev = nullptr, + minutes* offset = nullptr) { + p = Parsable{}; + if (abbrev) { + if constexpr (is_same_v) { + *abbrev = "!"; + } else { + *abbrev = L"!"; + } + } + + if (offset) { + *offset = minutes::min(); + } + + basic_stringstream sstr{str}; + if (abbrev) { + if (offset) { + sstr >> parse(basic_string{fmt}, p, *abbrev, *offset); + } else { + sstr >> parse(basic_string{fmt}, p, *abbrev); + } + } else { + if (offset) { + sstr >> parse(basic_string{fmt}, p, *offset); + } else { + sstr >> parse(basic_string{fmt}, p); + } + } + + assert(!sstr); +} + +template +void test_limits(const char* flag, const IntType min, const IntType max) { + char buffer[24]; + TimeType value; + auto conv_result = to_chars(begin(buffer), end(buffer), static_cast>(min) - 1); + assert(conv_result.ec == errc{} && conv_result.ptr != end(buffer)); + *conv_result.ptr = '\0'; + fail_parse(buffer, flag, value); + conv_result = to_chars(begin(buffer), end(buffer), max + 1); + assert(conv_result.ec == errc{} && conv_result.ptr != end(buffer)); + *conv_result.ptr = '\0'; + fail_parse(buffer, flag, value); + + conv_result = to_chars(begin(buffer), end(buffer), min); + assert(conv_result.ec == errc{} && conv_result.ptr != end(buffer)); + *conv_result.ptr = '\0'; + test_parse(buffer, flag, value); + assert(value == TimeType{min}); + conv_result = to_chars(begin(buffer), end(buffer), max); + assert(conv_result.ec == errc{} && conv_result.ptr != end(buffer)); + *conv_result.ptr = '\0'; + test_parse(buffer, flag, value); + assert(value == TimeType{max}); +} + +void parse_seconds() { + seconds time; + test_parse("1", "%S", time); + assert(time == 1s); + test_parse("12", "%S", time); + assert(time == 12s); + test_parse("234", "%S4", time); + assert(time == 23s); + test_parse("0345", "%3S5", time); + assert(time == 34s); + test_parse(" 456", "%3S6", time); + assert(time == 45s); + test_parse("99", "%S", time); // not out-of-range for duration + assert(time == 99s); + + milliseconds time_ms; + test_parse("12.543", "%S", time_ms); + assert(time_ms == 12s + 543ms); + assert(time_ms == seconds{12} + milliseconds{543}); + test_parse("01.234", "%S", time_ms); + assert(time_ms == 1'234ms); + test_parse(" 1.234", "%S", time_ms); + assert(time_ms == 1'234ms); + test_parse("1.234", "%S", time_ms); + assert(time_ms == 1'234ms); + test_parse("1. 234", "%S 234", time_ms); // Flag should consume "1.". + assert(time_ms == 1s); + test_parse("1 .234", "%S .234", time_ms); // Flag should consume "1". + assert(time_ms == 1s); + test_parse("12..345", "%S.345", time_ms); // Flag should consume "12.". + assert(time_ms == 12s); + fail_parse("1.2345", "%6S", time_ms); // would truncate + test_parse("1.2340", "%6S", time_ms); + assert(time_ms == 1'234ms); + + duration time_atto; + test_parse("0.400000000000000002", "%S", time_atto); + assert((time_atto == duration{4} + duration{2})); + + fail_parse("1.2 1.3", "%S %S", time_ms); + fail_parse("1.2 2.2", "%S %S", time_ms); +} + +void parse_minutes() { + seconds time; + test_parse("1", "%M", time); + assert(time == 1min); + test_parse("12", "%M", time); + assert(time == 12min); + test_parse("234", "%M4", time); + assert(time == 23min); + test_parse("0345", "%3M5", time); + assert(time == 34min); + test_parse(" 456", "%3M6", time); + assert(time == 45min); + test_parse("99", "%M", time); // not out-of-range for duration + assert(time == 99min); +} + +void parse_hours() { + seconds time; + + fail_parse("0", "%I", time); + test_parse("1", "%I", time); + assert(time == 1h); + test_parse("11", "%I", time); + assert(time == 11h); + test_parse("12", "%I", time); // assumes a.m. + assert(time == 0h); + fail_parse("13", "%I", time); + + fail_parse("0", "%OI", time); + test_parse("1", "%OI", time); + assert(time == 1h); + test_parse("11", "%OI", time); + assert(time == 11h); + test_parse("12", "%OI", time); // assumes a.m. + assert(time == 0h); + fail_parse("13", "%OI", time); + + test_parse("110", "%I0", time); + assert(time == 11h); + test_parse("0011", "%3I1", time); + assert(time == 1h); + test_parse("010", "%4I", time); + assert(time == 10h); + test_parse(" 12", "%4I", time); + assert(time == 0h); // assumes A.M. with no %p flag + + test_parse("1", "%H", time); + assert(time == 1h); + test_parse("12", "%H", time); + assert(time == 12h); + test_parse("234", "%H4", time); + assert(time == 23h); + test_parse("0123", "%3H3", time); + assert(time == 12h); + test_parse(" 234", "%3H4", time); + assert(time == 23h); + test_parse("30", "%H", time); // not out-of-range for duration + assert(time == 30h); + + // any permutation of %I, %p, and %H should be valid, as long as they're consistent + test_parse("8 pm 20", "%I %p %H", time); + assert(time == 20h); + test_parse("8 20 pm", "%I %H %p", time); + assert(time == 20h); + test_parse("pm 8 20", "%p %I %H", time); + assert(time == 20h); + test_parse("pm 20 8", "%p %H %I", time); + assert(time == 20h); + test_parse("20 pm 8", "%H %p %I", time); + assert(time == 20h); + test_parse("20 8 pm", "%H %I %p", time); + assert(time == 20h); + + // inconsistent + fail_parse("8 am 20", "%I %p %H", time); + fail_parse("8 20 am", "%I %H %p", time); + fail_parse("am 8 20", "%p %I %H", time); + fail_parse("am 20 8", "%p %H %I", time); + fail_parse("20 am 8", "%H %p %I", time); + fail_parse("20 8 am", "%H %I %p", time); +} + +void parse_other_duration() { + seconds time; + test_parse("42", "%j", time); + assert(time == days{42}); + duration time_micro; // maximum ~35.8 minutes + fail_parse("1", "%j", time_micro); + test_parse("400", "%j", time); + assert(time == days{400}); // not out-of-range for duration + + test_parse(" 1:23:42", "%T", time); + assert(time == 1h + 23min + 42s); + test_parse("01:23:42", "%T", time); + assert(time == 1h + 23min + 42s); + + test_parse("11: 2:42", "%T", time); + assert(time == 11h + 2min + 42s); + test_parse("11:02:42", "%T", time); + assert(time == 11h + 2min + 42s); + + test_parse("12:34: 4", "%T", time); + assert(time == 12h + 34min + 4s); + test_parse("12:34:04", "%T", time); + assert(time == 12h + 34min + 4s); + test_parse("00:34:04", "%T", time); + assert(time == 34min + 4s); + + milliseconds time_milli; + test_parse("12:34:56.789", "%T", time_milli); + assert(time_milli == 12h + 34min + 56s + 789ms); + + // locale's time representation %X (== "%H : %M : %S") + test_parse("12:34:04", "%X", time); + assert(time == 12h + 34min + 4s); + + // floating-point representations, parsing precision controlled by duration::period + duration df; + test_parse("9.125", "%S", df); + assert(df.count() == 9125.0f); + + duration dd; + test_parse("1.875", "%S", dd); + assert(dd.count() == 1875.0); +} + +void parse_time_zone() { + seconds time; + minutes offset; + test_parse("-0430", "%z", time, nullptr, &offset); + assert(offset == -(4h + 30min)); + test_parse("+0430", "%z", time, nullptr, &offset); + assert(offset == (4h + 30min)); + test_parse("0430", "%z", time, nullptr, &offset); + assert(offset == (4h + 30min)); + test_parse("11", "%z", time, nullptr, &offset); + assert(offset == 11h); + + fail_parse("4", "%z", time, nullptr, &offset); + fail_parse("043", "%z", time, nullptr, &offset); + fail_parse("!0430", "%z", time, nullptr, &offset); + + test_parse("-04:30", "%Ez", time, nullptr, &offset); + assert(offset == -(4h + 30min)); + test_parse("+04:30", "%Ez", time, nullptr, &offset); + assert(offset == (4h + 30min)); + test_parse("04:30", "%Ez", time, nullptr, &offset); + assert(offset == (4h + 30min)); + test_parse("4:30", "%Ez", time, nullptr, &offset); + assert(offset == (4h + 30min)); + test_parse("11", "%Ez", time, nullptr, &offset); + assert(offset == 11h); + test_parse("4", "%Ez", time, nullptr, &offset); + assert(offset == 4h); + + fail_parse("!4", "%Ez", time, nullptr, &offset); + // %Ez matches "04", leaving "30" in the stream. + fail_parse("0430 meow", "%Ez meow", time, nullptr, &offset); + fail_parse("04:", "%Ez", time, nullptr, &offset); + fail_parse("04:3", "%Ez", time, nullptr, &offset); + + string tz_name; + test_parse("UTC", "%Z", time, &tz_name); + assert(tz_name == "UTC"); + test_parse("Etc/GMT+11", "%Z", time, &tz_name); + assert(tz_name == "Etc/GMT+11"); + test_parse("Etc/GMT-4", "%Z", time, &tz_name); + assert(tz_name == "Etc/GMT-4"); + test_parse("Asia/Hong_Kong", "%Z", time, &tz_name); + assert(tz_name == "Asia/Hong_Kong"); + + fail_parse("Not_valid! 00", "%Z %H", time, &tz_name); + test_parse("Valid_Tz! 07", "%Z! %H", time, &tz_name); + assert(tz_name == "Valid_Tz" && time == 7h); +} + +void parse_calendar_types_basic() { + // basic day tests + day d; + test_parse("1", "%d", d); + assert(d == day{1}); + test_parse("23", "%d", d); + assert(d == day{23}); + test_parse("012", "%d2", d); + assert(d == day{1}); + test_parse(" 23", "%d3", d); + assert(d == day{2}); + + test_limits("%d", 1, 31); + + test_parse("1", "%e", d); + assert(d == day{1}); + test_parse("23", "%e", d); + assert(d == day{23}); + test_parse("012", "%e2", d); + assert(d == day{1}); + test_parse(" 23", "%e3", d); + assert(d == day{2}); + + // basic weekday tests + weekday wd; + + test_parse("Mon", "%a", wd); + assert(wd == Monday); + test_parse("wedNesday", "%A", wd); + assert(wd == Wednesday); + + test_parse("1", "%w", wd); // 0-based, Sunday=0 + assert(wd == Monday); + test_parse("1", "%u", wd); // ISO 1-based, Monday=1 + assert(wd == Monday); + test_parse("7", "%u", wd); + assert(wd == Sunday); + + test_limits("%w", 0, 6); + test_limits("%u", 1, 7); + + // basic month tests + month m; + test_parse("Apr", "%b", m); + assert(m == April); + test_parse("deCeMbeR", "%b", m); + assert(m == December); + test_parse("September", "%B", m); + assert(m == September); + test_parse("February", "%h", m); + assert(m == February); + + test_parse("3", "%m", m); + assert(m == March); + test_parse("11", "%m", m); + assert(m == November); + test_parse("110", "%m0", m); + assert(m == November); + test_parse("0011", "%3m1", m); + assert(m == January); + test_parse("010", "%4m", m); + assert(m == October); + test_parse(" 12", "%4m", m); + assert(m == December); + + test_limits("%m", 1, 12); + + // basic year tests + year y; + test_parse("1777", "%Y", y); + assert(y == 1777y); + test_parse("07 17", "%y %C", y); + assert(y == 1707y); + test_parse("18 077", "%C %3y", y); + assert(y == 1877y); + + // interpretation of two-digit year by itself + test_parse("00", "%y", y); + assert(y == 2000y); + test_parse("68", "%y", y); + assert(y == 2068y); + test_parse("69", "%y", y); + assert(y == 1969y); + test_parse("99", "%y", y); + assert(y == 1999y); + + // negative century + test_parse("-1 5", "%C %y", y); + assert(y == -95y); + + // check consistency, or lack thereof + test_parse("1887 18 87", "%Y %C %y", y); + assert(y == 1887y); + fail_parse("1888 18 87", "%Y %C %y", y); + fail_parse("1887 19 87", "%Y %C %y", y); + fail_parse("1887 18 88", "%Y %C %y", y); + + // basic month_day tests + month_day md; + test_parse("1 Jan 1", "%j %b %d", md); + assert(md == January / 1d); + test_parse("32 Feb 1", "%j %b %d", md); + assert(md == February / 1d); + test_parse("59 Feb 28", "%j %b %d", md); + assert(md == February / 28d); + fail_parse("0", "%j", md); + fail_parse("60", "%j", md); // could be Feb 29 or Mar 1 + + test_parse("January 1", "%b %d", md); + assert(md == January / 1d); + test_parse("January 31", "%b %d", md); + assert(md == January / 31d); + + test_parse("February 1", "%b %d", md); + assert(md == February / 1d); + test_parse("February 29", "%b %d", md); + assert(md == February / 29d); + + test_parse("March 1", "%b %d", md); + assert(md == March / 1d); + test_parse("March 31", "%b %d", md); + assert(md == March / 31d); + + test_parse("April 1", "%b %d", md); + assert(md == April / 1d); + test_parse("April 30", "%b %d", md); + assert(md == April / 30d); + + test_parse("May 1", "%b %d", md); + assert(md == May / 1d); + test_parse("May 31", "%b %d", md); + assert(md == May / 31d); + + test_parse("June 1", "%b %d", md); + assert(md == June / 1d); + test_parse("June 30", "%b %d", md); + assert(md == June / 30d); + + test_parse("July 1", "%b %d", md); + assert(md == July / 1d); + test_parse("July 31", "%b %d", md); + assert(md == July / 31d); + + test_parse("August 1", "%b %d", md); + assert(md == August / 1d); + test_parse("August 31", "%b %d", md); + assert(md == August / 31d); + + test_parse("September 1", "%b %d", md); + assert(md == September / 1d); + test_parse("September 30", "%b %d", md); + assert(md == September / 30d); + + test_parse("October 1", "%b %d", md); + assert(md == October / 1d); + test_parse("October 31", "%b %d", md); + assert(md == October / 31d); + + test_parse("November 1", "%b %d", md); + assert(md == November / 1d); + test_parse("November 30", "%b %d", md); + assert(md == November / 30d); + + test_parse("December 1", "%b %d", md); + assert(md == December / 1d); + test_parse("December 31", "%b %d", md); + assert(md == December / 31d); + + // not ambiguous with year + year_month_day ymd; + test_parse("60 2004-02-29", "%j %F", ymd); + assert(ymd == 2004y / February / 29); + + // basic year_month_day tests + // different ways of specifying year + test_parse("12-01-1997", "%d-%m-%Y", ymd); + assert(ymd == 12d / January / 1997y); + test_parse("12-01-19 97", "%d-%m-%C %y", ymd); + assert(ymd == 12d / January / 1997y); + test_parse("12-01-97", "%d-%m-%y", ymd); + assert(ymd == 12d / January / 1997y); + + // basic %D test + test_parse("07/04/76 17", "%D %C", ymd); + assert(ymd == July / 4d / 1776y); + // locale's date representation %x (== "%d / %m / %y") + test_parse("04/07/76 17", "%x %C", ymd); + assert(ymd == 4d / July / 1776y); + test_parse("10/12/15 18", "%x %C", ymd); + assert(ymd == 10d / December / 1815y); + + // day-of-year tests, leap and non-leap years + test_parse("60 2001-03-01", "%j %F", ymd); + assert(ymd == 2001y / March / 1d); + test_parse("61 2004-03-01", "%j %F", ymd); + assert(ymd == 2004y / March / 1d); + test_parse("90 2001-03-31", "%j %F", ymd); + assert(ymd == 2001y / March / last); + test_parse("91 2004-03-31", "%j %F", ymd); + assert(ymd == 2004y / March / last); + + test_parse("91 2001-04-01", "%j %F", ymd); + assert(ymd == 2001y / April / 1d); + test_parse("92 2004-04-01", "%j %F", ymd); + assert(ymd == 2004y / April / 1d); + test_parse("120 2001-04-30", "%j %F", ymd); + assert(ymd == 2001y / April / last); + test_parse("121 2004-04-30", "%j %F", ymd); + assert(ymd == 2004y / April / last); + + test_parse("121 2001-05-01", "%j %F", ymd); + assert(ymd == 2001y / May / 1d); + test_parse("122 2004-05-01", "%j %F", ymd); + assert(ymd == 2004y / May / 1d); + test_parse("151 2001-05-31", "%j %F", ymd); + assert(ymd == 2001y / May / last); + test_parse("152 2004-05-31", "%j %F", ymd); + assert(ymd == 2004y / May / last); + + test_parse("152 2001-06-01", "%j %F", ymd); + assert(ymd == 2001y / June / 1d); + test_parse("153 2004-06-01", "%j %F", ymd); + assert(ymd == 2004y / June / 1d); + test_parse("181 2001-06-30", "%j %F", ymd); + assert(ymd == 2001y / June / last); + test_parse("182 2004-06-30", "%j %F", ymd); + assert(ymd == 2004y / June / last); + + test_parse("182 2001-07-01", "%j %F", ymd); + assert(ymd == 2001y / July / 1d); + test_parse("183 2004-07-01", "%j %F", ymd); + assert(ymd == 2004y / July / 1d); + test_parse("212 2001-07-31", "%j %F", ymd); + assert(ymd == 2001y / July / last); + test_parse("213 2004-07-31", "%j %F", ymd); + assert(ymd == 2004y / July / last); + + test_parse("213 2001-08-01", "%j %F", ymd); + assert(ymd == 2001y / August / 1d); + test_parse("214 2004-08-01", "%j %F", ymd); + assert(ymd == 2004y / August / 1d); + test_parse("243 2001-08-31", "%j %F", ymd); + assert(ymd == 2001y / August / last); + test_parse("244 2004-08-31", "%j %F", ymd); + assert(ymd == 2004y / August / last); + + test_parse("244 2001-09-01", "%j %F", ymd); + assert(ymd == 2001y / September / 1d); + test_parse("245 2004-09-01", "%j %F", ymd); + assert(ymd == 2004y / September / 1d); + test_parse("273 2001-09-30", "%j %F", ymd); + assert(ymd == 2001y / September / last); + test_parse("274 2004-09-30", "%j %F", ymd); + assert(ymd == 2004y / September / last); + + test_parse("274 2001-10-01", "%j %F", ymd); + assert(ymd == 2001y / October / 1d); + test_parse("275 2004-10-01", "%j %F", ymd); + assert(ymd == 2004y / October / 1d); + test_parse("304 2001-10-31", "%j %F", ymd); + assert(ymd == 2001y / October / last); + test_parse("305 2004-10-31", "%j %F", ymd); + assert(ymd == 2004y / October / last); + + test_parse("305 2001-11-01", "%j %F", ymd); + assert(ymd == 2001y / November / 1d); + test_parse("306 2004-11-01", "%j %F", ymd); + assert(ymd == 2004y / November / 1d); + test_parse("334 2001-11-30", "%j %F", ymd); + assert(ymd == 2001y / November / last); + test_parse("335 2004-11-30", "%j %F", ymd); + assert(ymd == 2004y / November / last); + + test_parse("335 2001-12-01", "%j %F", ymd); + assert(ymd == 2001y / December / 1d); + test_parse("336 2004-12-01", "%j %F", ymd); + assert(ymd == 2004y / December / 1d); + test_parse("365 2001-12-31", "%j %F", ymd); + assert(ymd == 2001y / December / last); + test_parse("366 2004-12-31", "%j %F", ymd); + assert(ymd == 2004y / December / last); + + fail_parse("366 2001", "%j %Y", ymd); + fail_parse("367 2004", "%j %Y", ymd); + + // Check consistency between date and day-of-week + test_parse("Wed 2000-03-01", "%a %F", ymd); + fail_parse("Mon 2000-03-01", "%a %F", ymd); + + // For %F, width is applied only to the year + test_parse("12345-06-07", "%5F", ymd); + assert(ymd == 7d / June / 12345y); + fail_parse("12345-00006-07", "%5F", ymd); + fail_parse("12345-06-00007", "%5F", ymd); + fail_parse("12345-00006-00007", "%5F", ymd); +} + +void parse_iso_week_date() { + year_month_day ymd; + test_parse("2005-W52-6", "%G-W%V-%u", ymd); + assert(ymd == 2005y / December / 31d); + test_parse("2005-W52-7", "%G-W%V-%u", ymd); + assert(ymd == 2006y / January / 1d); + test_parse("2006-W01-1", "%G-W%V-%u", ymd); + assert(ymd == 2006y / January / 2d); + fail_parse("2006-W00-1", "%G-W%V-%u", ymd); + + test_parse("2007-W52-7", "%G-W%V-%u", ymd); + assert(ymd == 2007y / December / 30d); + test_parse("2008-W01-1", "%G-W%V-%u", ymd); + assert(ymd == 2007y / December / 31d); + test_parse("2008-W01-2", "%G-W%V-%u", ymd); + assert(ymd == 2008y / January / 1d); + + fail_parse("05-W52-6", "%g-W%V-%u", ymd); // no century + + year_month_day ref{2005y / December / 31d}; + fail_parse("2005-W52-6 19", "%G-W%V-%u %C", ymd); // inconsistent century + test_parse("2005-W52-6 20", "%G-W%V-%u %C", ymd); // consistent century + assert(ymd == ref); + test_parse("05-W52-6 20", "%g-W%V-%u %C", ymd); + assert(ymd == ref); + + fail_parse("2005-W52-6 2004", "%G-W%V-%u %Y", ymd); // inconsistent year + test_parse("2005-W52-6 2005", "%G-W%V-%u %Y", ymd); // consistent year + assert(ymd == ref); + test_parse("05-W52-6 2005", "%g-W%V-%u %Y", ymd); + assert(ymd == ref); + test_parse("2005-W52-6 05", "%G-W%V-%u %y", ymd); + assert(ymd == ref); + test_parse("05-W52-6 05", "%g-W%V-%u %y", ymd); + assert(ymd == ref); + + ref = 2007y / December / 31d; + fail_parse("2008-W01-1 2008", "%G-W%V-%u %Y", ymd); // inconsistent year (!) + test_parse("2008-W01-1 2007", "%G-W%V-%u %Y", ymd); // consistent year + assert(ymd == ref); + test_parse("08-W01-1 2007", "%g-W%V-%u %Y", ymd); + assert(ymd == ref); + test_parse("2008-W01-1 07", "%G-W%V-%u %y", ymd); + assert(ymd == ref); + test_parse("08-W01-1 07", "%g-W%V-%u %y", ymd); + assert(ymd == ref); + + // ISO and Gregorian years in different centuries + test_parse("1699-W53-5 1700", "%G-W%V-%u %Y", ymd); + assert(ymd == 1d / January / 1700y); + fail_parse("1699-W54-5 1700", "%G-W%V-%u %Y", ymd); + fail_parse("1699-W53-5 00", "%G-W%V-%u %y", ymd); // inconsistent %y (== 2000) + fail_parse("99-W53-5 16 00", "%g-W%V-%u %C %y", ymd); // inconsistent %C+%y year (== 1600) + fail_parse("99-W53-5 17 00", "%g-W%V-%u %C %y", ymd); // inconsistent %C+%g ISO year (== 1700) + + // This is expected to parse successfully. Even though %C+%g would give the wrong year, + // as above we don't try to use that for the ISO year when %G is present. + test_parse("1699 99-W53-5 17 00", "%G %g-W%V-%u %C %y", ymd); + assert(ymd == 1d / January / 1700y); + fail_parse("1699 98-W53-5 17 00", "%G %g-W%V-%u %C %y", ymd); +} + +void parse_other_week_date() { + year_month_day ymd; + // Year begins on Sunday. + test_parse("2017-01-0", "%Y-%U-%w", ymd); + assert(ymd == 2017y / January / 1d); + test_parse("2017-00-0", "%Y-%W-%w", ymd); + assert(ymd == 2017y / January / 1d); + test_parse("2017-53-0", "%Y-%U-%w", ymd); + assert(ymd == 2017y / December / 31d); + + fail_parse("2017/-1/0", "%Y/%W/%w", ymd); + fail_parse("2017/-1/0", "%Y/%U/%w", ymd); + fail_parse("2017-54-0", "%Y-%W-%w", ymd); + fail_parse("2017-54-0", "%Y-%U-%w", ymd); + fail_parse("2018-00-0", "%Y-%U-%w", ymd); // refers to 31 Dec. 2017 + fail_parse("2017-53-1", "%Y-%U-%w", ymd); // refers to 01 Jan. 2018 + + // Year begins on Monday. + test_parse("2018-00-1", "%Y-%U-%w", ymd); + assert(ymd == 2018y / January / 1d); + test_parse("2018-01-1", "%Y-%W-%w", ymd); + assert(ymd == 2018y / January / 1d); + test_parse("2018-53-1", "%Y-%W-%w", ymd); + assert(ymd == 2018y / December / 31d); + + // Year begins on Tuesday. + test_parse("2019-00-2", "%Y-%U-%w", ymd); + assert(ymd == 2019y / January / 1d); + test_parse("2019-00-2", "%Y-%W-%w", ymd); + assert(ymd == 2019y / January / 1d); + + // Year begins on Wednesday. + test_parse("2020-00-3", "%Y-%U-%w", ymd); + assert(ymd == 2020y / January / 1d); + test_parse("2020-00-3", "%Y-%W-%w", ymd); + assert(ymd == 2020y / January / 1d); + + // Year begins on Thursday. + test_parse("2015-00-4", "%Y-%U-%w", ymd); + assert(ymd == 2015y / January / 1d); + test_parse("2015-00-4", "%Y-%W-%w", ymd); + assert(ymd == 2015y / January / 1d); + + // Year begins on Friday. + test_parse("2016-00-5", "%Y-%U-%w", ymd); + assert(ymd == 2016y / January / 1d); + test_parse("2016-00-5", "%Y-%W-%w", ymd); + assert(ymd == 2016y / January / 1d); + + // Year begins on Saturday. + test_parse("2022-00-6", "%Y-%U-%w", ymd); + assert(ymd == 2022y / January / 1d); + test_parse("2022-00-6", "%Y-%W-%w", ymd); + assert(ymd == 2022y / January / 1d); +} + +void parse_whitespace() { + seconds time; + fail_parse("ab", "a%nb", time); + test_parse("a b", "a%nb", time); + fail_parse("a b", "a%nb", time); + fail_parse("a", "a%n", time); + test_parse("a ", "a%n", time); + + test_parse("ab", "a%tb", time); + test_parse("a b", "a%tb", time); + fail_parse("a b", "a%tb", time); + test_parse("a", "a%t", time); + test_parse("a ", "a%t", time); + + test_parse("a", "a ", time); + test_parse("", "", time); + test_parse("", " ", time); + test_parse("", "%t", time); + fail_parse("", "%n", time); +} + +void insert_leap_second(const sys_days& date, const seconds& value) { + const auto& my_tzdb = get_tzdb_list().front(); + vector zones; + vector links; + transform(my_tzdb.zones.begin(), my_tzdb.zones.end(), back_inserter(zones), + [](const auto& tz) { return time_zone{tz.name()}; }); + transform(my_tzdb.links.begin(), my_tzdb.links.end(), back_inserter(links), [](const auto& link) { + return time_zone_link{link.name(), link.target()}; + }); + + auto leap_vec = my_tzdb.leap_seconds; + leap_vec.emplace_back(date, value == 1s, leap_vec.back()._Elapsed()); + get_tzdb_list()._Emplace_front( + tzdb{my_tzdb.version, move(zones), move(links), move(leap_vec), my_tzdb._All_ls_positive && (value == 1s)}); +} + +void parse_timepoints() { + sys_seconds ref = sys_days{2020y / October / 29d} + 19h + 1min + 42s; + sys_seconds st; + utc_seconds ut; + file_time ft; + + test_parse("oct 29 19:01:42 2020", "%c", st); + test_parse("oct 29 19:01:42 2020", "%c", ut); + test_parse("oct 29 19:01:42 2020", "%c", ft); + + assert(st == ref); + assert(ut == utc_clock::from_sys(ref)); + assert(ft == clock_cast(ref)); + + test_parse("oct 29 19:01:42 2020 0430", "%c %z", st); + assert(st == ref - (4h + 30min)); + + // N4878 [time.clock.tai]/1: + // The clock tai_clock measures seconds since 1958-01-01 00:00:00 and is offset 10s ahead of UTC at this date. + // That is, 1958-01-01 00:00:00 TAI is equivalent to 1957-12-31 23:59:50 UTC. Leap seconds are not inserted into + // TAI. Therefore every time a leap second is inserted into UTC, UTC shifts another second with respect to TAI. + // For example by 2000-01-01 there had been 22 positive and 0 negative leap seconds inserted so 2000-01-01 + // 00:00:00 UTC is equivalent to 2000-01-01 00:00:32 TAI (22s plus the initial 10s offset). + + ref = sys_days{1957y / December / 31d} + days{1} - 10s; + tai_seconds tt; + test_parse("jan 1 00:00:00 1958", "%c", tt); + assert(tt == clock_cast(ref)); + + ref = sys_days{2000y / January / 1d}; + test_parse("jan 1 00:00:32 2000", "%c", tt); + assert(tt == clock_cast(ref)); + + // N4878 [time.clock.gps]/1: + // The clock gps_clock measures seconds since the first Sunday of January, 1980 00:00:00 UTC. Leap seconds are + // not inserted into GPS. Therefore every time a leap second is inserted into UTC, UTC shifts another second + // with respect to GPS. Aside from the offset from 1958y/January/1 to 1980y/January/Sunday[1], GPS is behind TAI + // by 19s due to the 10s offset between 1958 and 1970 and the additional 9 leap seconds inserted between 1970 + // and 1980. + + gps_seconds gt; + ref = sys_days{1980y / January / 6d}; + test_parse("jan 6 00:00:00 1980", "%c", gt); + assert(gt == clock_cast(ref)); + test_parse("jan 6 00:00:19 1980", "%c", tt); + assert(gt == clock_cast(tt)); + + seconds time; + test_parse(" 1:23:42 am", "%r", time); + assert(time == 1h + 23min + 42s); + test_parse("2000-01-02 01:23:42 pm", "%F %r", st); + assert(st == sys_days{2000y / January / 2d} + (13h + 23min + 42s)); + + test_parse("11: 2:42 am", "%r", time); + assert(time == 11h + 2min + 42s); + test_parse("2000-01-02 11:02:42 pm", "%F %r", st); + assert(st == sys_days{2000y / January / 2d} + (23h + 2min + 42s)); + + test_parse("12:34: 4 am", "%r", time); + assert(time == 34min + 4s); + test_parse("2000-01-02 12:34:04 pm", "%F %r", st); + assert(st == sys_days{2000y / January / 2d} + (12h + 34min + 4s)); + + + test_parse(" 3:14 2000-01-02", "%R %F", st); + assert(st == sys_days{2000y / January / 2d} + (3h + 14min)); + test_parse("03:14 2000-01-02", "%R %F", st); + assert(st == sys_days{2000y / January / 2d} + (3h + 14min)); + + test_parse("11: 3 2000-01-02", "%R %F", st); + assert(st == sys_days{2000y / January / 2d} + (11h + 3min)); + test_parse("11:03 2000-01-02", "%R %F", st); + assert(st == sys_days{2000y / January / 2d} + (11h + 3min)); + + test_parse("00:42 2000-01-02", "%R %F", st); + assert(st == sys_days{2000y / January / 2d} + (42min)); + test_parse("12:42 2000-01-02", "%R %F", st); + assert(st == sys_days{2000y / January / 2d} + (12h + 42min)); + + // Historical leap seconds don't allow complete testing, because they've all been positive and there haven't been + // any since 2016 (as of 2021). + insert_leap_second(1d / January / 2020y, -1s); + insert_leap_second(1d / January / 2022y, 1s); + + utc_seconds ut_ref = utc_clock::from_sys(sys_days{1d / July / 1972y}) - 1s; // leap second insertion + test_parse("june 30 23:59:60 1972", "%c", ut); + assert(ut == ut_ref); + + // Test a later leap second, where the accumulated offset is greater than 1s. + ut_ref = utc_clock::from_sys(sys_days{1d / July / 1992y}) - 1s; + test_parse("june 30 23:59:60 1992", "%c", ut); + assert(ut == ut_ref); + + // not leap-second aware + fail_parse("june 30 23:59:60 1972", "%c", st); + fail_parse("june 30 23:59:60 1972", "%c", tt); + fail_parse("june 30 23:59:60 1972", "%c", gt); + fail_parse("june 30 23:59:60 1972", "%c", ft); + + fail_parse("june 30 23:59:60 1973", "%c", ut); // not a leap second insertion + + ref = sys_days{1d / January / 2020y} - 1s; // negative leap second, UTC time doesn't exist + fail_parse("dec 31 23:59:59 2019", "%c", ut); + fail_parse("dec 31 23:59:59 2019", "%c", st); + fail_parse("dec 31 23:59:59 2019", "%c", ft); + + local_seconds lt; + test_parse("dec 31 23:59:59 2019", "%c", lt); // Not UTC, might be valid depending on the time zone. + assert(lt.time_since_epoch() == ref.time_since_epoch()); + + // Initially, TAI - UTC == 37s. + test_parse("dec 31 23:59:59 2019", "%c", tt); + test_parse("dec 31 23:59:22 2019", "%c", ut); + assert(tt == clock_cast(ut)); + + test_parse("jan 01 00:00:35 2020", "%c", tt); + test_parse("dec 31 23:59:58 2019", "%c", ut); + assert(tt == clock_cast(ut)); + + test_parse("jan 01 00:00:36 2020", "%c", tt); + test_parse("jan 01 00:00:00 2020", "%c", ut); + assert(tt == clock_cast(ut)); + + // Initially, GPS - UTC == 18s + test_parse("dec 31 23:59:59 2019", "%c", gt); + test_parse("dec 31 23:59:41 2019", "%c", ut); + assert(gt == clock_cast(ut)); + + test_parse("jan 01 00:00:16 2020", "%c", gt); + test_parse("dec 31 23:59:58 2019", "%c", ut); + assert(gt == clock_cast(ut)); + + test_parse("jan 01 00:00:17 2020", "%c", gt); + test_parse("jan 01 00:00:00 2020", "%c", ut); + assert(gt == clock_cast(ut)); + + + ut_ref = utc_clock::from_sys(sys_days{1d / January / 2022y}) - 1s; // leap second insertion + test_parse("dec 31 23:59:60 2021", "%c", ut); + assert(ut == ut_ref); + test_parse("dec 31 23:59:60 2021", "%c", ft); + assert(ft == clock_cast(ut_ref)); + + + // GH-1606: reads too many leading zeros + test_parse("19700405T000006", "%Y%m%dT%H%M%S", st); + assert(st == sys_days{5d / April / 1970y} + 6s); + + // GH-1280 tests + year_month_day ymd; + test_parse("20200609", "%Y%m%d", ymd); + assert(ymd == 9d / June / 2020y); + + test_parse("20201213", "%Y%m%d", ymd); + assert(ymd == 13d / December / 2020y); + + test_parse("2020112", "%Y%m%d", ymd); + assert(ymd == 2d / November / 2020y); + + test_parse("2020061125", "%Y%m%d", ymd); + assert(ymd == 11d / June / 2020y); + + test_parse("2020120625119", "%Y%m%d", ymd); + assert(ymd == 6d / December / 2020y); + + test_parse("2020092Text", "%Y%m%d", ymd); + assert(ymd == 2d / September / 2020y); + + test_parse("20200609", "%Y%m%d", ymd); + assert(ymd == 9d / June / 2020y); + + test_parse("2020112", "%Y%m%d", ymd); + assert(ymd == 2d / November / 2020y); + + test_parse("2020061125", "%Y%m%d", ymd); + assert(ymd == 11d / June / 2020y); + + test_parse("2020124", "%Y%m%d", ymd); + assert(ymd == 4d / December / 2020y); + + test_parse("2020104Text", "%Y%m%d", ymd); + assert(ymd == 4d / October / 2020y); + + fail_parse("202000000000000923", "%Y%m%d", ymd); + fail_parse("202000000000000923", "%Y%m%d", ymd); + + // time_point out-of-range tests + fail_parse("1887-12-22 00:00:-1", "%F %T", st); + fail_parse("1887-12-22 00:00:60", "%F %T", st); + fail_parse("1887-12-22 00:-1:00", "%F %T", st); + fail_parse("1887-12-22 00:60:00", "%F %T", st); + fail_parse("1887-12-22 -1:00:00", "%F %T", st); + fail_parse("1887-12-22 24:00:00", "%F %T", st); + + test_parse("1912-06-23 00:00:00", "%F %T", st); + assert(st == sys_days{23d / June / 1912y}); + test_parse("1912-06-23 23:59:59", "%F %T", st); + assert(st == sys_days{23d / June / 1912y} + 23h + 59min + 59s); +} + +void parse_wchar() { + seconds time; + test_parse(L"12", L"%S", time); + assert(time == 12s); + test_parse(L"12", L"%M", time); + assert(time == 12min); + test_parse(L"30", L"%H", time); + assert(time == 30h); + test_parse(L" 1:23:42", L"%T", time); + assert(time == 1h + 23min + 42s); + wstring tz_name; + test_parse(L"Etc/GMT+11", L"%Z", time, &tz_name); + assert(tz_name == L"Etc/GMT+11"); + fail_parse(L"Not_valid! 00", L"%Z %H", time, &tz_name); + + weekday wd; + test_parse(L"wedNesday", L"%A", wd); + assert(wd == Wednesday); + + month m; + test_parse(L"deCeMbeR", L"%b", m); + assert(m == December); + + sys_seconds st; + test_parse(L"oct 29 19:01:42 2020", L"%c", st); + assert(st == sys_days{2020y / October / 29d} + 19h + 1min + 42s); + + fail_parse(L"ab", L"a%nb", time); + test_parse(L"a b", L"a%nb", time); + fail_parse(L"a b", L"a%nb", time); +} + +void test_parse() { + parse_seconds(); + parse_minutes(); + parse_hours(); + parse_other_duration(); + parse_time_zone(); + parse_calendar_types_basic(); + parse_iso_week_date(); + parse_other_week_date(); + parse_whitespace(); + parse_timepoints(); + parse_wchar(); +} + +void test() { test_duration_output(); - return 0; + test_parse(); +} + +int main() { + run_tz_test([] { test(); }); } diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_time_zones/env.lst b/tests/std/tests/P0355R7_calendars_and_time_zones_time_zones/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_time_zones/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/P0355R7_calendars_and_time_zones_time_zones/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_time_zones/test.cpp new file mode 100644 index 00000000000..a84824e67d6 --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_time_zones/test.cpp @@ -0,0 +1,418 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; +using namespace std::chrono; + +// NOTE: +// These test suites will assume all data from the IANA database is correct +// and will not test historical changes in transitions. Instead the focus +// will be on using a select sample of transitions in both a positive and +// negative UTC offset zone (different corner cases). + +constexpr bool sys_equal(const sys_info& left, const sys_info& right) { + return left.begin == right.begin && left.end == right.end && left.offset == right.offset && left.save == right.save + && left.abbrev == right.abbrev; +} + +void test_time_zone_and_link(const tzdb& my_tzdb, string_view tz_name, string_view tz_link_name) { + const auto original_tz = my_tzdb.locate_zone(tz_name); + assert(original_tz != nullptr); + assert(original_tz->name() == tz_name); + + const auto linked_tz = my_tzdb.locate_zone(tz_link_name); + assert(linked_tz != nullptr); + assert(linked_tz->name() == tz_name); + assert(original_tz == linked_tz); + + const auto tz_link = _Locate_zone_impl(my_tzdb.links, tz_link_name); + assert(tz_link != nullptr); + assert(tz_link->name() == tz_link_name); + assert(tz_link->target() == tz_name); + assert(my_tzdb.locate_zone(tz_link->target()) == original_tz); + + assert(_Locate_zone_impl(my_tzdb.zones, tz_name) != nullptr); + assert(_Locate_zone_impl(my_tzdb.zones, tz_link_name) == nullptr); + assert(_Locate_zone_impl(my_tzdb.links, tz_name) == nullptr); +} + +void try_locate_invalid_zone(const tzdb& my_tzdb, string_view name) { + try { + (void) my_tzdb.locate_zone(name); + assert(false); + } catch (const runtime_error&) { + } +} + +void timezone_tzdb_list_test() { + const auto& my_tzdb_list = get_tzdb_list(); + + // only one entry in the list unless leap seconds were to change + assert(&my_tzdb_list.front() == &get_tzdb()); + assert(&my_tzdb_list.front() == &reload_tzdb()); + assert(++my_tzdb_list.begin() == my_tzdb_list.end()); + assert(++my_tzdb_list.cbegin() == my_tzdb_list.cend()); +} + +void timezone_version_test() { + const auto& my_tzdb = get_tzdb(); + assert(my_tzdb.version.empty() == false); + + // version should end in .X where X == number of leap seconds + const auto pos = my_tzdb.version.find_last_of('.'); + assert(pos != decltype(my_tzdb.version)::npos); + const string leap_seconds{my_tzdb.version, pos + 1}; + assert(leap_seconds.empty() == false); + assert(leap_seconds == to_string(my_tzdb.leap_seconds.size())); + + // remote version will only differ if leap seconds info changes, will not occur in tests + const auto& reloaded_tzdb = reload_tzdb(); + assert(reloaded_tzdb.version.empty() == false); + assert(&reloaded_tzdb == &my_tzdb); + + const auto& remote_ver = remote_version(); + assert(remote_ver.empty() == false); + assert(remote_ver == my_tzdb.version); +} + +void timezone_names_test() { + const auto& my_tzdb = get_tzdb(); + + test_time_zone_and_link(my_tzdb, "Asia/Thimphu", "Asia/Thimbu"); + test_time_zone_and_link(my_tzdb, "America/Tijuana", "America/Ensenada"); + + const auto curr_zone = current_zone(); + assert(curr_zone != nullptr); + assert(curr_zone->name().empty() == false); + assert(curr_zone == my_tzdb.current_zone()); + + const auto located_zone = locate_zone("UTC"); + assert(located_zone != nullptr); + assert(located_zone->name() == "Etc/UTC"); + assert(located_zone == my_tzdb.locate_zone("UTC")); + + try_locate_invalid_zone(my_tzdb, "Non/Existent"); + + // Abbreviations should not be time_zones or time_zone_links + try_locate_invalid_zone(my_tzdb, "PDT"); + try_locate_invalid_zone(my_tzdb, "AEST"); + + // Comparison operators + const time_zone tz1{"Earlier"}; + const time_zone tz2{"Earlier"}; + const time_zone tz3{"Later"}; + assert(tz1 == tz2); + assert(tz1 != tz3); +#ifdef __cpp_lib_concepts + assert(tz1 <=> tz2 == strong_ordering::equal); + assert(tz1 <=> tz3 == strong_ordering::less); + assert(tz3 <=> tz1 == strong_ordering::greater); +#endif // __cpp_lib_concepts + + const time_zone_link link1{"Earlier", "Target"}; + const time_zone_link link2{"Earlier", "Is"}; + const time_zone_link link3{"Later", "Ignored"}; + assert(link1 == link2); + assert(link1 != link3); +#ifdef __cpp_lib_concepts + assert(link1 <=> link2 == strong_ordering::equal); + assert(link1 <=> link3 == strong_ordering::less); + assert(link3 <=> link1 == strong_ordering::greater); +#endif // __cpp_lib_concepts + + try { + // ensure locate_zone returns time_zone with given name + assert(all_of(my_tzdb.zones.begin(), my_tzdb.zones.end(), + [&](const auto& zone) { return my_tzdb.locate_zone(zone.name())->name() == zone.name(); })); + // ensure locate_zone returns correct target of time_zone_link + assert(all_of(my_tzdb.links.begin(), my_tzdb.links.end(), + [&](const auto& link) { return my_tzdb.locate_zone(link.name())->name() == link.target(); })); + // ensure locate_zone does NOT return time_zone that is also a time_zone_link + assert(all_of(my_tzdb.links.begin(), my_tzdb.links.end(), + [&](const auto& link) { return my_tzdb.locate_zone(link.name())->name() != link.name(); })); + } catch (const runtime_error&) { + assert(false); + } + + // See GH-1786. These may change over time and might have to be removed from this test. + // These are some examples in which the ICU.dll and IANA database diverge in what they consider a zone or a link. + assert(_Locate_zone_impl(my_tzdb.links, "Atlantic/Faroe") != nullptr); // is a time_zone in IANA + assert(_Locate_zone_impl(my_tzdb.zones, "Africa/Addis_Ababa") != nullptr); // is a time_zone_link in IANA + assert(_Locate_zone_impl(my_tzdb.links, "PST") != nullptr); // time_zone_link does not exist in IANA + assert(_Locate_zone_impl(my_tzdb.links, "Africa/Asmara") != nullptr); // matches IANA but target is different + assert(_Locate_zone_impl(my_tzdb.links, "Africa/Asmara")->target() == "Africa/Asmera"); // target == Africa/Nairobi + assert(_Locate_zone_impl(my_tzdb.zones, "America/Nuuk") == nullptr); // added in ICU 68, update test when it arrives +} + +void validate_timezone_transitions(const time_zone* tz, const Transition& transition) { + auto info = tz->get_info(transition.begin()); + assert(info.begin == transition.begin()); + assert(info.end == transition.end()); + assert(info.offset == transition.offset()); + assert(info.save == transition.save()); + assert(info.abbrev == transition.abbrev()); +} + +void timezone_sys_info_test() { + const auto& my_tzdb = get_tzdb(); + + auto sydney_tz = my_tzdb.locate_zone(Sydney::Tz_name); + assert(sydney_tz != nullptr); + validate_timezone_transitions(sydney_tz, Sydney::Day_1); + validate_timezone_transitions(sydney_tz, Sydney::Std_1); + validate_timezone_transitions(sydney_tz, Sydney::Day_2); + + auto la_tz = my_tzdb.locate_zone(LA::Tz_name); + assert(la_tz != nullptr); + validate_timezone_transitions(la_tz, LA::Day_1); + validate_timezone_transitions(la_tz, LA::Std_1); + validate_timezone_transitions(la_tz, LA::Day_2); + + auto begin_info = sydney_tz->get_info(Sydney::Std_1.begin()); + auto middle_info = sydney_tz->get_info(Sydney::Std_1.begin() + days{1}); + auto end_info = sydney_tz->get_info(Sydney::Std_1.end()); + assert(sys_equal(begin_info, middle_info)); + assert(!sys_equal(begin_info, end_info)); + + auto min_info = sydney_tz->get_info(time_zone::_Min_seconds); + auto max_info = sydney_tz->get_info(time_zone::_Max_seconds - seconds{1}); + assert(min_info.begin == time_zone::_Min_seconds); + assert(min_info.end != time_zone::_Max_seconds); + assert(max_info.begin != time_zone::_Min_seconds); + assert(max_info.end == time_zone::_Max_seconds); + + auto utc_zone = my_tzdb.locate_zone("Etc/UTC"); + assert(utc_zone != nullptr); + + auto min_utc = utc_zone->get_info(time_zone::_Min_seconds); + auto max_utc = utc_zone->get_info(time_zone::_Max_seconds - seconds{1}); + // Only a single transition in UTC + assert(sys_equal(min_utc, max_utc)); + assert(min_utc.begin < max_utc.end); + assert(min_utc.begin == time_zone::_Min_seconds); + assert(min_utc.end == time_zone::_Max_seconds); + + // Test abbreviations other than standard/daylight savings such as war time. + // These scenarios are not handled correctly by icu.dll + auto war_time = la_tz->get_info(sys_days{year{1942} / April / day{1}}); + assert(war_time.abbrev == "PDT"); // IANA database == "PWT" +} + +void timezone_to_local_test() { + const auto& my_tzdb = get_tzdb(); + { + using namespace Sydney; + auto tz = my_tzdb.locate_zone(Tz_name); + assert(tz != nullptr); + + const auto& to_standard = Day_to_Std.first.end(); + assert(tz->to_local(to_standard) == Day_to_Std.second.local_begin()); + assert(tz->to_local(to_standard + minutes{30}) == Day_to_Std.second.local_begin() + minutes{30}); + assert(tz->to_local(to_standard - minutes{30}) == Day_to_Std.first.local_end() - minutes{30}); + + const auto& to_daylight = Std_to_Day.first.end(); + assert(tz->to_local(to_daylight) == Std_to_Day.second.local_begin()); + assert(tz->to_local(to_daylight + minutes{30}) == Std_to_Day.second.local_begin() + minutes{30}); + assert(tz->to_local(to_daylight - minutes{30}) == Std_to_Day.first.local_end() - minutes{30}); + } + { + using namespace LA; + auto tz = my_tzdb.locate_zone(Tz_name); + assert(tz != nullptr); + + const auto& to_standard = Day_to_Std.first.end(); + assert(tz->to_local(to_standard) == Day_to_Std.second.local_begin()); + assert(tz->to_local(to_standard + minutes{30}) == Day_to_Std.second.local_begin() + minutes{30}); + assert(tz->to_local(to_standard - minutes{30}) == Day_to_Std.first.local_end() - minutes{30}); + + const auto& to_daylight = Std_to_Day.first.end(); + assert(tz->to_local(to_daylight) == Std_to_Day.second.local_begin()); + assert(tz->to_local(to_daylight + minutes{30}) == Std_to_Day.second.local_begin() + minutes{30}); + assert(tz->to_local(to_daylight - minutes{30}) == Std_to_Day.first.local_end() - minutes{30}); + } +} + +void assert_local(const time_zone* tz, local_seconds local, int result, const sys_info& first, const sys_info& second) { + const auto info = tz->get_info(local); + assert(info.result == result); + assert(sys_equal(info.first, first)); + assert(sys_equal(info.second, second)); + + // time_zone::to_sys depends heavily on local_info so just test it here + // to exhaust all corner cases. + sys_seconds sys_earliest{local.time_since_epoch() - info.first.offset}; + sys_seconds sys_latest{local.time_since_epoch() - info.second.offset}; + try { + assert(tz->to_sys(local) == sys_earliest); + assert(result == local_info::unique); + } catch (const nonexistent_local_time&) { + assert(result == local_info::nonexistent); + } catch (const ambiguous_local_time&) { + assert(result == local_info::ambiguous); + } + + if (result == local_info::unique) { + assert(tz->to_sys(local, choose::earliest) == sys_earliest); + assert(tz->to_sys(local, choose::latest) == sys_earliest); + } else if (result == local_info::nonexistent) { + assert(tz->to_sys(local, choose::earliest) == info.first.end); + assert(tz->to_sys(local, choose::latest) == info.first.end); + } else if (result == local_info::ambiguous) { + assert(tz->to_sys(local, choose::earliest) == sys_earliest); + assert(tz->to_sys(local, choose::latest) == sys_latest); + } +} + +void validate_get_local_info(const time_zone* tz, const pair& transition, int result) { + sys_info default_info{}; + sys_info first = tz->get_info(transition.first.begin()); + sys_info second = tz->get_info(transition.second.begin()); + + // Get the local time for the beginning of the ambiguous/nonexistent section + const auto danger_begin = get_danger_begin(transition.first, transition.second); + const auto danger_end = get_danger_end(transition.first, transition.second); + assert_local(tz, danger_begin - days{2}, local_info::unique, first, default_info); // two days before + assert_local(tz, danger_begin - hours{1}, local_info::unique, first, default_info); // one hour before + assert_local(tz, danger_begin, result, first, second); // danger begin + assert_local(tz, danger_begin + minutes{30}, result, first, second); // danger middle + assert_local(tz, danger_end, local_info::unique, second, default_info); // danger end + assert_local(tz, danger_end + hours{1}, local_info::unique, second, default_info); // one hour after + assert_local(tz, danger_end + days{2}, local_info::unique, second, default_info); // two days after +} + +void timezone_local_info_test() { + const auto& my_tzdb = get_tzdb(); + { + // positive offset (UTC+10/+11) can fall in previous transition + using namespace Sydney; + auto tz = my_tzdb.locate_zone(Tz_name); + assert(tz != nullptr); + validate_get_local_info(tz, Day_to_Std, local_info::ambiguous); + validate_get_local_info(tz, Std_to_Day, local_info::nonexistent); + } + { + // negative offset (UTC-8/-7) can fall in next transition + using namespace LA; + auto tz = my_tzdb.locate_zone(Tz_name); + assert(tz != nullptr); + validate_get_local_info(tz, Day_to_Std, local_info::ambiguous); + validate_get_local_info(tz, Std_to_Day, local_info::nonexistent); + } +} + +template +void validate_precision(const time_zone* tz, const pair& transition_pair, Dur precision) { + const auto& first = transition_pair.first; + const auto& second = transition_pair.second; + const auto transition = first.end(); + const auto danger_begin = get_danger_begin(first, second); + const auto danger_end = get_danger_end(first, second); + sys_info first_info = tz->get_info(first.begin()); + sys_info second_info = tz->get_info(second.begin()); + + // test correct transition is picked + assert(sys_equal(tz->get_info(transition), second_info)); + assert(sys_equal(tz->get_info(transition - precision), first_info)); + + // test ambiguous/nonexistent info is handled + assert(tz->get_info(danger_end).result == local_info::unique); // exact end of danger zone + assert(tz->get_info(danger_end - precision).result != local_info::unique); // just inside danger zone + assert(tz->get_info(danger_begin).result != local_info::unique); // exact start of danger zone + assert(tz->get_info(danger_begin - precision).result == local_info::unique); // just before danger zone + + // test precision is not lost when converting to local + assert(tz->to_local(transition) == second.local_begin()); + assert(tz->to_local(transition + precision) == second.local_begin() + precision); + assert(tz->to_local(transition - precision) == first.local_end() - precision); + + // test precision is not lost when converting to sys + try { + const sys_time sys_danger_begin = transition - first.save(); + const sys_time sys_danger_end = transition + first.save(); + assert(tz->to_sys(danger_end) == sys_danger_end); + assert(tz->to_sys(danger_end + precision) == sys_danger_end + precision); + assert(tz->to_sys(danger_begin - precision) == sys_danger_begin - precision); + } catch (const nonexistent_local_time&) { + assert(false); + } catch (const ambiguous_local_time&) { + assert(false); + } + + try { + // test ambiguous/nonexistent info is found + (void) tz->to_sys(danger_end - precision); + assert(false); + } catch (const nonexistent_local_time&) { + } catch (const ambiguous_local_time&) { + } +} + +void timezone_precision_test() { + const auto& my_tzdb = get_tzdb(); + using MilliDur = duration; + using MicroDur = duration; + + { + using namespace Sydney; + auto tz = my_tzdb.locate_zone(Tz_name); + validate_precision(tz, Std_to_Day, sys_seconds::duration{1}); + validate_precision(tz, Std_to_Day, MilliDur{1}); + validate_precision(tz, Std_to_Day, MilliDur{0.5}); + validate_precision(tz, Std_to_Day, MilliDur{0.05}); + validate_precision(tz, Std_to_Day, MilliDur{0.005}); + validate_precision(tz, Std_to_Day, MilliDur{0.0005}); + // precision limit... + + validate_precision(tz, Std_to_Day, MicroDur{1}); + validate_precision(tz, Std_to_Day, MicroDur{0.5}); + // precision limit... + + // validate opposite transition + validate_precision(tz, Day_to_Std, MicroDur{0.5}); + validate_precision(tz, Day_to_Std, MilliDur{0.0005}); + } + { + using namespace LA; + auto tz = my_tzdb.locate_zone(Tz_name); + validate_precision(tz, Std_to_Day, sys_seconds::duration{1}); + validate_precision(tz, Std_to_Day, MilliDur{1}); + validate_precision(tz, Std_to_Day, MilliDur{0.5}); + validate_precision(tz, Std_to_Day, MilliDur{0.05}); + validate_precision(tz, Std_to_Day, MilliDur{0.005}); + validate_precision(tz, Std_to_Day, MilliDur{0.0005}); + // precision limit... + + validate_precision(tz, Std_to_Day, MicroDur{1}); + validate_precision(tz, Std_to_Day, MicroDur{0.5}); + // precision limit... + + // validate opposite transition + validate_precision(tz, Day_to_Std, MicroDur{0.5}); + validate_precision(tz, Day_to_Std, MilliDur{0.0005}); + } +} + +void test() { + timezone_tzdb_list_test(); + timezone_version_test(); + timezone_names_test(); + timezone_sys_info_test(); + timezone_to_local_test(); + timezone_local_info_test(); + timezone_precision_test(); +} + +int main() { + run_tz_test([] { test(); }); +} diff --git a/tests/std/tests/P0355R7_calendars_and_time_zones_zoned_time/env.lst b/tests/std/tests/P0355R7_calendars_and_time_zones_zoned_time/env.lst new file mode 100644 index 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_zoned_time/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/P0355R7_calendars_and_time_zones_zoned_time/test.cpp b/tests/std/tests/P0355R7_calendars_and_time_zones_zoned_time/test.cpp new file mode 100644 index 00000000000..6bd031a5e22 --- /dev/null +++ b/tests/std/tests/P0355R7_calendars_and_time_zones_zoned_time/test.cpp @@ -0,0 +1,348 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +#include + +using namespace std; +using namespace std::chrono; +using ZT = zoned_traits; + +struct time_zone_info { + time_zone_info(string_view _name_, const time_zone* _tz_, seconds _sys_) : name(_name_), tz(_tz_), sys(_sys_) {} + + string_view name; + const time_zone* tz; + sys_seconds sys; + local_seconds local{tz->to_local(sys)}; + zoned_time zone{tz, sys}; +}; + +void zonedtime_constructor_test() { + const time_zone_info utc{"Etc/UTC", ZT::default_zone(), seconds{42}}; + const time_zone_info syd{Sydney::Tz_name, ZT::locate_zone(Sydney::Tz_name), seconds{20}}; + + // ensure local conversions are valid + assert(utc.tz->get_info(utc.local).result == local_info::unique); + assert(syd.tz->get_info(syd.local).result == local_info::unique); + + { + // defaulted copy + zoned_time zone{syd.zone}; + assert(zone.get_time_zone() == syd.tz); + assert(zone.get_sys_time() == syd.sys); + } + { + // (1) zoned_time(); + zoned_time zone{}; + assert(zone.get_time_zone() == utc.tz); + assert(zone.get_sys_time() == sys_seconds{}); + } + { + // (2) zoned_time(const sys_time& st); + zoned_time zone{sys_seconds{seconds{1}}}; + assert(zone.get_time_zone() == utc.tz); + assert(zone.get_sys_time() == sys_seconds{seconds{1}}); + } + { + // (3) explicit zoned_time(TimeZonePtr z); + zoned_time zone{&*syd.tz}; + assert(zone.get_time_zone() == syd.tz); + assert(zone.get_sys_time() == sys_seconds{}); + + // (4) explicit zoned_time(string_view name); + assert(zoned_time{syd.name} == zone); + } + { + // (5) template + // zoned_time(const zoned_time& y); + zoned_time zone_hours{syd.name, sys_time{hours{1}}}; + assert(zone_hours.get_time_zone() == syd.tz); + assert(zone_hours.get_sys_time() == sys_time{hours{1}}); + } + { + // (6) zoned_time(TimeZonePtr z, const sys_time& st); + zoned_time zone{&*syd.tz, syd.sys}; + assert(zone.get_time_zone() == syd.tz); + assert(zone.get_sys_time() == syd.sys); + + // (7) zoned_time(string_view name, const sys_time& st); + assert((zoned_time{syd.name, syd.sys}) == zone); + } + { + // (8) zoned_time(TimeZonePtr z, const local_time& tp); + zoned_time zone{&*syd.tz, syd.local}; + assert(zone.get_time_zone() == syd.tz); + assert(zone.get_sys_time() == syd.sys); + + // (9) zoned_time(string_view name, const local_time& st); + assert((zoned_time{syd.name, syd.local}) == zone); + } + { + // (10) zoned_time(TimeZonePtr z, const local_time& tp, choose c); + zoned_time zone{&*syd.tz, syd.local, choose::earliest}; + assert(zone.get_time_zone() == syd.tz); + assert(zone.get_sys_time() == syd.sys); + + // (11) zoned_time(string_view name, const local_time& tp, choose c); + assert((zoned_time{syd.name, syd.local, choose::earliest}) == zone); + } + { + // (12) template + // zoned_time(TimeZonePtr z, const zoned_time& y); + zoned_time z1{&*utc.tz, syd.zone}; + zoned_time z2{&*syd.tz, utc.zone}; + assert(z1.get_time_zone() == utc.tz); + assert(z1.get_sys_time() == syd.sys); + assert(z2.get_time_zone() == syd.tz); + assert(z2.get_sys_time() == utc.sys); + + // (13) template + // zoned_time(TimeZonePtr z, const zoned_time& y, choose); + assert((zoned_time{&*utc.tz, syd.zone, choose::earliest}) == z1); + assert((zoned_time{&*syd.tz, utc.zone, choose::earliest}) == z2); + + // (14) template + // zoned_time(string_view name, const zoned_time& y); + assert((zoned_time{utc.name, syd.zone}) == z1); + assert((zoned_time{syd.name, utc.zone}) == z2); + + // (15) template + // zoned_time(string_view name, const zoned_time& y, choose); + assert((zoned_time{utc.name, syd.zone, choose::earliest}) == z1); + assert((zoned_time{syd.name, utc.zone, choose::earliest}) == z2); + + // when (_Duration2 != _Duration) + zoned_time zone_hours{&*syd.tz, sys_time{hours{1}}}; + zoned_time zone{&*utc.tz, zone_hours}; + assert(zone.get_time_zone() == utc.tz); + assert(zone.get_sys_time() == sys_time{hours{1}}); + + assert((zoned_time{&*utc.tz, zone_hours, choose::earliest}) == zone); + assert((zoned_time{utc.name, zone_hours}) == zone); + assert((zoned_time{utc.name, zone_hours, choose::earliest}) == zone); + } +} + +void zonedtime_operator_test() { + const auto utc_tz = ZT::default_zone(); + const zoned_time utc_zone{}; + assert(utc_zone.get_time_zone() == utc_tz); + assert(utc_zone.get_sys_time() == sys_seconds{}); + assert(utc_zone.get_local_time() == local_seconds{}); + + const auto syd_tz = ZT::locate_zone(Sydney::Tz_name); + const auto transition = Sydney::Day_1; + const auto sys = transition.begin(); + const auto local = syd_tz->to_local(transition.begin()); + assert(syd_tz->get_info(local).result == local_info::unique); + + zoned_time zone{ZT::locate_zone(Sydney::Tz_name), sys}; + assert(zone.get_time_zone() == syd_tz); + assert(zone.get_sys_time() == sys); + assert(zone.get_local_time() == local); + assert(zone.get_info().begin == sys); + + // set time to be == epoch + zone = sys_seconds{}; + assert(zone.get_time_zone() == syd_tz); + assert(zone.get_sys_time() == sys_seconds{}); + assert(zone.get_local_time() == syd_tz->to_local(sys_seconds{})); + assert(zone.get_info().begin != sys); + + // reset sys_time to transition.begin() via local_time + zone = local; + assert(zone.get_time_zone() == syd_tz); + assert(zone.get_sys_time() == sys); + assert(zone.get_local_time() == local); + assert(zone.get_info().begin == sys); +} + +void zonedtime_exception_tests() { + const auto syd_tz = ZT::locate_zone(Sydney::Tz_name); + const auto ambiguous_local = syd_tz->to_local(Sydney::Standard_begin_2020 - minutes{1}); + assert(syd_tz->get_info(ambiguous_local).result == local_info::ambiguous); + + // unsafe constructors + try { + (void) zoned_time{ZT::locate_zone(Sydney::Tz_name), ambiguous_local}; + assert(false); + } catch (nonexistent_local_time&) { + } catch (ambiguous_local_time&) { + } + + try { + (void) zoned_time{Sydney::Tz_name, ambiguous_local}; + assert(false); + } catch (nonexistent_local_time&) { + } catch (ambiguous_local_time&) { + } + + // safe constructors + try { + (void) zoned_time{ZT::locate_zone(Sydney::Tz_name), ambiguous_local, choose::earliest}; + (void) zoned_time{Sydney::Tz_name, ambiguous_local, choose::earliest}; + } catch (nonexistent_local_time&) { + assert(false); + } catch (ambiguous_local_time&) { + assert(false); + } + + // unsafe operator + try { + zoned_time zone{Sydney::Tz_name}; + zone = ambiguous_local; + (void) zone; + assert(false); + } catch (nonexistent_local_time&) { + } catch (ambiguous_local_time&) { + } +} + +struct Always_zero { + [[nodiscard]] string_view name() const noexcept { + return "Zero"; + } + + template + [[nodiscard]] sys_info get_info(const sys_time&) const { + return {}; + } + + template + [[nodiscard]] local_info get_info(const local_time&) const { + return {}; + } + + template + [[nodiscard]] sys_time> to_sys(const local_time&) const { + return sys_time>{}; + } + + template + [[nodiscard]] sys_time> to_sys(const local_time&, const choose) const { + return sys_time>{}; + } + + template + [[nodiscard]] local_time> to_local(const sys_time&) const { + return local_time>{}; + } +}; + +struct Has_default : Always_zero {}; +struct Has_locate : Always_zero {}; + +Always_zero zero_zone{}; +Has_default has_default_zone{}; +Has_locate has_locate_zone{}; + +template <> +struct zoned_traits { + [[nodiscard]] static const Has_default* default_zone() { + return &has_default_zone; + } + + // missing string_view parameter... + [[nodiscard]] static const Has_locate* locate_zone() { + return &has_locate_zone; + } +}; + +template <> +struct zoned_traits { + [[nodiscard]] static const Has_locate* locate_zone(string_view) { + return &has_locate_zone; + } +}; + +void zonedtime_traits_test() { + // operation using timezone should always result in zero + using Always_zero_ptr = const Always_zero*; + + zoned_time zone{&zero_zone, sys_seconds{seconds{1}}}; + assert(zone.get_time_zone() == &zero_zone); + assert(zone.get_sys_time() == sys_seconds{seconds{1}}); + assert(sys_seconds{zone} == sys_seconds{seconds{1}}); + assert(zone.get_local_time() == local_seconds{}); + assert(local_seconds{zone} == local_seconds{}); + assert(zone.get_info().begin == sys_seconds{}); + + zone = sys_seconds{seconds{2}}; + assert(zone.get_sys_time() == sys_seconds{seconds{2}}); + assert(sys_seconds{zone} == sys_seconds{seconds{2}}); + assert(zone.get_local_time() == local_seconds{}); + assert(local_seconds{zone} == local_seconds{}); + assert(zone.get_info().begin == sys_seconds{}); + + zone = local_seconds{seconds{3}}; + assert(zone.get_sys_time() == sys_seconds{}); // zero because timezone is used to compute sys_seconds + assert(sys_seconds{zone} == sys_seconds{}); + assert(zone.get_local_time() == local_seconds{}); + assert(local_seconds{zone} == local_seconds{}); + assert(zone.get_info().begin == sys_seconds{}); +} + +template +constexpr void assert_constructible_durations() { + using Zoned_seconds = zoned_time; + using Zoned_duration = zoned_time; + + static_assert(is_constructible_v == Result); + static_assert(is_constructible_v == Result); + static_assert(is_constructible_v == (Result && Has_locate_zone)); + static_assert(is_constructible_v == Result); + static_assert(is_constructible_v == (Result && Has_locate_zone)); +} + +template +constexpr void assert_constructible() { + using Zoned = zoned_time; + using Zoned_no_locate = zoned_time; + + static_assert(is_constructible_v == Has_default_zone); + static_assert(is_constructible_v&> == Has_default_zone); + static_assert(is_constructible_v == true); + static_assert(is_constructible_v == Has_locate_zone); + static_assert(is_constructible_v == true); + static_assert(is_constructible_v&> == true); + static_assert(is_constructible_v&> == Has_locate_zone); + static_assert(is_constructible_v&> == true); + static_assert(is_constructible_v&> == Has_locate_zone); + static_assert(is_constructible_v&, choose> == true); + static_assert(is_constructible_v&, choose> == Has_locate_zone); + static_assert(is_constructible_v == true); + static_assert(is_constructible_v == Has_locate_zone); + static_assert(is_constructible_v == true); + static_assert(is_constructible_v == Has_locate_zone); + + // when (_Duration2 != _Duration) + assert_constructible_durations(); + static_assert(is_convertible_v, sys_time> == true); + assert_constructible_durations(); + static_assert(is_convertible_v, sys_time> == false); + assert_constructible_durations(); +} + +constexpr void zonedtime_constraints_test() { + assert_constructible(); + assert_constructible(); + assert_constructible(); + assert_constructible(); +} + +void test() { + zonedtime_constructor_test(); + zonedtime_operator_test(); + zonedtime_exception_tests(); + zonedtime_traits_test(); + zonedtime_constraints_test(); +} + +int main() { + run_tz_test([] { test(); }); +} diff --git a/tests/std/tests/P0674R1_make_shared_for_arrays/test.cpp b/tests/std/tests/P0674R1_make_shared_for_arrays/test.cpp index 0a9082fe926..f703ded4876 100644 --- a/tests/std/tests/P0674R1_make_shared_for_arrays/test.cpp +++ b/tests/std/tests/P0674R1_make_shared_for_arrays/test.cpp @@ -623,6 +623,67 @@ void test_allocate_shared_array_unknown_bounds() { } } +// Test GH-1733 ": error C2694 when calling make_shared on class with throwing destructor" +struct NontrivialThrowingDtor { + ~NontrivialThrowingDtor() noexcept(false) {} +}; +static_assert(!is_nothrow_destructible_v); +static_assert(!is_trivially_destructible_v); + +struct TrivialThrowingDtor { + ~TrivialThrowingDtor() noexcept(false) = default; +}; + +#ifndef __EDG__ // TRANSITION, VSO-1292292 +static_assert(!is_nothrow_destructible_v); +#endif // ^^^ no workaround ^^^ +static_assert(is_trivially_destructible_v); + +template +struct WeirdDeleter { + void operator()(T* const ptr) const { + delete ptr; + } + + ~WeirdDeleter() noexcept(false) {} +}; +static_assert(!is_nothrow_destructible_v>); + +void test_GH_1733() { + WeirdDeleter del; + allocator al; + + // _Ref_count + (void) shared_ptr{new NontrivialThrowingDtor}; + + // _Ref_count_resource + (void) shared_ptr{new NontrivialThrowingDtor, del}; + + // _Ref_count_resource_alloc + (void) shared_ptr{new NontrivialThrowingDtor, del, al}; + + // _Ref_count_obj2 + (void) make_shared(); + + // _Ref_count_obj_alloc3 + (void) allocate_shared(al); + + // _Ref_count_unbounded_array<_Ty, true> + (void) make_shared(10); + + // _Ref_count_unbounded_array<_Ty, false> + (void) make_shared(10); + + // _Ref_count_bounded_array + (void) make_shared(); + + // _Ref_count_unbounded_array_alloc + (void) allocate_shared(al, 10); + + // _Ref_count_bounded_array_alloc + (void) allocate_shared(al); +} + int main() { test_make_shared_not_array(); test_make_shared_array_known_bounds(); @@ -631,4 +692,6 @@ int main() { test_allocate_shared_not_array(); test_allocate_shared_array_known_bounds(); test_allocate_shared_array_unknown_bounds(); + + test_GH_1733(); } diff --git a/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp b/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp index 32c6b8ad22a..9ff968ec58b 100644 --- a/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp @@ -63,6 +63,14 @@ constexpr void smoke_test() { int const two_ints[] = {0, 1}; assert(!equal(one_int, two_ints, comp, proj, proj)); } + { + // Validate memcmp case + int arr1[3]{0, 2, 5}; + int arr2[3]{0, 2, 5}; + assert(equal(arr1, arr2)); + arr2[1] = 7; + assert(!equal(arr1, arr2)); + } } int main() { diff --git a/tests/std/tests/P0896R4_ranges_alg_find/test.cpp b/tests/std/tests/P0896R4_ranges_alg_find/test.cpp index 768de98b7fc..f364a6823d2 100644 --- a/tests/std/tests/P0896R4_ranges_alg_find/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_find/test.cpp @@ -48,6 +48,16 @@ struct instantiator { STATIC_ASSERT(same_as>); assert(result == wrapped_input.end()); } + { // Validate memchr case [found case] + char arr[5]{4, 8, 1, -15, 125}; + auto result = find(arr, 1); + assert(*result == 1); + } + { // Validate memchr case [not found case] + char arr[5]{4, 8, 1, -15, 125}; + auto result = find(arr, 10); + assert(result == end(arr)); + } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_lexicographical_compare/test.cpp b/tests/std/tests/P0896R4_ranges_alg_lexicographical_compare/test.cpp index 3d37682ccd9..5bc0e34358b 100644 --- a/tests/std/tests/P0896R4_ranges_alg_lexicographical_compare/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_lexicographical_compare/test.cpp @@ -193,6 +193,15 @@ struct instantiator { empty1.begin(), empty1.end(), empty2.begin(), empty2.end(), less{}, get_first, get_second); assert(!result); } + { // Validate memcmp case + unsigned char arr1[3]{0, 1, 2}; + unsigned char arr2[3]{0, 1, 3}; + assert(lexicographical_compare(arr1, arr2)); + arr2[2] = 2; + assert(!lexicographical_compare(arr1, arr2)); + arr2[2] = 1; + assert(!lexicographical_compare(arr1, arr2)); + } } }; diff --git a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp index 2e4698779e3..683dade0133 100644 --- a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp @@ -105,9 +105,11 @@ 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::join)); STATIC_ASSERT(test_cpo(ranges::views::keys)); STATIC_ASSERT(test_cpo(ranges::views::reverse)); STATIC_ASSERT(test_cpo(ranges::views::single)); +STATIC_ASSERT(test_cpo(ranges::views::split)); STATIC_ASSERT(test_cpo(ranges::views::take)); STATIC_ASSERT(test_cpo(ranges::views::take_while)); STATIC_ASSERT(test_cpo(ranges::views::transform)); diff --git a/tests/std/tests/P0896R4_views_drop_while_death/test.cpp b/tests/std/tests/P0896R4_views_drop_while_death/test.cpp index feb5c926892..216fefc6633 100644 --- a/tests/std/tests/P0896R4_views_drop_while_death/test.cpp +++ b/tests/std/tests/P0896R4_views_drop_while_death/test.cpp @@ -21,7 +21,7 @@ void test_view_predicate() { void test_view_begin() { DWV r; - (void) r.begin(); // LWG-3490 forbids calling begin on a drop_while_view with no predicate + (void) r.begin(); // N4885 [range.drop.while.view] forbids calling begin on a drop_while_view with no predicate } int main(int argc, char* argv[]) { diff --git a/tests/std/tests/P0896R4_views_join/env.lst b/tests/std/tests/P0896R4_views_join/env.lst new file mode 100644 index 00000000000..62a24024479 --- /dev/null +++ b/tests/std/tests/P0896R4_views_join/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_join/test.cpp b/tests/std/tests/P0896R4_views_join/test.cpp new file mode 100644 index 00000000000..cd989531f13 --- /dev/null +++ b/tests/std/tests/P0896R4_views_join/test.cpp @@ -0,0 +1,511 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +using namespace std; + +template +concept CanViewJoin = requires(Rng&& r) { + views::join(static_cast(r)); +}; + +template +constexpr bool test_one(Outer&& rng, Expected&& expected) { + using ranges::join_view, ranges::begin, ranges::end, ranges::next, ranges::prev, ranges::input_range, + ranges::forward_range, ranges::bidirectional_range, ranges::common_range, ranges::borrowed_range, + ranges::iterator_t, ranges::range_value_t, ranges::range_reference_t; + + using Inner = range_value_t; + constexpr bool deref_is_glvalue = is_reference_v>; + + // clang-format off + constexpr bool can_test = ranges::viewable_range + && input_range> + && (deref_is_glvalue || ranges::view); + // clang-format on + + if constexpr (can_test) { + using V = views::all_t; + using R = join_view; + static_assert(ranges::view); + static_assert(input_range == input_range); + static_assert(forward_range == (deref_is_glvalue && forward_range && forward_range) ); + // clang-format off + static_assert(bidirectional_range == + (deref_is_glvalue && bidirectional_range && bidirectional_range && common_range)); + // clang-format on + static_assert(!ranges::random_access_range); + static_assert(!ranges::contiguous_range); + + constexpr bool is_view = ranges::view; + + // Validate range adapter object + // ...with lvalue argument + static_assert(CanViewJoin == (!is_view || copyable) ); + if constexpr (CanViewJoin) { + constexpr bool is_noexcept = !is_view || is_nothrow_copy_constructible_v; + + static_assert(same_as); + static_assert(noexcept(views::join(rng)) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(rng | views::join) == is_noexcept); + } + + // ... with const lvalue argument + static_assert(CanViewJoin&> == (!is_view || copyable) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v; + + static_assert(same_as); + static_assert(noexcept(views::join(as_const(rng))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(as_const(rng) | views::join) == is_noexcept); + } else if constexpr (!is_view) { + using RC = join_view&>>; + constexpr bool is_noexcept = is_nothrow_constructible_v&>; + + static_assert(same_as); + static_assert(noexcept(views::join(as_const(rng))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(as_const(rng) | views::join) == is_noexcept); + } + + // ... with rvalue argument + static_assert(CanViewJoin> == (is_view || borrowed_range>) ); + if constexpr (is_view) { + constexpr bool is_noexcept = is_nothrow_move_constructible_v; + static_assert(same_as); + static_assert(noexcept(views::join(move(rng))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(rng) | views::join) == is_noexcept); + } else if constexpr (borrowed_range>) { + using S = decltype(ranges::subrange{move(rng)}); + using RS = join_view; + constexpr bool is_noexcept = noexcept(S{move(rng)}); + + static_assert(same_as); + static_assert(noexcept(views::join(move(rng))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(rng) | views::join) == is_noexcept); + } + + // ... with const rvalue argument + static_assert(CanViewJoin> == (is_view && copyable) + || (!is_view && borrowed_range>) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v; + + static_assert(same_as); + static_assert(noexcept(views::join(move(as_const(rng)))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(as_const(rng)) | views::join) == is_noexcept); + } else if constexpr (!is_view && borrowed_range>) { + using S = decltype(ranges::subrange{as_const(rng)}); + using RS = join_view; + constexpr bool is_noexcept = noexcept(S{as_const(rng)}); + + static_assert(same_as); + static_assert(noexcept(views::join(move(as_const(rng)))) == is_noexcept); + + static_assert(same_as); + static_assert(noexcept(move(as_const(rng)) | views::join) == is_noexcept); + } + + // Validate deduction guide + same_as auto r = join_view{forward(rng)}; + assert(ranges::equal(r, expected)); + const bool is_empty = ranges::empty(expected); + + // Validate lack of size + static_assert(!CanSize); + + // Validate view_interface::empty and operator bool + static_assert(CanEmpty == forward_range); + static_assert(CanMemberEmpty == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); + + static_assert(CanEmpty == forward_range); + static_assert(CanMemberEmpty == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(as_const(r).empty() == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + } + } + + // Validate join_view::begin + static_assert(CanMemberBegin); + static_assert(CanMemberBegin == (input_range && is_reference_v>) ); + if (forward_range) { + const iterator_t i = r.begin(); + if (!is_empty) { + assert(*i == *begin(expected)); + } + + if constexpr (copyable) { + auto r2 = r; + const same_as> auto i2 = r2.begin(); + if (!is_empty) { + assert(*i2 == *i); + } + } + + static_assert(CanMemberBegin == CanBegin); + if constexpr (CanMemberBegin) { + const iterator_t ci = as_const(r).begin(); + if (!is_empty) { + assert(*ci == *i); + } + + if constexpr (copyable) { + const auto r2 = r; + const same_as> auto ci2 = r2.begin(); + if (!is_empty) { + assert(*ci2 == *i); + } + } + } + } + + // Validate join_view::end + static_assert(CanMemberEnd); + static_assert(CanMemberEnd == (input_range && is_reference_v>) ); + // clang-format off + static_assert(common_range == (forward_range && is_reference_v> && common_range + && forward_range && common_range) ); + static_assert(common_range == (forward_range && is_reference_v> + && common_range && forward_range> + && common_range>) ); + // clang-format on + const ranges::sentinel_t s = r.end(); + if (!is_empty) { + if constexpr (bidirectional_range && common_range) { + assert(*prev(s) == *prev(end(expected))); + + if constexpr (copyable) { + auto r2 = r; + assert(*prev(r2.end()) == *prev(end(expected))); + } + } + + static_assert(CanMemberEnd == CanEnd); + if constexpr (CanMemberEnd) { + const ranges::sentinel_t cs = as_const(r).end(); + if constexpr (bidirectional_range && common_range) { + assert(*prev(cs) == *prev(end(expected))); + + if constexpr (copyable) { + const auto r2 = r; + const ranges::sentinel_t cs2 = r2.end(); + assert(*prev(cs2) == *prev(end(expected))); + } + } + } + } + + // Validate view_interface::data + static_assert(!CanData); + static_assert(!CanData); + + // Validate view_interface::operator[] + static_assert(!CanIndex); + static_assert(!CanIndex); + + // Validate view_interface::front and back + static_assert(CanMemberFront == forward_range); + static_assert(CanMemberFront == forward_range); + if (!is_empty) { + if constexpr (CanMemberFront) { + assert(r.front() == *begin(expected)); + } + + if constexpr (CanMemberFront) { + assert(as_const(r).front() == *begin(expected)); + } + } + + static_assert(CanMemberBack == (bidirectional_range && common_range) ); + // clang-format off + static_assert(CanMemberBack == (bidirectional_range && common_range + && is_reference_v>) ); + // clang-format on + if (!is_empty) { + if constexpr (CanMemberBack) { + assert(r.back() == *prev(end(expected))); + } + + if constexpr (CanMemberBack) { + assert(as_const(r).back() == *prev(end(expected))); + } + } + + // Validate join_view::base() const& + static_assert(CanMemberBase == copy_constructible); + if constexpr (copy_constructible && forward_range) { + same_as auto b1 = as_const(r).base(); + static_assert(noexcept(as_const(r).base()) == is_nothrow_copy_constructible_v); + if (!is_empty) { + auto bi1 = b1.begin(); + while (ranges::empty(*bi1)) { + ++bi1; + } + auto&& inner_first = *bi1; + assert(*begin(inner_first) == *begin(expected)); + + if constexpr (bidirectional_range && common_range) { + auto ei1 = prev(b1.end()); + while (ranges::empty(*ei1)) { + --ei1; + } + auto&& inner_last = *ei1; + assert(*prev(end(inner_last)) == *prev(end(expected))); + } + } + } + + // Validate join_view::base() && (NB: do this last since it leaves r moved-from) + if (forward_range) { // intentionally not if constexpr + same_as auto b2 = move(r).base(); + static_assert(noexcept(move(r).base()) == is_nothrow_move_constructible_v); + if constexpr (CanEmpty) { + if (!is_empty) { + auto bi2 = b2.begin(); + while (ranges::empty(*bi2)) { + ++bi2; + } + auto&& inner_first = *bi2; + assert(*begin(inner_first) == *begin(expected)); + + if constexpr (bidirectional_range && common_range) { + auto ei2 = prev(b2.end()); + while (ranges::empty(*ei2)) { + --ei2; + } + auto&& inner_last = *ei2; + assert(*prev(end(inner_last)) == *prev(end(expected))); + } + } + } + } + } + return true; +} + +constexpr int expected_ints[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; +constexpr span intervals[] = { + {expected_ints + 0, expected_ints + 3}, + {expected_ints + 3, expected_ints + 7}, + {expected_ints + 7, expected_ints + 7}, + {expected_ints + 7, expected_ints + 10}, + {expected_ints + 10, expected_ints + 10}, +}; + +struct instantiator { + template + static constexpr void call() { + static_assert(ranges::size(intervals) == 5); + Inner inner_ranges[] = { + Inner{intervals[0]}, Inner{intervals[1]}, Inner{intervals[2]}, Inner{intervals[3]}, Inner{intervals[4]}}; + test_one(Outer{inner_ranges}, expected_ints); + } +}; + +enum class RefOrView { reference, view }; + +template > +using inner_test_range = test::range || IsCommon == test::Common::yes}, + test::ProxyRef::no, IsView, test::Copyability::copyable>; + +template > +using outer_test_range = test::range || IsCommon == test::Common::yes}, + (RV == RefOrView::view ? test::ProxyRef::prvalue : test::ProxyRef::no), test::CanView::yes, + test::Copyability::copyable>; + +constexpr bool instantiation_test() { + // The adaptor is sensitive to: + // * inner and outer range common category (input, forward, bidi) + // * outer range's reference type referenceness vs. value type viewness + // * if the inner range models common_range + // * if the outer range models common_range + // * if both inner and outer iterators are equality_comparable (the defaults for input-non-common and forward + // suffice to get coverage here) + // * if the inner range has -> (Ditto defaults) + using test::CanView, test::Common; + + instantiator::call, + outer_test_range, + RefOrView::reference, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::reference, Common::yes>>(); + instantiator::call, + outer_test_range, + RefOrView::reference, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::reference, Common::yes>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::yes>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::yes>>(); + instantiator::call, + outer_test_range, + RefOrView::reference, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::reference, Common::yes>>(); + instantiator::call, + outer_test_range, + RefOrView::reference, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::reference, Common::yes>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::yes>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::no>>(); + instantiator::call, + outer_test_range, + RefOrView::view, Common::yes>>(); + instantiator::call, + outer_test_range, RefOrView::reference, Common::no>>(); + instantiator::call, + outer_test_range, RefOrView::reference, + Common::yes>>(); + instantiator::call, + outer_test_range, RefOrView::reference, + Common::no>>(); + instantiator::call, + outer_test_range, RefOrView::reference, + Common::yes>>(); + instantiator::call, + outer_test_range, RefOrView::view, Common::no>>(); + instantiator::call, + outer_test_range, RefOrView::view, Common::yes>>(); + instantiator::call, + outer_test_range, RefOrView::view, Common::no>>(); + instantiator::call, + outer_test_range, RefOrView::view, Common::yes>>(); + + return true; +} + +using mo_inner = test::range; + +template > +using move_only_view = test::range}, test::ProxyRef::no, test::CanView::yes, + test::Copyability::move_only>; +void test_move_only_views() { + const auto gen = [] { + return array{mo_inner{intervals[0]}, mo_inner{intervals[1]}, mo_inner{intervals[2]}, mo_inner{intervals[3]}}; + }; + + auto input = gen(); + test_one(move_only_view{input}, expected_ints); + + input = gen(); + test_one(move_only_view{input}, expected_ints); + + input = gen(); + test_one(move_only_view{input}, expected_ints); + + input = gen(); + test_one(move_only_view{input}, expected_ints); + + input = gen(); + test_one(move_only_view{input}, expected_ints); +} + +int main() { + // Validate views + constexpr string_view expected = "Hello World!"sv; + + { // ...copyable + static constexpr array input = {{{}, "Hello "sv, {}, "World!"sv, {}}}; + constexpr span sp{input}; + static_assert(test_one(sp, expected)); + test_one(sp, expected); + } + // ... move-only + test_move_only_views(); + + // Validate non-views + { // ... C array + static constexpr int join_me[5][2] = {{0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}}; +#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-934264 + static_assert(test_one(join_me, expected_ints)); +#endif // TRANSITION, VSO-934264 + test_one(join_me, expected_ints); + } + { // ... fwd container + forward_list lst = {{}, "Hello "sv, {}, "World!"sv, {}}; + test_one(lst, expected); + } + { // ... bidi container + list lst = {{}, "Hello "sv, {}, "World!"sv, {}}; + test_one(lst, expected); + } + { // ... random container + vector lst = {{}, "Hello "sv, {}, "World!"sv, {}}; + test_one(lst, expected); + } + + { // From example in LWG-3474 + vector>> nested_vectors = {{{1, 2, 3}, {4, 5}, {6}}, {{7}, {8, 9}, {10, 11, 12}}, {{13}}}; + auto joined = nested_vectors | views::join | views::join; + static constexpr int result[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; + assert(ranges::equal(joined, result)); + } + +#if defined(__clang__) || defined(__EDG__) // TRANSITION, VSO-934264 + STATIC_ASSERT(instantiation_test()); +#endif // TRANSITION, VSO-934264 + instantiation_test(); +} diff --git a/tests/std/tests/P0896R4_views_split/env.lst b/tests/std/tests/P0896R4_views_split/env.lst new file mode 100644 index 00000000000..62a24024479 --- /dev/null +++ b/tests/std/tests/P0896R4_views_split/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_split/test.cpp b/tests/std/tests/P0896R4_views_split/test.cpp new file mode 100644 index 00000000000..548cc07c6bf --- /dev/null +++ b/tests/std/tests/P0896R4_views_split/test.cpp @@ -0,0 +1,342 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include + +#include +using namespace std; + +template +concept CanViewSplit = requires(Rng&& r, Delimiter&& d) { + views::split(static_cast(r), static_cast(d)); +}; + +constexpr auto equal_ranges = [](auto&& left, auto&& right) { return ranges::equal(left, right); }; +constexpr auto text = "This is a test, this is only a test."sv; + +template +struct delimiter_view_impl { + template + using apply = ranges::single_view>; +}; +template <> +struct delimiter_view_impl { + template + using apply = views::all_t; +}; +template +using delimiter_view_t = + typename delimiter_view_impl>>::template apply; + +template +constexpr void test_one(Base&& base, Delimiter&& delimiter, Expected&& expected) { + STATIC_ASSERT(CanViewSplit); + using R = decltype(views::split(forward(base), forward(delimiter))); + + // Validate type properties + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(ranges::input_range); + STATIC_ASSERT(ranges::forward_range == ranges::forward_range); + STATIC_ASSERT(!ranges::bidirectional_range); + + // Validate range adaptor object and range adaptor closure + using DV = delimiter_view_t; + const auto closure = views::split(delimiter); + + constexpr bool is_view = ranges::view>; + + // ... with lvalue argument + STATIC_ASSERT(CanViewSplit == (!is_view || copyable>) ); + if constexpr (CanViewSplit) { // Validate lvalue + constexpr bool is_noexcept = + (!is_view || is_nothrow_copy_constructible_v>) &&is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::split(base, delimiter)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(base | closure) == is_noexcept); + } + + // ... with const lvalue argument + STATIC_ASSERT( + CanViewSplit&, Delimiter&> == (!is_view || copyable>) ); + if constexpr (is_view && copyable>) { + constexpr bool is_noexcept = + is_nothrow_copy_constructible_v> && is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::split(as_const(base), delimiter)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(base) | closure) == is_noexcept); + } else if constexpr (!is_view) { + using RC = ranges::split_view&>, DV>; + constexpr bool is_noexcept = is_nothrow_constructible_v&, Delimiter&>; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::split(as_const(base), delimiter)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(base) | closure) == is_noexcept); + } + + // ... with rvalue argument + STATIC_ASSERT( + CanViewSplit, Delimiter&> == is_view || ranges::borrowed_range>); + if constexpr (is_view) { + constexpr bool is_noexcept = + is_nothrow_move_constructible_v> && is_nothrow_copy_constructible_v; + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::split(move(base), delimiter)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(base) | closure) == is_noexcept); + } else if constexpr (ranges::borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + using RS = ranges::split_view; + constexpr bool is_noexcept = + noexcept(S{declval>()}) && is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::split(move(base), delimiter)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(base) | closure) == is_noexcept); + } + + // ... with const rvalue argument + STATIC_ASSERT( + CanViewSplit, Delimiter&> == (is_view && copyable>) + || (!is_view && ranges::borrowed_range>) ); + if constexpr (is_view && copyable>) { + constexpr bool is_noexcept = + is_nothrow_copy_constructible_v> && is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::split(move(as_const(base)), delimiter)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(base)) | closure) == is_noexcept); + } else if constexpr (!is_view && ranges::borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + using RS = ranges::split_view; + constexpr bool is_noexcept = + noexcept(S{declval>()}) && is_nothrow_copy_constructible_v; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::split(move(as_const(base)), delimiter)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(base)) | closure) == is_noexcept); + } + + // Validate deduction guide + same_as auto r = ranges::split_view{forward(base), forward(delimiter)}; + assert(ranges::equal(r, expected, equal_ranges)); + + // Validate view_interface::empty and operator bool + const bool is_empty = ranges::empty(expected); + STATIC_ASSERT(CanMemberEmpty == ranges::forward_range); + STATIC_ASSERT(CanBool == ranges::forward_range); + if constexpr (ranges::forward_range) { + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); + } + + // Validate split_view::begin + STATIC_ASSERT(CanMemberBegin); + if (ranges::forward_range) { // intentionally not if constexpr + const auto i = r.begin(); + if (!is_empty) { + assert(ranges::equal(*i, *ranges::begin(expected))); + } + + if constexpr (copyable) { + auto r2 = r; + const auto i2 = r2.begin(); + if (!is_empty) { + assert(ranges::equal(*i2, *i)); + } + } + + STATIC_ASSERT(CanMemberBegin == ranges::forward_range>); + STATIC_ASSERT(CanBegin == CanMemberBegin); + if constexpr (CanBegin) { + const auto ic = as_const(r).begin(); + if (!is_empty) { + assert(ranges::equal(*ic, *ranges::begin(expected))); + } + + if constexpr (copyable>) { + auto r2 = r; + const auto i2 = as_const(r2).begin(); + if (!is_empty) { + assert(ranges::equal(*i2, *ic)); + } + } + } + } + + // Validate split_view::end + STATIC_ASSERT(CanMemberEnd); + [[maybe_unused]] same_as> auto s = r.end(); + if (ranges::forward_range) { + assert((r.begin() == s) == is_empty); + } + STATIC_ASSERT(ranges::common_range == (ranges::forward_range && ranges::common_range) ); + if constexpr (!ranges::common_range) { + STATIC_ASSERT(same_as, default_sentinel_t>); + } + + STATIC_ASSERT(CanMemberEnd); + // clang-format off + constexpr bool should_be_const_common = ranges::forward_range + && ranges::forward_range> && ranges::common_range>; + // clang-format on + STATIC_ASSERT(ranges::common_range == should_be_const_common); + const auto sc = as_const(r).end(); + if constexpr (ranges::forward_range && ranges::forward_range>) { + STATIC_ASSERT(same_as>); + assert((as_const(r).begin() == sc) == is_empty); + } + if constexpr (!ranges::common_range) { + STATIC_ASSERT(same_as, default_sentinel_t>); + } + + // Validate view_interface::data + STATIC_ASSERT(!CanData); + STATIC_ASSERT(!CanData); + + // Validate view_interface::size + STATIC_ASSERT(!CanSize); + STATIC_ASSERT(!CanSize); + + // Validate view_interface::operator[] + STATIC_ASSERT(!CanIndex); + STATIC_ASSERT(!CanIndex); + + if (!is_empty) { + // Validate view_interface::front and back + STATIC_ASSERT(CanMemberFront == ranges::forward_range); + if constexpr (ranges::forward_range) { + assert(ranges::equal(r.front(), *ranges::begin(expected))); + } + + STATIC_ASSERT(CanMemberFront == ranges::forward_range>); + if constexpr (CanMemberFront) { + assert(ranges::equal(as_const(r).front(), *ranges::begin(expected))); + } + + STATIC_ASSERT(!CanMemberBack); + STATIC_ASSERT(!CanMemberBack); + } + + // Validate split_view::base() const& + STATIC_ASSERT(CanMemberBase == copy_constructible>); + if constexpr (CanMemberBase && ranges::forward_range) { + same_as> auto b1 = as_const(r).base(); + STATIC_ASSERT(noexcept(as_const(r).base()) == is_nothrow_copy_constructible_v>); + if (!is_empty) { + assert(*b1.begin() == *ranges::begin(*ranges::begin(expected))); + } + } + + // Validate split_view::base() && (NB: do this last since it leaves r moved-from) + if (ranges::forward_range>) { // intentionally not if constexpr + same_as> auto b2 = move(r).base(); + STATIC_ASSERT(noexcept(move(r).base()) == is_nothrow_move_constructible_v>); + if (!is_empty) { + assert(*b2.begin() == *ranges::begin(*ranges::begin(expected))); + } + } +} + +struct instantiator { + static constexpr string_view expected_single[] = { + "This"sv, "is"sv, "a"sv, "test,"sv, "this"sv, "is"sv, "only"sv, "a"sv, "test."sv}; + static constexpr string_view expected_range[] = {"Th"sv, " "sv, " a test, th"sv, " "sv, " only a test."sv}; + static constexpr string_view expected_empty[] = {"T"sv, "h"sv, "i"sv, "s"sv, " "sv, "i"sv, "s"sv, " "sv, "a"sv, + " "sv, "t"sv, "e"sv, "s"sv, "t"sv, ","sv, " "sv, "t"sv, "h"sv, "i"sv, "s"sv, " "sv, "i"sv, "s"sv, " "sv, "o"sv, + "n"sv, "l"sv, "y"sv, " "sv, "a"sv, " "sv, "t"sv, "e"sv, "s"sv, "t"sv, "."sv}; + + template + static constexpr decltype(auto) move_if_needed(T& t) noexcept { + if constexpr (ranges::view && !copyable) { + return move(t); + } else { + return t; + } + } + + template + static constexpr void call() { + { // Single-element delimiter + Read read{span{text}}; + test_one(move_if_needed(read), ' ', expected_single); + + Read empty{}; + test_one(move_if_needed(empty), ' ', views::empty); + } + { // Empty delimiter + Read read{span{text}}; + test_one(move_if_needed(read), views::empty, expected_empty); + + Read empty{}; + test_one(move_if_needed(empty), views::empty, views::empty); + } + if constexpr (ranges::forward_range) { // Range delimiter + Read read{span{text}}; + test_one(move_if_needed(read), "is"sv, expected_range); + + Read empty{}; + test_one(move_if_needed(empty), "is"sv, views::empty); + } + } +}; + +template +using test_range = test::range}, IsCommon, + test::CanCompare{derived_from || IsCommon == test::Common::yes}, + test::ProxyRef{!derived_from}, IsView, CanCopy>; + +constexpr bool instantiation_test() { + using test::CanView, test::Common, test::Copyability; + + // The view is sensitive to: + // 1. Category of the range to be split (input, forward) + // 2. Copyability + // 4. Commonality + // 3. Length of delimiter pattern (0/static 1/dynamic) [covered in instantiator::call] + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + + return true; +} + +int main() { + STATIC_ASSERT(instantiation_test()); + instantiation_test(); +} diff --git a/tests/std/tests/P0898R3_concepts/test.cpp b/tests/std/tests/P0898R3_concepts/test.cpp index a0d79d6010f..bb7b2aeaf20 100644 --- a/tests/std/tests/P0898R3_concepts/test.cpp +++ b/tests/std/tests/P0898R3_concepts/test.cpp @@ -1492,16 +1492,12 @@ namespace test_default_initializable { using std::default_initializable, std::initializer_list; STATIC_ASSERT(default_initializable); -#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-952724 +#if defined(MSVC_INTERNAL_TESTING) || defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-952724 STATIC_ASSERT(!default_initializable); -#else // ^^^ no workaround / assert bug so we'll notice when it's fixed vvv - STATIC_ASSERT(default_initializable); #endif // TRANSITION, DevCom-952724 STATIC_ASSERT(default_initializable); -#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-952724 +#if defined(MSVC_INTERNAL_TESTING) || defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-952724 STATIC_ASSERT(!default_initializable); -#else // ^^^ no workaround / assert bug so we'll notice when it's fixed vvv - STATIC_ASSERT(default_initializable); #endif // TRANSITION, DevCom-952724 STATIC_ASSERT(default_initializable); STATIC_ASSERT(!default_initializable); @@ -1515,10 +1511,8 @@ namespace test_default_initializable { STATIC_ASSERT(!default_initializable); STATIC_ASSERT(!default_initializable); STATIC_ASSERT(!default_initializable); -#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-952724 +#if defined(MSVC_INTERNAL_TESTING) || defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-952724 STATIC_ASSERT(!default_initializable); -#else // ^^^ no workaround / assert bug so we'll notice when it's fixed vvv - STATIC_ASSERT(default_initializable); #endif // TRANSITION, DevCom-952724 STATIC_ASSERT(!default_initializable); @@ -1563,10 +1557,8 @@ namespace test_default_initializable { int x; }; STATIC_ASSERT(default_initializable); -#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-952724 +#if defined(MSVC_INTERNAL_TESTING) || defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-952724 STATIC_ASSERT(!default_initializable); -#else // ^^^ no workaround / assert bug so we'll notice when it's fixed vvv - STATIC_ASSERT(default_initializable); #endif // TRANSITION, DevCom-952724 // Also test GH-1603 "default_initializable accepts types that are not default-initializable" diff --git a/tests/std/tests/P0912R5_coroutine/env.lst b/tests/std/tests/P0912R5_coroutine/env.lst index 642f530ffad..f873150e6bb 100644 --- a/tests/std/tests/P0912R5_coroutine/env.lst +++ b/tests/std/tests/P0912R5_coroutine/env.lst @@ -1,4 +1,21 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -RUNALL_INCLUDE ..\usual_latest_matrix.lst +RUNALL_INCLUDE ..\prefix.lst +RUNALL_CROSSLIST +PM_CL="/EHsc /MD /await:strict /std:c++14" +PM_CL="/EHsc /MD /await:strict /std:c++14 /permissive-" +PM_CL="/EHsc /MTd /await:strict /std:c++14 /permissive- /Zc:preprocessor" +PM_CL="/EHsc /MD /await:strict /std:c++14 /permissive- /analyze:only /analyze:autolog-" +PM_CL="/EHsc /MD /await:strict /std:c++17" +PM_CL="/EHsc /MD /await:strict /std:c++17 /permissive-" +PM_CL="/EHsc /MTd /await:strict /std:c++17 /permissive- /Zc:preprocessor" +PM_CL="/EHsc /MD /await:strict /std:c++17 /permissive- /analyze:only /analyze:autolog-" +PM_CL="/EHsc /MD /std:c++latest /permissive" +PM_CL="/EHsc /MD /std:c++latest /permissive-" +PM_CL="/EHsc /MTd /std:c++latest /permissive- /Zc:preprocessor" +PM_CL="/EHsc /MD /std:c++latest /permissive- /analyze:only /analyze:autolog-" +PM_CL="/BE /c /EHsc /MD /std:c++latest /permissive-" +PM_CL="/BE /c /EHsc /MTd /std:c++latest /permissive-" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MD /std:c++latest /permissive-" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MTd /std:c++latest /permissive-" diff --git a/tests/std/tests/P0912R5_coroutine/test.cpp b/tests/std/tests/P0912R5_coroutine/test.cpp index 0aa48651cb6..2edce782cee 100644 --- a/tests/std/tests/P0912R5_coroutine/test.cpp +++ b/tests/std/tests/P0912R5_coroutine/test.cpp @@ -9,6 +9,8 @@ #include using namespace std; +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + int g_tasks_destroyed{0}; struct Task { @@ -33,7 +35,8 @@ struct Task { void await_resume() noexcept {} coroutine_handle<> await_suspend(coroutine_handle h) noexcept { - if (auto& pre = h.promise().previous; pre) { + auto& pre = h.promise().previous; + if (pre) { return pre; // resume awaiting coroutine } @@ -97,50 +100,50 @@ Task triangular_number(const int n) { void test_noop_handle() { // Validate noop_coroutine_handle const noop_coroutine_handle noop = noop_coroutine(); - static_assert(noexcept(noop_coroutine())); + STATIC_ASSERT(noexcept(noop_coroutine())); const coroutine_handle<> as_void = noop; - static_assert(noexcept(static_cast>(noop_coroutine()))); + STATIC_ASSERT(noexcept(static_cast>(noop_coroutine()))); assert(noop); assert(as_void); - static_assert(noexcept(static_cast(noop))); - static_assert(noexcept(static_cast(as_void))); + STATIC_ASSERT(noexcept(static_cast(noop))); + STATIC_ASSERT(noexcept(static_cast(as_void))); assert(!noop.done()); assert(!as_void.done()); - static_assert(noexcept(noop.done())); - static_assert(noexcept(as_void.done())); + STATIC_ASSERT(noexcept(noop.done())); + STATIC_ASSERT(noexcept(as_void.done())); assert(noop); assert(as_void); noop(); as_void(); - static_assert(noexcept(noop())); + STATIC_ASSERT(noexcept(noop())); assert(noop); assert(as_void); noop.resume(); as_void.resume(); - static_assert(noexcept(noop.resume())); + STATIC_ASSERT(noexcept(noop.resume())); assert(noop); assert(as_void); noop.destroy(); as_void.destroy(); - static_assert(noexcept(noop.destroy())); + STATIC_ASSERT(noexcept(noop.destroy())); assert(noop); assert(as_void); assert(&noop.promise() != nullptr); - static_assert(noexcept(noop.promise())); + STATIC_ASSERT(noexcept(noop.promise())); assert(noop); assert(as_void); assert(noop.address() != nullptr); assert(noop.address() == as_void.address()); - static_assert(noexcept(noop.address())); - static_assert(noexcept(as_void.address())); + STATIC_ASSERT(noexcept(noop.address())); + STATIC_ASSERT(noexcept(as_void.address())); } int main() { diff --git a/tests/std/tests/P1502R1_standard_library_header_units/env.lst b/tests/std/tests/P1502R1_standard_library_header_units/env.lst index a5817069927..e0df24e714c 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/env.lst +++ b/tests/std/tests/P1502R1_standard_library_header_units/env.lst @@ -7,7 +7,7 @@ PM_CL="/w14365 /D_ENFORCE_FACET_SPECIALIZATIONS=1" RUNALL_CROSSLIST PM_CL="/w14640 /Zc:threadSafeInit- /EHsc /std:c++latest" RUNALL_CROSSLIST -PM_CL="/Zc:preprocessor /D_SILENCE_CXX17_STRSTREAM_DEPRECATION_WARNING" +PM_CL="/Zc:preprocessor /D_SILENCE_CXX17_STRSTREAM_DEPRECATION_WARNING /D_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING" RUNALL_CROSSLIST PM_CL="/MD" PM_CL="/MDd" diff --git a/tests/std/tests/P1502R1_standard_library_header_units/lit.local.cfg b/tests/std/tests/P1502R1_standard_library_header_units/lit.local.cfg index a58f404ef47..5f1221f3b0f 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/lit.local.cfg +++ b/tests/std/tests/P1502R1_standard_library_header_units/lit.local.cfg @@ -7,3 +7,4 @@ site.addsitedir(os.path.dirname(os.path.dirname(__file__))) import P1502R1_standard_library_header_units.custom_format config.test_format = P1502R1_standard_library_header_units.custom_format.CustomTestFormat() +config.parallelism_group = 'multi-threaded' 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 2102a025ea9..5794c45566a 100644 --- a/tests/std/tests/P1502R1_standard_library_header_units/test.cpp +++ b/tests/std/tests/P1502R1_standard_library_header_units/test.cpp @@ -206,7 +206,7 @@ int main() { assert(conv.from_bytes(utf8_koshka_cat) == utf16_koshka_cat); assert(conv.to_bytes(utf16_koshka_cat) == utf8_koshka_cat); - static_assert(static_cast(codecvt_mode::consume_header) == 4); // TRANSITION, DevCom-1160041 (deprecated) + static_assert(static_cast(codecvt_mode::consume_header) == 4); } { diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/env.lst b/tests/std/tests/VSO_0000000_vector_algorithms/env.lst index 19f025bd0e6..2e61ef96b9c 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/env.lst +++ b/tests/std/tests/VSO_0000000_vector_algorithms/env.lst @@ -2,3 +2,6 @@ # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception RUNALL_INCLUDE ..\usual_matrix.lst +RUNALL_CROSSLIST +PM_CL="" # Test default setting +PM_CL="/D_USE_STD_VECTOR_ALGORITHMS=0" # Test escape hatch, see GH-1751 diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 7fad05d2151..1b82f92ce4c 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -1187,6 +1187,20 @@ STATIC_ASSERT(__cpp_lib_polymorphic_allocator == 201902L); STATIC_ASSERT(__cpp_lib_quoted_string_io == 201304L); #endif +#if _HAS_CXX20 && !defined(__EDG__) // TRANSITION, EDG concepts support +#ifndef __cpp_lib_ranges +#error __cpp_lib_ranges is not defined +#elif __cpp_lib_ranges != 201911L +#error __cpp_lib_ranges is not 201911L +#else +STATIC_ASSERT(__cpp_lib_ranges == 201911L); +#endif +#else +#ifdef __cpp_lib_ranges +#error __cpp_lib_ranges is defined +#endif +#endif + #if _HAS_CXX17 #ifndef __cpp_lib_raw_memory_algorithms #error __cpp_lib_raw_memory_algorithms is not defined diff --git a/tests/std/tests/VSO_0180469_ptr_cat/test.compile.pass.cpp b/tests/std/tests/VSO_0180469_ptr_cat/test.compile.pass.cpp index 1862d65e7d8..0f8716cf6b7 100644 --- a/tests/std/tests/VSO_0180469_ptr_cat/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0180469_ptr_cat/test.compile.pass.cpp @@ -42,7 +42,7 @@ struct test_ptr_cat_helper { STATIC_ASSERT(!MoveReallyTrivial || MoveTriviallyCopyable); }; -template +template void test_ptr_cat() { (void) test_ptr_cat_helper{}; // Also make sure that the source being const doesn't change the answer @@ -59,16 +59,6 @@ void test_ptr_cat() { (void) test_ptr_cat_helper<0, const volatile Source*, const Dest*>{}; (void) test_ptr_cat_helper<0, const Source*, const volatile Dest*>{}; (void) test_ptr_cat_helper<0, const volatile Source*, const volatile Dest*>{}; - // volatile anywhere should go to the trivial implementation for builtin types, but - // the general implementation for PODs, since the compiler-generated copy assign has signature: - // Meow& operator=(const Meow&); - // which hates volatile on both the source and the target - (void) test_ptr_cat_helper{}; - (void) test_ptr_cat_helper{}; - (void) test_ptr_cat_helper{}; - (void) test_ptr_cat_helper{}; - (void) test_ptr_cat_helper{}; - (void) test_ptr_cat_helper{}; // Also make sure _Ptr_cat listens to the iterator type (void) test_ptr_cat_helper<0, typename list::iterator, typename list::iterator>{}; @@ -131,8 +121,8 @@ void ptr_cat_test_cases() { // Identity cases: test_ptr_cat<2, int, int>(); test_ptr_cat<2, bool, bool>(); - test_ptr_cat<2, pod_struct, pod_struct, 0>(); - test_ptr_cat<1, trivially_copyable_struct, trivially_copyable_struct, 0>(); + test_ptr_cat<2, pod_struct, pod_struct>(); + test_ptr_cat<1, trivially_copyable_struct, trivially_copyable_struct>(); test_ptr_cat<0, custom_copy_struct, custom_copy_struct>(); test_ptr_cat<2, int_enum, int_enum>(); test_ptr_cat<2, short_enum, short_enum>(); diff --git a/tests/utils/stl/test/config.py b/tests/utils/stl/test/config.py index 5cff67b5133..d18d8170f38 100644 --- a/tests/utils/stl/test/config.py +++ b/tests/utils/stl/test/config.py @@ -46,3 +46,6 @@ def configure(parameters, features, config, lit_config): ['-LIBPATH:' + os.path.normpath(dir) for dir in lit_config.library_dirs[config.name]] lit_config.test_env = {'PATH' : os.path.normpath(lit_config.cxx_runtime)} + + # Add a parallelism group that multi-threaded tests can be marked as a part of. + lit_config.parallelism_groups['multi-threaded'] = 1