diff --git a/.clang-format b/.clang-format index a5878f28342..5ea2651baa0 100644 --- a/.clang-format +++ b/.clang-format @@ -1,54 +1,40 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -# https://releases.llvm.org/9.0.0/tools/clang/docs/ClangFormatStyleOptions.html +# https://releases.llvm.org/10.0.0/tools/clang/docs/ClangFormatStyleOptions.html --- # Language: Cpp - BasedOnStyle: LLVM - # AccessModifierOffset: -2 AccessModifierOffset: -4 - # AlignAfterOpenBracket: Align AlignAfterOpenBracket: DontAlign - # AlignConsecutiveMacros: false AlignConsecutiveMacros: true - # AlignConsecutiveAssignments: false AlignConsecutiveAssignments: true - # AlignConsecutiveDeclarations: false - # AlignEscapedNewlines: Right AlignEscapedNewlines: Left - # AlignOperands: true - # AlignTrailingComments: true AlignTrailingComments: false - # AllowAllArgumentsOnNextLine: true # AllowAllConstructorInitializersOnNextLine: true # AllowAllParametersOfDeclarationOnNextLine: true -# AllowShortBlocksOnASingleLine: false +# AllowShortBlocksOnASingleLine: Never # AllowShortCaseLabelsOnASingleLine: false - # AllowShortFunctionsOnASingleLine: All AllowShortFunctionsOnASingleLine: Empty - # AllowShortLambdasOnASingleLine: All # AllowShortIfStatementsOnASingleLine: Never # AllowShortLoopsOnASingleLine: false # AlwaysBreakAfterDefinitionReturnType: None # AlwaysBreakAfterReturnType: None # AlwaysBreakBeforeMultilineStrings: false - # AlwaysBreakTemplateDeclarations: MultiLine AlwaysBreakTemplateDeclarations: Yes - # BinPackArguments: true # BinPackParameters: true # BraceWrapping: @@ -68,10 +54,8 @@ AlwaysBreakTemplateDeclarations: Yes # SplitEmptyFunction: true # SplitEmptyRecord: true # SplitEmptyNamespace: true - # BreakBeforeBinaryOperators: None BreakBeforeBinaryOperators: NonAssignment - # BreakBeforeBraces: Attach # BreakBeforeInheritanceComma: false # BreakInheritanceList: BeforeColon @@ -80,16 +64,16 @@ BreakBeforeBinaryOperators: NonAssignment # BreakConstructorInitializers: BeforeColon # BreakAfterJavaFieldAnnotations: false # BreakStringLiterals: true - # ColumnLimit: 80 ColumnLimit: 120 - # CommentPragmas: '^ IWYU pragma:' # CompactNamespaces: false # ConstructorInitializerAllOnOneLineOrOnePerLine: false # ConstructorInitializerIndentWidth: 4 # ContinuationIndentWidth: 4 # Cpp11BracedListStyle: true +# DeriveLineEnding: true +DeriveLineEnding: false # DerivePointerAlignment: false # DisableFormat: false # ExperimentalAutoDetectBinPacking: false @@ -98,55 +82,52 @@ ColumnLimit: 120 # - foreach # - Q_FOREACH # - BOOST_FOREACH - # IncludeBlocks: Preserve IncludeBlocks: Regroup - # IncludeCategories: # - Regex: '^"(llvm|llvm-c|clang|clang-c)/' # Priority: 2 +# SortPriority: 0 # - Regex: '^(<|"(gtest|gmock|isl|json)/)' # Priority: 3 +# SortPriority: 0 # - Regex: '.*' # Priority: 1 +# SortPriority: 0 IncludeCategories: - Regex: '^$' Priority: 1 - Regex: '^<(Windows|userenv)\.h>$' Priority: 3 + SortPriority: 3 - Regex: '^$' - Priority: 4 + Priority: 3 + SortPriority: 4 - Regex: '^<__.*\.hpp>$' Priority: 2 - Regex: '\.hpp[>"]$' Priority: 5 - Regex: '.*' Priority: 2 - # IncludeIsMainRegex: '(Test)?$' +# IncludeIsMainSourceRegex: '' # IndentCaseLabels: false +# IndentGotoLabels: true # IndentPPDirectives: None - # IndentWidth: 2 IndentWidth: 4 - # IndentWrappedFunctionNames: false IndentWrappedFunctionNames: true - # JavaScriptQuotes: Leave # JavaScriptWrapImports: true # KeepEmptyLinesAtTheStartOfBlocks: true - # NOTE: MacroBlockBegin/MacroBlockEnd don't work with _CATCH_ALL. # MacroBlockBegin: '' # MacroBlockEnd: '' - # MaxEmptyLinesToKeep: 1 MaxEmptyLinesToKeep: 2 - # NamespaceIndentation: None NamespaceIndentation: All - # ObjCBinPackProtocolList: Auto # ObjCBlockIndentWidth: 2 # ObjCSpaceAfterProperty: false @@ -159,17 +140,13 @@ NamespaceIndentation: All # PenaltyBreakTemplateDeclaration: 10 # PenaltyExcessCharacter: 1000000 # PenaltyReturnTypeOnItsOwnLine: 60 - # PointerAlignment: Right PointerAlignment: Left - # ReflowComments: true # SortIncludes: true # SortUsingDeclarations: true - # SpaceAfterCStyleCast: false SpaceAfterCStyleCast: true - # SpaceAfterLogicalNot: false # SpaceAfterTemplateKeyword: true # SpaceBeforeAssignmentOperators: true @@ -178,20 +155,22 @@ SpaceAfterCStyleCast: true # SpaceBeforeInheritanceColon: true # SpaceBeforeParens: ControlStatements # SpaceBeforeRangeBasedForLoopColon: true +# SpaceInEmptyBlock: false # SpaceInEmptyParentheses: false # SpacesBeforeTrailingComments: 1 # SpacesInAngles: false +# SpacesInConditionalStatement: false # SpacesInContainerLiterals: true # SpacesInCStyleCastParentheses: false # SpacesInParentheses: false # SpacesInSquareBrackets: false -# Standard: Cpp11 - -# NOTE: _STD_BEGIN, _STD_END, etc. aren't macros for complete statements, but telling clang-format that they are -# produces the behavior that we want (with no block indentation). +# SpaceBeforeSquareBrackets: false +# Standard: Latest # StatementMacros: # - Q_UNUSED # - QT_REQUIRE_VERSION +# NOTE: _STD_BEGIN, _STD_END, etc. aren't macros for complete statements, but telling +# clang-format that they are produces the behavior that we want (with no block indentation). StatementMacros: - _STD_BEGIN - _STD_END @@ -201,7 +180,8 @@ StatementMacros: - _END_EXTERN_C - _EXTERN_C_UNLESS_PURE - _END_EXTERN_C_UNLESS_PURE - # TabWidth: 8 +# UseCRLF: false +UseCRLF: true # UseTab: Never ... diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ed60548213..1f977d62bd1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,13 +1,17 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -cmake_minimum_required(VERSION 3.16) +if (NOT DEFINED CMAKE_TOOLCHAIN_FILE AND EXISTS "${CMAKE_CURRENT_LIST_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake") + set(CMAKE_TOOLCHAIN_FILE "${CMAKE_CURRENT_LIST_DIR}/vcpkg/scripts/buildsystems/vcpkg.cmake") +endif() + +cmake_minimum_required(VERSION 3.17) set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) project(msvc_standard_libraries LANGUAGES CXX) find_package(Boost REQUIRED) -set(VCLIBS_MIN_BOOST_VERSION 1.70.0) +set(VCLIBS_MIN_BOOST_VERSION 1.73.0) if("${Boost_VERSION}" VERSION_LESS "${VCLIBS_MIN_BOOST_VERSION}") message(FATAL_ERROR "Detected Boost version is too old (older than ${VCLIBS_MIN_BOOST_VERSION}).") endif() @@ -32,27 +36,27 @@ if("${VCLIBS_TARGET_ARCHITECTURE}" MATCHES "^[xX]86$") set(VCLIBS_X86_OR_X64 "x86") # Note that we set _WIN32_WINNT to a high level to make declarations available, but still engage downlevel # runtime dynamic linking by setting our own _STL_WIN32_WINNT back to Windows XP. - add_compile_definitions(_X86_ _VCRT_WIN32_WINNT=_WIN32_WINNT_WINXP _STL_WIN32_WINNT=_WIN32_WINNT_WINXP + add_compile_definitions(_X86_ _VCRT_WIN32_WINNT=0x0501 _STL_WIN32_WINNT=0x0501 _WIN32_WINNT=0x0602 NTDDI_VERSION=NTDDI_WIN8) add_compile_options(/arch:IA32) elseif(VCLIBS_TARGET_ARCHITECTURE MATCHES "^[xX]64$") set(VCLIBS_TARGET_ARCHITECTURE "x64") set(VCLIBS_I386_OR_AMD64 "amd64") set(VCLIBS_X86_OR_X64 "x64") - add_compile_definitions(_AMD64_ _VCRT_WIN32_WINNT=_WIN32_WINNT_WINXP _STL_WIN32_WINNT=_WIN32_WINNT_WINXP + add_compile_definitions(_AMD64_ _VCRT_WIN32_WINNT=0x0501 _STL_WIN32_WINNT=0x0501 _WIN32_WINNT=0x0602 NTDDI_VERSION=NTDDI_WIN8) elseif(VCLIBS_TARGET_ARCHITECTURE MATCHES "^[aA][rR][mM][vV]7$") set(VCLIBS_TARGET_ARCHITECTURE "arm") set(VCLIBS_I386_OR_AMD64 "arm") set(VCLIBS_X86_OR_X64 "arm") - add_compile_definitions(_ARM_ _VCRT_WIN32_WINNT=_WIN32_WINNT_WIN8 _STL_WIN32_WINNT=_WIN32_WINNT_WIN8 + add_compile_definitions(_ARM_ _VCRT_WIN32_WINNT=0x0602 _STL_WIN32_WINNT=0x0602 _WIN32_WINNT=0x0602 NTDDI_VERSION=NTDDI_WIN8) string(APPEND CMAKE_CXX_STANDARD_LIBRARIES " Synchronization.lib") elseif(VCLIBS_TARGET_ARCHITECTURE MATCHES "^[aA][rR][mM]64$") set(VCLIBS_TARGET_ARCHITECTURE "arm64") set(VCLIBS_I386_OR_AMD64 "arm64") set(VCLIBS_X86_OR_X64 "arm64") - add_compile_definitions(_ARM64_ _VCRT_WIN32_WINNT=_WIN32_WINNT_WIN10 _STL_WIN32_WINNT=_WIN32_WINNT_WIN10 + add_compile_definitions(_ARM64_ _VCRT_WIN32_WINNT=0x0A00 _STL_WIN32_WINNT=0x0A00 _WIN32_WINNT=0x0A00 NTDDI_VERSION=NTDDI_WIN10) string(APPEND CMAKE_CXX_STANDARD_LIBRARIES " Synchronization.lib") else() diff --git a/README.md b/README.md index f092b0b191a..5b361a006e4 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ Just try to follow these rules, so we can spend more time fixing bugs and implem The STL uses boost-math headers to provide P0226R1 Mathematical Special Functions. We recommend using [vcpkg][] to acquire this dependency. -1. Install Visual Studio 2019 16.7 Preview 3 or later. +1. Install Visual Studio 2019 16.8 Preview 1 or later. 2. Invoke `git clone https://github.com/microsoft/vcpkg` 3. Invoke `cd vcpkg` 4. Invoke `.\bootstrap-vcpkg.bat` @@ -159,19 +159,19 @@ acquire this dependency. These instructions assume you're targeting `x64-windows`; you can change this constant below to target other architectures. -1. Install [CMake][] 3.16.5 or later, [Ninja][] 1.10.0 or later, and Visual Studio 2019 16.7 Preview 3 or later. -2. Invoke `git clone https://github.com/microsoft/vcpkg` -3. Invoke `cd vcpkg` -4. Invoke `.\bootstrap-vcpkg.bat` -5. Invoke `.\vcpkg.exe install boost-math:x64-windows` to install the boost-math dependency. -6. Open an "x64 Native Tools Command Prompt for VS 2019". -7. Change directories to a location where you'd like a clone of this STL repository. -8. Invoke `git clone https://github.com/microsoft/STL` -9. Invoke `cd STL` -10. Invoke `cmake -G Ninja -DCMAKE_TOOLCHAIN_FILE={where your vcpkg clone is located}\scripts\buildsystems\vcpkg.cmake --S . -B {wherever you want binaries}` to configure the project. For example, `cmake -G Ninja --DCMAKE_TOOLCHAIN_FILE=C:\Dev\vcpkg\scripts\buildsystems\vcpkg.cmake -S . -B out\build\x64` -11. Invoke `ninja -C {wherever you want binaries}` to build the project. For example, `ninja -C out\build\x64` +1. Install Visual Studio 2019 16.8 Preview 1 or later. + * We recommend selecting "C++ CMake tools for Windows" in the VS Installer. + This will ensure that you're using supported versions of CMake and Ninja. + * Otherwise, install [CMake][] 3.17 or later, and [Ninja][] 1.8.2 or later. +2. Open an "x64 Native Tools Command Prompt for VS 2019". +3. Change directories to a location where you'd like a clone of this STL repository. +4. Invoke `git clone https://github.com/microsoft/STL` +5. Invoke `cd STL` +6. Invoke `git submodule update --init vcpkg` +7. Invoke `.\vcpkg\bootstrap-vcpkg.bat` +8. Invoke `.\vcpkg\vcpkg.exe install boost-math:x86-windows boost-math:x64-windows` to install the boost-math dependency. +9. Invoke `cmake -G Ninja -S . -B {wherever you want binaries}` to configure the project. For example, `cmake -G Ninja -S . -B out\build\x64` +10. Invoke `ninja -C {wherever you want binaries}` to build the project. For example, `ninja -C out\build\x64` # How To Consume @@ -225,8 +225,10 @@ C:\Users\bion\Desktop>dumpbin /IMPORTS .\example.exe | findstr msvcp 1. Follow either [How To Build With A Native Tools Command Prompt][] or [How To Build With The Visual Studio IDE][]. 2. Invoke `git submodule update --init llvm-project` at the root of the STL source tree. 3. Acquire [Python][] 3.8 or newer and have it on the `PATH` (or run it directly using its absolute or relative path). -4. Have LLVM's `bin` directory on the `PATH`. Simply using [LLVM's installer][] and choosing to add LLVM to your `PATH` -during installation is the easiest way to get LLVM's `bin` directory on your `PATH`. +4. Have LLVM's `bin` directory on the `PATH` (so `clang-cl.exe` is available). + * We recommend selecting "C++ Clang tools for Windows" in the VS Installer. This will automatically add LLVM to the + `PATH` of the x86 and x64 Native Tools Command Prompts, and will ensure that you're using a supported version. + * Otherwise, use [LLVM's installer][] and choose to add LLVM to your `PATH` during installation. 5. Follow the instructions below. ## Running All The Tests diff --git a/azure-devops/create-vmss.ps1 b/azure-devops/create-vmss.ps1 index ff218265be6..9f47f3825e9 100644 --- a/azure-devops/create-vmss.ps1 +++ b/azure-devops/create-vmss.ps1 @@ -11,7 +11,7 @@ system. See https://docs.microsoft.com/en-us/azure/virtual-machine-scale-sets/ov for more information. This script assumes you have installed Azure tools into PowerShell by following the instructions -at https://docs.microsoft.com/en-us/powershell/azure/install-az-ps?view=azps-3.6.1 +at https://docs.microsoft.com/en-us/powershell/azure/install-az-ps or are running from Azure Cloud Shell. #> @@ -211,20 +211,20 @@ $denyEverythingElse = New-AzNetworkSecurityRuleConfig ` -DestinationAddressPrefix * ` -DestinationPortRange * -$NetworkSecurityGroupName = $ResourceGroupName + 'NetworkSecurity' +$NetworkSecurityGroupName = $ResourceGroupName + '-NetworkSecurity' $NetworkSecurityGroup = New-AzNetworkSecurityGroup ` -Name $NetworkSecurityGroupName ` -ResourceGroupName $ResourceGroupName ` -Location $Location ` -SecurityRules @($allowHttp, $allowDns, $denyEverythingElse) -$SubnetName = $ResourceGroupName + 'Subnet' +$SubnetName = $ResourceGroupName + '-Subnet' $Subnet = New-AzVirtualNetworkSubnetConfig ` -Name $SubnetName ` -AddressPrefix "10.0.0.0/16" ` -NetworkSecurityGroup $NetworkSecurityGroup -$VirtualNetworkName = $ResourceGroupName + 'Network' +$VirtualNetworkName = $ResourceGroupName + '-Network' $VirtualNetwork = New-AzVirtualNetwork ` -Name $VirtualNetworkName ` -ResourceGroupName $ResourceGroupName ` @@ -237,7 +237,7 @@ Write-Progress ` -Activity 'Creating prototype VM' ` -PercentComplete (100 / $TotalProgress * $CurrentProgress++) -$NicName = $ResourceGroupName + 'NIC' +$NicName = $ResourceGroupName + '-NIC' $Nic = New-AzNetworkInterface ` -Name $NicName ` -ResourceGroupName $ResourceGroupName ` @@ -272,13 +272,15 @@ Write-Progress ` -Status 'Running provisioning script provision-image.ps1 in VM' ` -PercentComplete (100 / $TotalProgress * $CurrentProgress++) -Invoke-AzVMRunCommand ` +$ProvisionImageResult = Invoke-AzVMRunCommand ` -ResourceGroupName $ResourceGroupName ` -VMName $ProtoVMName ` -CommandId 'RunPowerShellScript' ` -ScriptPath "$PSScriptRoot\provision-image.ps1" ` -Parameter @{AdminUserPassword = $AdminPW } +Write-Host "provision-image.ps1 output: $($ProvisionImageResult.value.Message)" + #################################################################################################### Write-Progress ` -Activity $ProgressActivity ` @@ -343,9 +345,9 @@ Write-Progress ` -Status 'Creating scale set' ` -PercentComplete (100 / $TotalProgress * $CurrentProgress++) -$VmssIpConfigName = $ResourceGroupName + 'VmssIpConfig' +$VmssIpConfigName = $ResourceGroupName + '-VmssIpConfig' $VmssIpConfig = New-AzVmssIpConfig -SubnetId $Nic.IpConfigurations[0].Subnet.Id -Primary -Name $VmssIpConfigName -$VmssName = $ResourceGroupName + 'Vmss' +$VmssName = $ResourceGroupName + '-Vmss' $Vmss = New-AzVmssConfig ` -Location $Location ` -SkuCapacity 0 ` diff --git a/azure-devops/enforce-clang-format.cmd b/azure-devops/enforce-clang-format.cmd index f59ae28a7f8..1e1010afe93 100644 --- a/azure-devops/enforce-clang-format.cmd +++ b/azure-devops/enforce-clang-format.cmd @@ -1,5 +1,7 @@ :: Copyright (c) Microsoft Corporation. :: SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +call "%PROGRAMFILES(X86)%\Microsoft Visual Studio\2019\Preview\Common7\Tools\VsDevCmd.bat" ^ +-host_arch=amd64 -arch=amd64 -no_logo "%1" "clang-format.exe -style=file -i" ^ stl/inc ^ stl/src ^ diff --git a/azure-devops/provision-image.ps1 b/azure-devops/provision-image.ps1 index 7cc0072eaa4..a207f10e8e4 100644 --- a/azure-devops/provision-image.ps1 +++ b/azure-devops/provision-image.ps1 @@ -50,10 +50,14 @@ Function Get-TempFilePath { return Join-Path $tempPath $tempName } -if (-not [string]::IsNullOrEmpty($AdminUserPassword)) { - Write-Host "AdminUser password supplied; switching to AdminUser" +$TranscriptPath = 'C:\provision-image-transcript.txt' + +if ([string]::IsNullOrEmpty($AdminUserPassword)) { + Start-Transcript -Path $TranscriptPath +} else { + Write-Host 'AdminUser password supplied; switching to AdminUser.' $PsExecPath = Get-TempFilePath -Extension 'exe' - Write-Host "Downloading psexec to $PsExecPath" + Write-Host "Downloading psexec to: $PsExecPath" & curl.exe -L -o $PsExecPath -s -S https://live.sysinternals.com/PsExec64.exe $PsExecArgs = @( '-u', @@ -69,9 +73,11 @@ if (-not [string]::IsNullOrEmpty($AdminUserPassword)) { $PSCommandPath ) - Write-Host "Executing $PsExecPath " + @PsExecArgs + Write-Host "Executing: $PsExecPath $PsExecArgs" $proc = Start-Process -FilePath $PsExecPath -ArgumentList $PsExecArgs -Wait -PassThru + Write-Host 'Reading transcript...' + Get-Content -Path $TranscriptPath Write-Host 'Cleaning up...' Remove-Item $PsExecPath exit $proc.ExitCode @@ -79,20 +85,19 @@ if (-not [string]::IsNullOrEmpty($AdminUserPassword)) { $Workloads = @( 'Microsoft.VisualStudio.Component.VC.CLI.Support', + 'Microsoft.VisualStudio.Component.VC.CMake.Project', 'Microsoft.VisualStudio.Component.VC.CoreIde', - 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', - 'Microsoft.VisualStudio.Component.VC.Tools.ARM64', + 'Microsoft.VisualStudio.Component.VC.Llvm.Clang', 'Microsoft.VisualStudio.Component.VC.Tools.ARM', - 'Microsoft.VisualStudio.Component.Windows10SDK.18362' + 'Microsoft.VisualStudio.Component.VC.Tools.ARM64', + 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', + 'Microsoft.VisualStudio.Component.Windows10SDK.19041' ) $ReleaseInPath = 'Preview' $Sku = 'Enterprise' $VisualStudioBootstrapperUrl = 'https://aka.ms/vs/16/pre/vs_enterprise.exe' -$CMakeUrl = 'https://github.com/Kitware/CMake/releases/download/v3.16.5/cmake-3.16.5-win64-x64.msi' -$LlvmUrl = 'https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/LLVM-10.0.0-win64.exe' -$NinjaUrl = 'https://github.com/ninja-build/ninja/releases/download/v1.10.0/ninja-win.zip' -$PythonUrl = 'https://www.python.org/ftp/python/3.8.2/python-3.8.2-amd64.exe' +$PythonUrl = 'https://www.python.org/ftp/python/3.8.5/python-3.8.5-amd64.exe' $CudaUrl = 'https://developer.download.nvidia.com/compute/cuda/10.1/Prod/local_installers/cuda_10.1.243_426.00_win10.exe' $CudaFeatures = 'nvcc_10.1 cuobjdump_10.1 nvprune_10.1 cupti_10.1 gpu_library_advisor_10.1 memcheck_10.1 ' + ` @@ -162,7 +167,7 @@ Function InstallVisualStudio { Write-Host 'Downloading Visual Studio...' [string]$bootstrapperExe = Get-TempFilePath -Extension 'exe' curl.exe -L -o $bootstrapperExe -s -S $BootstrapperUrl - Write-Host "Installing Visual Studio..." + Write-Host 'Installing Visual Studio...' $args = @('/c', $bootstrapperExe, '--quiet', '--norestart', '--wait', '--nocache') foreach ($workload in $Workloads) { $args += '--add' @@ -187,103 +192,6 @@ Function InstallVisualStudio { } } -<# -.SYNOPSIS -Install an .msi file. - -.DESCRIPTION -InstallMSI takes a URL where an .msi lives, and installs that .msi to the system. - -.PARAMETER Name -The name of the thing to install. - -.PARAMETER Url -The URL at which the .msi lives. -#> -Function InstallMSI { - Param( - [String]$Name, - [String]$Url - ) - - try { - Write-Host "Downloading $Name..." - [string]$msiPath = Get-TempFilePath -Extension 'msi' - curl.exe -L -o $msiPath -s -S $Url - Write-Host "Installing $Name..." - $args = @('/i', $msiPath, '/norestart', '/quiet', '/qn') - $proc = Start-Process -FilePath 'msiexec.exe' -ArgumentList $args -Wait -PassThru - PrintMsiExitCodeMessage $proc.ExitCode - } - catch { - Write-Error "Failed to install $Name! $($_.Exception.Message)" - } -} - -<# -.SYNOPSIS -Unpacks a zip file to $Dir. - -.DESCRIPTION -InstallZip takes a URL of a zip file, and unpacks the zip file to the directory -$Dir. - -.PARAMETER Name -The name of the tool being installed. - -.PARAMETER Url -The URL of the zip file to unpack. - -.PARAMETER Dir -The directory to unpack the zip file to. -#> -Function InstallZip { - Param( - [String]$Name, - [String]$Url, - [String]$Dir - ) - - try { - Write-Host "Downloading $Name..." - [string]$zipPath = Get-TempFilePath -Extension 'zip' - curl.exe -L -o $zipPath -s -S $Url - Write-Host "Installing $Name..." - Expand-Archive -Path $zipPath -DestinationPath $Dir -Force - } - catch { - Write-Error "Failed to install $Name! $($_.Exception.Message)" - } -} - -<# -.SYNOPSIS -Installs LLVM. - -.DESCRIPTION -InstallLLVM installs LLVM from the supplied URL. - -.PARAMETER Url -The URL of the LLVM installer. -#> -Function InstallLLVM { - Param( - [String]$Url - ) - - try { - Write-Host 'Downloading LLVM...' - [string]$installerPath = Get-TempFilePath -Extension 'exe' - curl.exe -L -o $installerPath -s -S $Url - Write-Host 'Installing LLVM...' - $proc = Start-Process -FilePath $installerPath -ArgumentList @('/S') -NoNewWindow -Wait -PassThru - PrintMsiExitCodeMessage $proc.ExitCode - } - catch { - Write-Error "Failed to install LLVM! $($_.Exception.Message)" - } -} - <# .SYNOPSIS Installs Python. @@ -369,16 +277,16 @@ Function PipInstall { ) try { - Write-Host 'Installing or upgrading $Package...' + Write-Host "Installing or upgrading $Package..." python.exe -m pip install --upgrade $Package - Write-Host 'Done installing or upgrading $Package' + Write-Host "Done installing or upgrading $Package." } catch { - Write-Error "Failed to install or upgrade $Package" + Write-Error "Failed to install or upgrade $Package." } } -Write-Host "AdminUser password not supplied; assuming already running as AdminUser" +Write-Host 'AdminUser password not supplied; assuming already running as AdminUser.' Write-Host 'Configuring AntiVirus exclusions...' Add-MpPreference -ExclusionPath C:\agent @@ -388,20 +296,28 @@ Add-MpPreference -ExclusionProcess clang-cl.exe Add-MpPreference -ExclusionProcess cl.exe Add-MpPreference -ExclusionProcess link.exe Add-MpPreference -ExclusionProcess python.exe +Add-MpPreference -ExclusionProcess test.exe -InstallMSI 'CMake' $CMakeUrl -InstallZip 'Ninja' $NinjaUrl 'C:\Program Files\CMake\bin' -InstallLLVM $LlvmUrl InstallPython $PythonUrl InstallVisualStudio -Workloads $Workloads -BootstrapperUrl $VisualStudioBootstrapperUrl InstallCuda -Url $CudaUrl -Features $CudaFeatures Write-Host 'Updating PATH...' + +# Step 1: Read the system path, which was just updated by installing Python. $environmentKey = Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' -Name Path -$Env:PATH="$($environmentKey.Path);C:\Program Files\CMake\bin;C:\Program Files\LLVM\bin" + +# Step 2: Update the local path (for this running script), so PipInstall can run python.exe. +# Additional directories can be added here (e.g. if we extracted a zip file +# or installed something that didn't update the system path). +$Env:PATH="$($environmentKey.Path)" + +# Step 3: Update the system path, permanently recording any additional directories that were added in the previous step. Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' ` -Name Path ` -Value "$Env:PATH" +Write-Host 'Finished updating PATH!' + PipInstall pip PipInstall psutil diff --git a/azure-devops/sysprep.ps1 b/azure-devops/sysprep.ps1 index bb057e3c55f..285719366d4 100644 --- a/azure-devops/sysprep.ps1 +++ b/azure-devops/sysprep.ps1 @@ -3,4 +3,4 @@ $ErrorActionPreference = 'Stop' Write-Host 'Running sysprep' -& C:\Windows\system32\sysprep\sysprep.exe /oobe /generalize /shutdown +& C:\Windows\system32\sysprep\sysprep.exe /oobe /generalize /mode:vm /shutdown diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e0b6adb4807..ea1b680c6e3 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -4,7 +4,7 @@ # Build STL targeting x86, x64, arm, arm64 variables: - agentPool: 'StlBuild-2020-07-08-2' + agentPool: 'StlBuild-2020-08-07' tmpDir: 'D:\Temp' stages: diff --git a/stl/CMakeLists.txt b/stl/CMakeLists.txt index 4d03fa0e186..6a2069bae7f 100644 --- a/stl/CMakeLists.txt +++ b/stl/CMakeLists.txt @@ -196,6 +196,7 @@ set(HEADERS ${CMAKE_CURRENT_LIST_DIR}/inc/vector ${CMAKE_CURRENT_LIST_DIR}/inc/version ${CMAKE_CURRENT_LIST_DIR}/inc/xatomic.h + ${CMAKE_CURRENT_LIST_DIR}/inc/xatomic_wait.h ${CMAKE_CURRENT_LIST_DIR}/inc/xbit_ops.h ${CMAKE_CURRENT_LIST_DIR}/inc/xcall_once.h ${CMAKE_CURRENT_LIST_DIR}/inc/xcharconv.h @@ -241,7 +242,6 @@ set(IMPLIB_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/filesystem.cpp ${CMAKE_CURRENT_LIST_DIR}/src/locale0_implib.cpp ${CMAKE_CURRENT_LIST_DIR}/src/nothrow.cpp - ${CMAKE_CURRENT_LIST_DIR}/src/parallel_algorithms.cpp ${CMAKE_CURRENT_LIST_DIR}/src/sharedmutex.cpp ${CMAKE_CURRENT_LIST_DIR}/src/syserror_import_lib.cpp ${CMAKE_CURRENT_LIST_DIR}/src/vector_algorithms.cpp @@ -388,6 +388,11 @@ set(SOURCES_SATELLITE_2 ${CMAKE_CURRENT_LIST_DIR}/src/special_math.cpp ) +set(SOURCES_SATELLITE_ATOMIC_WAIT + ${CMAKE_CURRENT_LIST_DIR}/src/atomic_wait.cpp + ${CMAKE_CURRENT_LIST_DIR}/src/parallel_algorithms.cpp +) + set(SOURCES_SATELLITE_CODECVT_IDS ${CMAKE_CURRENT_LIST_DIR}/src/ulocale.cpp ) @@ -396,6 +401,7 @@ set(SOURCES_SATELLITE_CODECVT_IDS set(STATIC_SOURCES ${SOURCES_SATELLITE_1} ${SOURCES_SATELLITE_2} + ${SOURCES_SATELLITE_ATOMIC_WAIT} ${SOURCES_SATELLITE_CODECVT_IDS} ) @@ -459,6 +465,28 @@ function(add_stl_dlls D_SUFFIX THIS_CONFIG_DEFINITIONS THIS_CONFIG_COMPILE_OPTIO set_target_properties(msvcp_2${D_SUFFIX} PROPERTIES OUTPUT_NAME "msvcp140_2${D_SUFFIX}${VCLIBS_SUFFIX}") target_link_options(msvcp_2${D_SUFFIX} PRIVATE "${THIS_CONFIG_LINK_OPTIONS}") + # msvcp140_atomic_wait.dll (the atomic wait satellite) + add_library(msvcp${D_SUFFIX}_atomic_wait_objects OBJECT ${SOURCES_SATELLITE_ATOMIC_WAIT}) + target_compile_definitions(msvcp${D_SUFFIX}_atomic_wait_objects PRIVATE "_BUILDING_SATELLITE_ATOMIC_WAIT;_DLL;${THIS_CONFIG_DEFINITIONS}") + target_compile_options(msvcp${D_SUFFIX}_atomic_wait_objects PRIVATE "${THIS_CONFIG_COMPILE_OPTIONS};${GL_FLAG};/EHsc") + + # generate the .def for msvcp140_atomic_wait.dll + set(_ATOMIC_WAIT_OUTPUT_NAME "msvcp140${D_SUFFIX}_atomic_wait${VCLIBS_SUFFIX}") + string(TOUPPER "${_ATOMIC_WAIT_OUTPUT_NAME}" _ATOMIC_WAIT_OUTPUT_NAME_UPPER) + set(_ATOMIC_WAIT_DEF_NAME "${CMAKE_BINARY_DIR}/msvcp_atomic_wait${D_SUFFIX}.def") + set(_ATOMIC_WAIT_DEF_FILE_SRC "${CMAKE_CURRENT_LIST_DIR}/src/msvcp_atomic_wait.src") + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${_ATOMIC_WAIT_DEF_FILE_SRC}") + file(READ "${_ATOMIC_WAIT_DEF_FILE_SRC}" _ATOMIC_WAIT_SRC_CONTENTS) + string(REPLACE "LIBRARYNAME" "${_ATOMIC_WAIT_OUTPUT_NAME_UPPER}" _ATOMIC_WAIT_DEF_CONTENTS "${_ATOMIC_WAIT_SRC_CONTENTS}") + 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}" "${TOOLSET_LIB}/vcruntime${D_SUFFIX}.lib" "${TOOLSET_LIB}/msvcrt${D_SUFFIX}.lib" "ucrt${D_SUFFIX}.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}") + target_link_options(msvcp${D_SUFFIX}_atomic_wait PRIVATE "${THIS_CONFIG_LINK_OPTIONS}") + # msvcp140_codecvt_ids.dll add_library(msvcp${D_SUFFIX}_codecvt_ids_objects OBJECT ${SOURCES_SATELLITE_CODECVT_IDS}) target_compile_definitions(msvcp${D_SUFFIX}_codecvt_ids_objects PRIVATE "_BUILDING_SATELLITE_CODECVT_IDS;_DLL;${THIS_CONFIG_DEFINITIONS}") @@ -474,8 +502,8 @@ function(add_stl_dlls D_SUFFIX THIS_CONFIG_DEFINITIONS THIS_CONFIG_COMPILE_OPTIO # import library add_library(msvcp${D_SUFFIX}_implib STATIC ${HEADERS}) target_link_libraries(msvcp${D_SUFFIX}_implib msvcp${D_SUFFIX}_implib_objects std_init_once_begin_initialize std_init_once_complete) - add_dependencies(msvcp${D_SUFFIX}_implib msvcp${D_SUFFIX} msvcp_1${D_SUFFIX} msvcp_2${D_SUFFIX} msvcp${D_SUFFIX}_codecvt_ids) - set_target_properties(msvcp${D_SUFFIX}_implib PROPERTIES STATIC_LIBRARY_OPTIONS "/NOLOGO;/NODEFAULTLIB;/IGNORE:4006;$;$;$;$") + add_dependencies(msvcp${D_SUFFIX}_implib msvcp${D_SUFFIX} msvcp_1${D_SUFFIX} msvcp_2${D_SUFFIX} msvcp${D_SUFFIX}_atomic_wait msvcp${D_SUFFIX}_codecvt_ids) + set_target_properties(msvcp${D_SUFFIX}_implib PROPERTIES STATIC_LIBRARY_OPTIONS "/NOLOGO;/NODEFAULTLIB;/IGNORE:4006;$;$;$;$;$") set_target_properties(msvcp${D_SUFFIX}_implib PROPERTIES ARCHIVE_OUTPUT_NAME "msvcprt${D_SUFFIX}") endfunction() diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 9f5ea0f97cf..f684affcc07 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -17,6 +17,26 @@ _STL_DISABLE_CLANG_WARNINGS #pragma push_macro("new") #undef new +#if _USE_STD_VECTOR_ALGORITHMS +_EXTERN_C +// The "noalias" attribute tells the compiler optimizer that pointers going into these hand-vectorized algorithms +// won't be stored beyond the lifetime of the function, and that the function will only reference arrays denoted by +// those pointers. The optimizer also assumes in that case that a pointer parameter is not returned to the caller via +// the return value, so functions using "noalias" must usually return void. This attribute is valuable because these +// functions are in native code objects that the compiler cannot analyze. In the absence of the noalias attribute, the +// compiler has to assume that the denoted arrays are "globally address taken", and that any later calls to +// unanalyzable routines may modify those arrays. +__declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_1( + const void* _First, const void* _Last, void* _Dest) noexcept; +__declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_2( + const void* _First, const void* _Last, void* _Dest) noexcept; +__declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_4( + const void* _First, const void* _Last, void* _Dest) noexcept; +__declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_8( + const void* _First, const void* _Last, void* _Dest) noexcept; +_END_EXTERN_C +#endif // _USE_STD_VECTOR_ALGORITHMS + _STD_BEGIN // COMMON SORT PARAMETERS _INLINE_VAR constexpr int _ISORT_MAX = 32; // maximum size for insertion sort @@ -336,21 +356,40 @@ namespace ranges { indirectly_unary_invocable> _Fn> constexpr for_each_result<_It, _Fn> operator()(_It _First, _Se _Last, _Fn _Func, _Pj _Proj = {}) const { _Adl_verify_range(_First, _Last); - auto _UFirst = _Get_unwrapped(_STD move(_First)); - const auto _ULast = _Get_unwrapped(_STD move(_Last)); - for (; _UFirst != _ULast; ++_UFirst) { - _STD invoke(_Func, _STD invoke(_Proj, *_UFirst)); - } - _Seek_wrapped(_First, _STD move(_UFirst)); - return {_STD move(_First), _STD move(_Func)}; + auto _UResult = _For_each_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _STD move(_Func), _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult.in)); + return {_STD move(_First), _STD move(_UResult.fun)}; } template , _Pj>> _Fn> constexpr for_each_result, _Fn> operator()( _Rng&& _Range, _Fn _Func, _Pj _Proj = {}) const { - return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _STD move(_Func), _Pass_fn(_Proj)); + auto _First = _RANGES begin(_Range); + + auto _UResult = _For_each_unchecked( + _Get_unwrapped(_STD move(_First)), _Uend(_Range), _STD move(_Func), _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult.in)); + return {_STD move(_First), _STD move(_UResult.fun)}; + } + + private: + template + _NODISCARD static constexpr for_each_result<_It, _Fn> _For_each_unchecked( + _It _First, const _Se _Last, _Fn _Func, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_unary_invocable<_Fn, projected<_It, _Pj>>); + + for (; _First != _Last; ++_First) { + _STD invoke(_Func, _STD invoke(_Proj, *_First)); + } + + return {_STD move(_First), _STD move(_Func)}; } }; @@ -520,22 +559,40 @@ namespace ranges { indirect_unary_predicate> _Pr> _NODISCARD constexpr _It operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { _Adl_verify_range(_First, _Last); - auto _UFirst = _Get_unwrapped(_STD move(_First)); - const auto _ULast = _Get_unwrapped(_STD move(_Last)); - for (; _UFirst != _ULast; ++_UFirst) { - if (!_STD invoke(_Pred, _STD invoke(_Proj, *_UFirst))) { - break; - } - } - _Seek_wrapped(_First, _STD move(_UFirst)); + auto _UResult = _Find_if_not_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult)); return _First; } template , _Pj>> _Pr> _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { - return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + auto _First = _RANGES begin(_Range); + + auto _UResult = _Find_if_not_unchecked( + _Get_unwrapped(_STD move(_First)), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult)); + return _First; + } + + private: + template + _NODISCARD static constexpr _It _Find_if_not_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>); + + for (; _First != _Last; ++_First) { + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + break; + } + } + + return _First; } }; @@ -589,34 +646,46 @@ namespace ranges { template _Se, class _Pj = identity, indirect_binary_predicate, projected<_It, _Pj>> _Pr = ranges::equal_to> _NODISCARD constexpr _It operator()(_It _First, const _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { - // find first satisfying _Pred with successor _Adl_verify_range(_First, _Last); - auto _UFirst = _Get_unwrapped(_First); - const auto _ULast = _Get_unwrapped(_Last); - if (_UFirst == _ULast) { + auto _UResult = _Adjacent_find_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult)); + return _First; + } + + template , _Pj>, projected, _Pj>> _Pr = + ranges::equal_to> + _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + auto _UResult = _Adjacent_find_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + return _Rewrap_iterator(_Range, _STD move(_UResult)); + } + + private: + template + _NODISCARD static constexpr _It _Adjacent_find_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { + // find first satisfying _Pred with successor + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_binary_predicate<_Pr, projected<_It, _Pj>, projected<_It, _Pj>>); + + if (_First == _Last) { return _First; } - for (auto _UNext = _UFirst;; _UFirst = _UNext) { - if (++_UNext == _ULast) { - _Seek_wrapped(_First, _UNext); - return _First; + for (auto _Next = _First;; ++_First) { + if (++_Next == _Last) { + return _Next; } - if (_STD invoke(_Pred, _STD invoke(_Proj, *_UFirst), _STD invoke(_Proj, *_UNext))) { - _Seek_wrapped(_First, _UFirst); + if (_STD invoke(_Pred, _STD invoke(_Proj, *_First), _STD invoke(_Proj, *_Next))) { return _First; } } } - - template , _Pj>, projected, _Pj>> _Pr = - ranges::equal_to> - _NODISCARD constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { - return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); - } }; inline constexpr _Adjacent_find_fn adjacent_find{_Not_quite_object::_Construct_tag{}}; @@ -1417,21 +1486,31 @@ namespace ranges { indirect_unary_predicate> _Pr> _NODISCARD constexpr bool operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { _Adl_verify_range(_First, _Last); - auto _UFirst = _Get_unwrapped(_STD move(_First)); - const auto _ULast = _Get_unwrapped(_STD move(_Last)); - for (; _UFirst != _ULast; ++_UFirst) { - if (!_STD invoke(_Pred, _STD invoke(_Proj, *_UFirst))) { - return false; - } - } - return true; + return _All_of_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj)); } template , _Pj>> _Pr> _NODISCARD constexpr bool operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { - return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _All_of_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + } + + private: + template + _NODISCARD static constexpr bool _All_of_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>); + + for (; _First != _Last; ++_First) { + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + return false; + } + } + + return true; } }; @@ -1471,21 +1550,31 @@ namespace ranges { indirect_unary_predicate> _Pr> _NODISCARD constexpr bool operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { _Adl_verify_range(_First, _Last); - auto _UFirst = _Get_unwrapped(_STD move(_First)); - const auto _ULast = _Get_unwrapped(_STD move(_Last)); - for (; _UFirst != _ULast; ++_UFirst) { - if (_STD invoke(_Pred, _STD invoke(_Proj, *_UFirst))) { - return true; - } - } - return false; + return _Any_of_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj)); } template , _Pj>> _Pr> _NODISCARD constexpr bool operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { - return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Any_of_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + } + + private: + template + _NODISCARD static constexpr bool _Any_of_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>); + + for (; _First != _Last; ++_First) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + return true; + } + } + + return false; } }; @@ -1525,21 +1614,31 @@ namespace ranges { indirect_unary_predicate> _Pr> _NODISCARD constexpr bool operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { _Adl_verify_range(_First, _Last); - auto _UFirst = _Get_unwrapped(_STD move(_First)); - const auto _ULast = _Get_unwrapped(_STD move(_Last)); - for (; _UFirst != _ULast; ++_UFirst) { - if (_STD invoke(_Pred, _STD invoke(_Proj, *_UFirst))) { - return false; - } - } - return true; + return _None_of_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj)); } template , _Pj>> _Pr> _NODISCARD constexpr bool operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { - return (*this)(_RANGES begin(_Range), _RANGES end(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _None_of_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + } + + private: + template + _NODISCARD static constexpr bool _None_of_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>); + + for (; _First != _Last; ++_First) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + return false; + } + } + + return true; } }; @@ -1789,6 +1888,58 @@ namespace ranges { }; inline constexpr _Move_fn move{_Not_quite_object::_Construct_tag{}}; + + // ALIAS TEMPLATE move_backward_result + template + using move_backward_result = in_out_result<_In, _Out>; + + // VARIABLE ranges::move_backward + // clang-format off + // concept-constrained for strict enforcement as it is used by several algorithms + template + requires indirectly_movable<_It1, _It2> + _NODISCARD constexpr _It2 _Move_backward_common(const _It1 _First, _It1 _Last, _It2 _Result) { + if constexpr (_Ptr_move_cat<_It1, _It2>::_Trivially_copyable) { + if (!_STD is_constant_evaluated()) { + return _Copy_backward_memmove(_First, _Last, _Result); + } + } + + while (_First != _Last) { + *--_Result = _RANGES iter_move(--_Last); + } + + return _Result; + } + // clang-format on + + class _Move_backward_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se1, bidirectional_iterator _It2> + requires indirectly_movable<_It1, _It2> + constexpr move_backward_result<_It1, _It2> operator()(_It1 _First, _Se1 _Last, _It2 _Result) const { + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + auto _ULast = _Get_final_iterator_unwrapped<_It1>(_UFirst, _STD move(_Last)); + _Seek_wrapped(_First, _ULast); + _Result = _RANGES _Move_backward_common(_STD move(_UFirst), _STD move(_ULast), _STD move(_Result)); + return {_STD move(_First), _STD move(_Result)}; + } + + template + requires indirectly_movable, _It> + constexpr move_backward_result, _It> operator()(_Rng&& _Range, _It _Result) const { + auto _ULast = _Get_final_iterator_unwrapped(_Range); + _Result = _RANGES _Move_backward_common(_Ubegin(_Range), _ULast, _STD move(_Result)); + return {_Rewrap_iterator(_Range, _STD move(_ULast)), _STD move(_Result)}; + } + // clang-format on + }; + + inline constexpr _Move_backward_fn move_backward{_Not_quite_object::_Construct_tag{}}; } // namespace ranges #endif // __cpp_lib_concepts @@ -3184,24 +3335,16 @@ namespace ranges { template _Se1, forward_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 _It1 operator()(_It1 _First1, const _Se1 _Last1, const _It2 _First2, const _Se2 _Last2, - _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + _NODISCARD constexpr _It1 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); - auto _UFirst1 = _Get_unwrapped(_STD move(_First1)); - const auto _ULast1 = _Get_unwrapped(_Last1); - const auto _UFirst2 = _Get_unwrapped(_First2); - const auto _ULast2 = _Get_unwrapped(_Last2); - for (; _UFirst1 != _ULast1; ++_UFirst1) { - for (auto _UMid2 = _UFirst2; _UMid2 != _ULast2; ++_UMid2) { - if (_STD invoke(_Pred, _STD invoke(_Proj1, *_UFirst1), _STD invoke(_Proj2, *_UMid2))) { - _Seek_wrapped(_First1, _STD move(_UFirst1)); - return _First1; - } - } - } - _Seek_wrapped(_First1, _STD move(_UFirst1)); + auto _UResult = _Find_first_of_unchecked(_Get_unwrapped(_STD move(_First1)), + _Get_unwrapped(_STD move(_Last1)), _Get_unwrapped(_STD move(_First2)), + _Get_unwrapped(_STD move(_Last2)), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + + _Seek_wrapped(_First1, _STD move(_UResult)); return _First1; } @@ -3210,10 +3353,35 @@ namespace ranges { requires indirectly_comparable, iterator_t<_Rng2>, _Pr, _Pj1, _Pj2> _NODISCARD constexpr borrowed_iterator_t<_Rng1> operator()( _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { - return (*this)(_RANGES begin(_Range1), _RANGES end(_Range1), _RANGES begin(_Range2), _RANGES end(_Range2), - _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + auto _First1 = _RANGES begin(_Range1); + + auto _UResult = _Find_first_of_unchecked(_Get_unwrapped(_STD move(_First1)), _Uend(_Range1), + _Ubegin(_Range2), _Uend(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + + _Seek_wrapped(_First1, _STD move(_UResult)); + return _First1; } // clang-format on + private: + template + _NODISCARD static constexpr _It1 _Find_first_of_unchecked(_It1 _First1, const _Se1 _Last1, const _It2 _First2, + const _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>); + + for (; _First1 != _Last1; ++_First1) { + for (auto _Mid2 = _First2; _Mid2 != _Last2; ++_Mid2) { + if (_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_Mid2))) { + return _First1; + } + } + } + + return _First1; + } }; inline constexpr _Find_first_of_fn find_first_of{_Not_quite_object::_Construct_tag{}}; @@ -3234,16 +3402,13 @@ namespace ranges { _It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2) const { _Adl_verify_range(_First1, _Last1); _Adl_verify_range(_First2, _Last2); - auto _UFirst1 = _Get_unwrapped(_STD move(_First1)); - auto _ULast1 = _Get_unwrapped(_STD move(_Last1)); - auto _UFirst2 = _Get_unwrapped(_STD move(_First2)); - auto _ULast2 = _Get_unwrapped(_STD move(_Last2)); - for (; _UFirst1 != _ULast1 && _UFirst2 != _ULast2; ++_UFirst1, (void) ++_UFirst2) { - _RANGES iter_swap(_UFirst1, _UFirst2); - } - _Seek_wrapped(_First1, _STD move(_UFirst1)); - _Seek_wrapped(_First2, _STD move(_UFirst2)); + auto _UResult = + _Swap_ranges_unchecked(_Get_unwrapped(_STD move(_First1)), _Get_unwrapped(_STD move(_Last1)), + _Get_unwrapped(_STD move(_First2)), _Get_unwrapped(_STD move(_Last2))); + + _Seek_wrapped(_First1, _STD move(_UResult.in1)); + _Seek_wrapped(_First2, _STD move(_UResult.in2)); return {_STD move(_First1), _STD move(_First2)}; } @@ -3251,9 +3416,33 @@ namespace ranges { requires indirectly_swappable, iterator_t<_Rng2>> constexpr swap_ranges_result, borrowed_iterator_t<_Rng2>> operator()( _Rng1&& _Range1, _Rng2&& _Range2) const { - return (*this)(_RANGES begin(_Range1), _RANGES end(_Range1), _RANGES begin(_Range2), _RANGES end(_Range2)); + auto _First1 = _RANGES begin(_Range1); + auto _First2 = _RANGES begin(_Range2); + + auto _UResult = _Swap_ranges_unchecked( + _Get_unwrapped(_STD move(_First1)), _Uend(_Range1), _Get_unwrapped(_STD move(_First2)), _Uend(_Range2)); + + _Seek_wrapped(_First1, _STD move(_UResult.in1)); + _Seek_wrapped(_First2, _STD move(_UResult.in2)); + return {_STD move(_First1), _STD move(_First2)}; } // clang-format on + private: + template + _NODISCARD static constexpr swap_ranges_result<_It1, _It2> _Swap_ranges_unchecked( + _It1 _First1, const _Se1 _Last1, _It2 _First2, const _Se2 _Last2) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_swappable<_It1, _It2>); + + for (; _First1 != _Last1 && _First2 != _Last2; ++_First1, (void) ++_First2) { + _RANGES iter_swap(_First1, _First2); + } + + return {_STD move(_First1), _STD move(_First2)}; + } }; inline constexpr _Swap_ranges_fn swap_ranges{_Not_quite_object::_Construct_tag{}}; @@ -4334,18 +4523,84 @@ _NODISCARD _FwdIt unique(_ExPo&&, _FwdIt _First, _FwdIt _Last) noexcept /* termi } #endif // _HAS_CXX17 -// FUNCTION TEMPLATE unique_copy -#if _HAS_IF_CONSTEXPR -// clang-format off #ifdef __cpp_lib_concepts -template -concept _Can_reread_dest = forward_iterator<_OutIt> && same_as, iter_value_t<_OutIt>>; -#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv -template -_INLINE_VAR constexpr bool _Can_reread_dest = - _Is_fwd_iter_v<_OutIt> && is_same_v<_Iter_value_t<_InIt>, _Iter_value_t<_OutIt>>; -#endif // __cpp_lib_concepts -// clang-format on +namespace ranges { + // VARIABLE ranges::unique + class _Unique_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se, class _Pj = identity, + indirect_equivalence_relation> _Pr = ranges::equal_to> + _NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UResult = _Unique_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + return _Rewrap_subrange>(_First, _STD move(_UResult)); + } + + // clang-format off + template , _Pj>> _Pr = ranges::equal_to> + requires permutable> + _NODISCARD constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + auto _UResult = _Unique_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + return _Rewrap_subrange>(_Range, _STD move(_UResult)); + } + // clang-format on + private: + template + _NODISCARD static constexpr subrange<_It> _Unique_unchecked(_It _First, const _Se _Last, _Pr _Pred, _Pj _Proj) { + // Remove adjacent elements from [_First, _Last) whose projections satisfy _Pred + _STL_INTERNAL_STATIC_ASSERT(permutable<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_equivalence_relation<_Pr, projected<_It, _Pj>>); + + auto _Current = _First; + if (_First == _Last) { + return {_STD move(_Current), _STD move(_First)}; + } + + for (;; ++_Current) { + if (++_First == _Last) { + return {_STD move(_Current), _STD move(_First)}; + } + + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Current), _STD invoke(_Proj, *_First))) { + break; + } + } + + while (++_First != _Last) { + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_Current), _STD invoke(_Proj, *_First))) { + ++_Current; + *_Current = _RANGES iter_move(_First); + } + } + ++_Current; + + return {_STD move(_Current), _STD move(_First)}; + } + }; + + inline constexpr _Unique_fn unique{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + +// FUNCTION TEMPLATE unique_copy +#if _HAS_IF_CONSTEXPR +// clang-format off +#ifdef __cpp_lib_concepts +template +concept _Can_reread_dest = forward_iterator<_OutIt> && same_as, iter_value_t<_OutIt>>; +#else // ^^^ defined(__cpp_lib_concepts) / !defined(__cpp_lib_concepts) vvv +template +_INLINE_VAR constexpr bool _Can_reread_dest = + _Is_fwd_iter_v<_OutIt> && is_same_v<_Iter_value_t<_InIt>, _Iter_value_t<_OutIt>>; +#endif // __cpp_lib_concepts +// clang-format on template _CONSTEXPR20 _OutIt unique_copy(_InIt _First, _InIt _Last, _OutIt _Dest, _Pr _Pred) { @@ -4507,6 +4762,110 @@ _FwdIt2 unique_copy(_ExPo&&, _FwdIt1 _First, _FwdIt1 _Last, _FwdIt2 _Dest) noexc #ifdef __cpp_lib_concepts namespace ranges { + // ALIAS TEMPLATE unique_copy_result + template + using unique_copy_result = in_out_result<_In, _Out>; + + // VARIABLE ranges::unique_copy + // clang-format off + template + concept _Can_reread_or_store = forward_iterator<_It> + || (input_iterator<_Out> && same_as, iter_value_t<_Out>>) + || indirectly_copyable_storable<_It, _Out>; + // clang-format on + class _Unique_copy_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, weakly_incrementable _Out, class _Pj = identity, + indirect_equivalence_relation> _Pr = ranges::equal_to> + requires indirectly_copyable<_It, _Out> && _Can_reread_or_store<_It, _Out> + constexpr unique_copy_result<_It, _Out> operator()( + _It _First, _Se _Last, _Out _Result, _Pr _Pred = {}, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); + auto _UResult = _Unique_copy_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), + _Get_unwrapped_unverified(_STD move(_Result)), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult.in)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First), _STD move(_Result)}; + } + + template , _Pj>> _Pr = ranges::equal_to> + requires indirectly_copyable, _Out> && _Can_reread_or_store, _Out> + constexpr unique_copy_result, _Out> operator()( + _Rng&& _Range, _Out _Result, _Pr _Pred = {}, _Pj _Proj = {}) const { + auto _First = _RANGES begin(_Range); + auto _UResult = _Unique_copy_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range), + _Get_unwrapped_unverified(_STD move(_Result)), _Pass_fn(_Pred), _Pass_fn(_Proj)); + + _Seek_wrapped(_First, _STD move(_UResult.in)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First), _STD move(_Result)}; + } + // clang-format on + private: + template + _NODISCARD static constexpr unique_copy_result<_It, _Out> _Unique_copy_unchecked( + _It _First, const _Se _Last, _Out _Result, _Pr _Pred, _Pj _Proj) { + // Copy elements from [_First, _Last) to _Result, compressing adjacent elements whose projections satisfy + // _Pred + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); + _STL_INTERNAL_STATIC_ASSERT(indirect_equivalence_relation<_Pr, projected<_It, _Pj>>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_copyable<_It, _Out>); + _STL_INTERNAL_STATIC_ASSERT(_Can_reread_or_store<_It, _Out>); + + if (_First == _Last) { + return {_STD move(_First), _STD move(_Result)}; + } + + if constexpr (input_iterator<_Out> && same_as, iter_value_t<_Out>>) { + // Can reread _Result + *_Result = *_First; + + while (++_First != _Last) { + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_Result), _STD invoke(_Proj, *_First))) { + ++_Result; + *_Result = *_First; + } + } + } else if constexpr (forward_iterator<_It>) { + // Can reread _First + auto _Current = _First; + *_Result = *_First; + + while (++_First != _Last) { + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_Current), _STD invoke(_Proj, *_First))) { + _Current = _First; + ++_Result; + *_Result = *_First; + } + } + } else { + // Neither _First nor _Result can be reread, construct temporary + iter_value_t<_It> _Val = *_First; + + while (++_First != _Last) { + if (!_STD invoke(_Pred, _STD invoke(_Proj, _Val), _STD invoke(_Proj, *_First))) { + *_Result = _STD move(_Val); + ++_Result; + _Val = *_First; + } + } + *_Result = _STD move(_Val); + } + ++_Result; + + return {_STD move(_First), _STD move(_Result)}; + } + }; + + inline constexpr _Unique_copy_fn unique_copy{_Not_quite_object::_Construct_tag{}}; + // VARIABLE ranges::reverse // clang-format off // concept-constrained for strict enforcement as it is used by several algorithms @@ -4520,6 +4879,7 @@ namespace ranges { constexpr bool _Allow_vectorization = conjunction_v<_Is_trivially_swappable<_Elem>, negation>>; +#pragma warning(suppress : 6326) // Potential comparison of a constant with another constant if constexpr (_Allow_vectorization && _Nx <= 8 && (_Nx & (_Nx - 1)) == 0) { if (!_STD is_constant_evaluated()) { _Elem* const _First_addr = _STD to_address(_First); @@ -4558,7 +4918,7 @@ namespace ranges { auto _UFirst = _Get_unwrapped(_STD move(_First)); auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); _Seek_wrapped(_First, _ULast); - _RANGES _Reverse_common(_STD move(_UFirst), _STD move(_ULast)); + _Reverse_common(_STD move(_UFirst), _STD move(_ULast)); return _First; } @@ -4566,7 +4926,7 @@ namespace ranges { requires permutable> constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range) const { auto _ULast = _Get_final_iterator_unwrapped(_Range); - _RANGES _Reverse_common(_Ubegin(_Range), _ULast); + _Reverse_common(_Ubegin(_Range), _ULast); return _Rewrap_iterator(_Range, _STD move(_ULast)); } // clang-format on @@ -4584,6 +4944,37 @@ _CONSTEXPR20 _OutIt reverse_copy(_BidIt _First, _BidIt _Last, _OutIt _Dest) { const auto _UFirst = _Get_unwrapped(_First); auto _ULast = _Get_unwrapped(_Last); auto _UDest = _Get_unwrapped_n(_Dest, _Idl_distance<_BidIt>(_UFirst, _ULast)); + +#if _HAS_IF_CONSTEXPR && _USE_STD_VECTOR_ALGORITHMS + using _Elem = remove_pointer_t; + using _DestElem = remove_pointer_t; + constexpr bool _Allow_vectorization = conjunction_v, _DestElem>, + is_pointer, is_trivially_copyable<_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) { +#ifdef __cpp_lib_is_constant_evaluated + if (!_STD is_constant_evaluated()) +#endif // __cpp_lib_is_constant_evaluated + { + if constexpr (_Nx == 1) { + __std_reverse_copy_trivially_copyable_1(_UFirst, _ULast, _UDest); + } else if constexpr (_Nx == 2) { + __std_reverse_copy_trivially_copyable_2(_UFirst, _ULast, _UDest); + } else if constexpr (_Nx == 4) { + __std_reverse_copy_trivially_copyable_4(_UFirst, _ULast, _UDest); + } else { + __std_reverse_copy_trivially_copyable_8(_UFirst, _ULast, _UDest); + } + + _UDest += _ULast - _UFirst; + _Seek_wrapped(_Dest, _UDest); + return _Dest; + } + } +#endif // _HAS_IF_CONSTEXPR && _USE_STD_VECTOR_ALGORITHMS + for (; _UFirst != _ULast; ++_UDest) { *_UDest = *--_ULast; } @@ -4600,8 +4991,210 @@ _FwdIt reverse_copy(_ExPo&&, _BidIt _First, _BidIt _Last, _FwdIt _Dest) noexcept _REQUIRE_PARALLEL_ITERATOR(_FwdIt); return _STD reverse_copy(_First, _Last, _Dest); } + +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE reverse_copy_result + template + using reverse_copy_result = in_out_result<_In, _Out>; + + // VARIABLE ranges::reverse_copy + class _Reverse_copy_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, weakly_incrementable _Out> + requires indirectly_copyable<_It, _Out> + constexpr reverse_copy_result<_It, _Out> operator()(_It _First, _Se _Last, _Out _Result) const { + // clang-format on + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); + _Seek_wrapped(_First, _ULast); + _Result = _Reverse_copy_common(_STD move(_UFirst), _STD move(_ULast), _STD move(_Result)); + return {_STD move(_First), _STD move(_Result)}; + } + + // clang-format off + template + requires indirectly_copyable, _Out> + constexpr reverse_copy_result, _Out> operator()(_Rng&& _Range, _Out _Result) const { + // clang-format on + if constexpr (common_range<_Rng>) { + _Result = _Reverse_copy_common(_Ubegin(_Range), _Uend(_Range), _STD move(_Result)); + return {_RANGES end(_Range), _STD move(_Result)}; + } else { + auto _ULast = _Get_final_iterator_unwrapped(_Range); + _Result = _Reverse_copy_common(_Ubegin(_Range), _ULast, _STD move(_Result)); + return {_Rewrap_iterator(_Range, _STD move(_ULast)), _STD move(_Result)}; + } + } + + private: + template + _NODISCARD static constexpr _Out _Reverse_copy_common(const _It _First, _It _Last, _Out _Result) { + _STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_copyable<_It, _Out>); + +#if _USE_STD_VECTOR_ALGORITHMS + if constexpr (contiguous_iterator<_It> && contiguous_iterator<_Out>) { + using _Elem = remove_reference_t>; + using _DestElem = remove_reference_t>; + constexpr bool _Allow_vectorization = conjunction_v, _DestElem>, + is_trivially_copyable<_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) { + if (!_STD is_constant_evaluated()) { + _Elem* const _First_addr = _STD to_address(_First); + _Elem* const _Last_addr = _STD to_address(_Last); + _DestElem* const _Result_addr = _STD to_address(_Result); + if constexpr (_Nx == 1) { + __std_reverse_copy_trivially_copyable_1(_First_addr, _Last_addr, _Result_addr); + } else if constexpr (_Nx == 2) { + __std_reverse_copy_trivially_copyable_2(_First_addr, _Last_addr, _Result_addr); + } else if constexpr (_Nx == 4) { + __std_reverse_copy_trivially_copyable_4(_First_addr, _Last_addr, _Result_addr); + } else { + __std_reverse_copy_trivially_copyable_8(_First_addr, _Last_addr, _Result_addr); + } + + _Result += _Last - _First; + return _Result; + } + } + } +#endif // _USE_STD_VECTOR_ALGORITHMS + + for (; _First != _Last; ++_Result) { + *_Result = *--_Last; + } + + return _Result; + } + }; + + inline constexpr _Reverse_copy_fn reverse_copy{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts #endif // _HAS_CXX17 +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::rotate + class _Rotate_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se> + constexpr subrange<_It> operator()(_It _First, _It _Mid, _Se _Last) const { + _Adl_verify_range(_First, _Mid); + _Adl_verify_range(_Mid, _Last); + auto _UResult = _Rotate_unchecked( + _Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Mid)), _Get_unwrapped(_STD move(_Last))); + + return _Rewrap_subrange>(_First, _STD move(_UResult)); + } + + // clang-format off + template + requires permutable> + constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, iterator_t<_Rng> _Mid) const { + // clang-format on + _Adl_verify_range(_RANGES begin(_Range), _Mid); + _Adl_verify_range(_Mid, _RANGES end(_Range)); + auto _UResult = _Rotate_unchecked(_Ubegin(_Range), _Get_unwrapped(_STD move(_Mid)), _Uend(_Range)); + + return _Rewrap_subrange>(_Mid, _STD move(_UResult)); + } + + private: + template + _NODISCARD static constexpr subrange<_It> _Rotate_unchecked(_It _First, _It _Mid, _Se _Last) { + // Exchange the ranges [_First, _Mid) and [_Mid, _Last) + // that is, rotates [_First, _Last) left by distance(_First, _Mid) positions + _STL_INTERNAL_STATIC_ASSERT(permutable<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + + if (_First == _Mid) { + auto _Final = _Get_final_iterator_unwrapped<_It>(_Mid, _STD move(_Last)); + return {_Final, _Final}; + } + + if (_Mid == _Last) { + return {_STD move(_First), _STD move(_Mid)}; + } + + if constexpr (bidirectional_iterator<_It>) { + _Reverse_common(_First, _Mid); + auto _Final = _Get_final_iterator_unwrapped<_It>(_Mid, _STD move(_Last)); + _Reverse_common(_Mid, _Final); + + if constexpr (random_access_iterator<_It>) { + _Reverse_common(_First, _Final); + _First += _Final - _Mid; + + return {_STD move(_First), _STD move(_Final)}; + } else { + auto [_Mid_first, _Mid_last] = _Reverse_until_mid_unchecked(_STD move(_First), _Mid, _Final); + _Reverse_common(_Mid_first, _Mid_last); + + if (_Mid_first == _Mid) { + return {_STD move(_Mid_last), _STD move(_Final)}; + } else { + return {_STD move(_Mid_first), _STD move(_Final)}; + } + } + } else { + auto _Next = _Mid; + do { // rotate the first cycle + _RANGES iter_swap(_First, _Next); + ++_First; + ++_Next; + if (_First == _Mid) { + _Mid = _Next; + } + } while (_Next != _Last); + + auto _Begin = _First; + + while (_Mid != _Last) { // rotate subsequent cycles + _Next = _Mid; + do { + _RANGES iter_swap(_First, _Next); + ++_First; + ++_Next; + if (_First == _Mid) { + _Mid = _Next; + } + } while (_Next != _Last); + } + return {_STD move(_Begin), _STD move(_Mid)}; + } + } + + template + _NODISCARD static constexpr subrange<_It> _Reverse_until_mid_unchecked(_It _First, const _It _Mid, _It _Last) { + // reverse until either _First or _Last hits _Mid + _STL_INTERNAL_STATIC_ASSERT(permutable<_It>); + _STL_INTERNAL_CHECK(_First != _Mid); + _STL_INTERNAL_CHECK(_Mid != _Last); + + do { + _RANGES iter_swap(_First, --_Last); + } while (++_First != _Mid && _Last != _Mid); + + return {_STD move(_First), _STD move(_Last)}; + } + }; + + inline constexpr _Rotate_fn rotate{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE rotate_copy template _CONSTEXPR20 _OutIt rotate_copy(_FwdIt _First, _FwdIt _Mid, _FwdIt _Last, _OutIt _Dest) { @@ -4626,6 +5219,65 @@ _FwdIt2 rotate_copy(_ExPo&&, _FwdIt1 _First, _FwdIt1 _Mid, _FwdIt1 _Last, _FwdIt return _STD rotate_copy(_First, _Mid, _Last, _Dest); } +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE rotate_copy_result + template + using rotate_copy_result = in_out_result<_In, _Out>; + + // VARIABLE ranges::rotate_copy + class _Rotate_copy_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, weakly_incrementable _Out> + requires indirectly_copyable<_It, _Out> + constexpr rotate_copy_result<_It, _Out> operator()(_It _First, _It _Mid, _Se _Last, _Out _Result) const { + // clang-format on + _Adl_verify_range(_First, _Mid); + _Adl_verify_range(_Mid, _Last); + auto _UResult = _Rotate_copy_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Mid)), + _Get_unwrapped(_STD move(_Last)), _STD move(_Result)); + + _Seek_wrapped(_First, _STD move(_UResult.in)); + return {_STD move(_First), _STD move(_UResult.out)}; + } + + // clang-format off + template + requires indirectly_copyable, _Out> + constexpr rotate_copy_result, _Out> operator()( + _Rng&& _Range, iterator_t<_Rng> _Mid, _Out _Result) const { + // clang-format on + _Adl_verify_range(_RANGES begin(_Range), _Mid); + _Adl_verify_range(_Mid, _RANGES end(_Range)); + auto _UResult = _Rotate_copy_unchecked( + _Ubegin(_Range), _Get_unwrapped(_STD move(_Mid)), _Uend(_Range), _STD move(_Result)); + + return {_Rewrap_iterator(_Range, _STD move(_UResult.in)), _STD move(_UResult.out)}; + } + + private: + template + _NODISCARD static constexpr rotate_copy_result<_It, _Out> _Rotate_copy_unchecked( + _It _First, _It _Mid, _Se _Last, _Out _Result) { + // Copy the content of [_Mid, _Last) and [_First, _Mid) to _Result + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_copyable<_It, _Out>); + + auto _UResult1 = _RANGES _Copy_unchecked(_Mid, _STD move(_Last), _STD move(_Result)); + auto _UResult2 = _RANGES _Copy_unchecked(_STD move(_First), _STD move(_Mid), _STD move(_UResult1.out)); + return {_STD move(_UResult1.in), _STD move(_UResult2.out)}; + } + }; + + inline constexpr _Rotate_copy_fn rotate_copy{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE sample template _SampleIt _Sample_reservoir_unchecked( @@ -4634,12 +5286,12 @@ _SampleIt _Sample_reservoir_unchecked( // pre: _SampleIt is random-access && 0 < _Count && the range [_Dest, _Dest + _Count) is valid using _Diff_sample = _Iter_diff_t<_SampleIt>; const auto _SCount = static_cast<_Diff_sample>(_Count); - _Iter_diff_t<_PopIt> _PopSize{}; - for (; _PopSize < _SCount; ++_PopSize, (void) ++_First) { - // _PopSize is less than _SCount, and [_Dest, _Dest + _SCount) is valid, - // so [_Dest, _Dest + _PopSize) must be valid, so narrowing to _Diff_sample + _Iter_diff_t<_PopIt> _Pop_size{}; + for (; _Pop_size < _SCount; ++_Pop_size, (void) ++_First) { + // _Pop_size is less than _SCount, and [_Dest, _Dest + _SCount) is valid, + // so [_Dest, _Dest + _Pop_size) must be valid, so narrowing to _Diff_sample // can't overflow - const auto _Sample_pop = static_cast<_Diff_sample>(_PopSize); + const auto _Sample_pop = static_cast<_Diff_sample>(_Pop_size); if (_First == _Last) { return _Dest + _Sample_pop; } @@ -4647,7 +5299,7 @@ _SampleIt _Sample_reservoir_unchecked( *(_Dest + _Sample_pop) = *_First; } for (; _First != _Last; ++_First) { - const auto _Idx = _RngFunc(++_PopSize); + const auto _Idx = _RngFunc(++_Pop_size); if (_Idx < _SCount) { *(_Dest + static_cast<_Diff_sample>(_Idx)) = *_First; // again, valid narrowing because _Idx < _SCount } @@ -4657,12 +5309,12 @@ _SampleIt _Sample_reservoir_unchecked( template _SampleIt _Sample_selection_unchecked( - _PopIt _First, const _PopIt _Last, _Iter_diff_t<_PopIt> _PopSize, _SampleIt _Dest, _Diff _Count, _RngFn& _RngFunc) { + _PopIt _First, _Iter_diff_t<_PopIt> _Pop_size, _SampleIt _Dest, _Diff _Count, _RngFn& _RngFunc) { // source is forward *and* we know the source range size: use selection sampling (stable) - // pre: _PopIt is forward && _Count <= _PopSize + // pre: _PopIt is forward && _Count <= _Pop_size using _CT = common_type_t<_Iter_diff_t<_PopIt>, _Diff>; - for (; _Count > 0 && _First != _Last; ++_First, (void) --_PopSize) { - if (static_cast<_CT>(_RngFunc(_PopSize)) < static_cast<_CT>(_Count)) { + for (; _Pop_size > 0; ++_First, (void) --_Pop_size) { + if (static_cast<_CT>(_RngFunc(_Pop_size)) < static_cast<_CT>(_Count)) { --_Count; *_Dest = *_First; ++_Dest; @@ -4684,15 +5336,15 @@ template _SampleIt _Sample1(_PopIt _First, _PopIt _Last, _SampleIt _Dest, _Diff _Count, _RngFn& _RngFunc, forward_iterator_tag) { // source is forward: use selection sampling (stable) // pre: _Count > 0 - using _PopDiff = _Iter_diff_t<_PopIt>; - using _CT = common_type_t<_Diff, _PopDiff>; - const auto _PopSize = _STD distance(_First, _Last); - if (static_cast<_CT>(_Count) > static_cast<_CT>(_PopSize)) { - _Count = static_cast<_Diff>(_PopSize); // narrowing OK because _Count is getting smaller + using _PopDiff = _Iter_diff_t<_PopIt>; + using _CT = common_type_t<_Diff, _PopDiff>; + const auto _Pop_size = _STD distance(_First, _Last); + if (static_cast<_CT>(_Count) > static_cast<_CT>(_Pop_size)) { + _Count = static_cast<_Diff>(_Pop_size); // narrowing OK because _Count is getting smaller } _Seek_wrapped( - _Dest, _Sample_selection_unchecked(_First, _Last, _PopSize, _Get_unwrapped_n(_Dest, _Count), _Count, _RngFunc)); + _Dest, _Sample_selection_unchecked(_First, _Pop_size, _Get_unwrapped_n(_Dest, _Count), _Count, _RngFunc)); return _Dest; } @@ -4710,6 +5362,128 @@ _SampleIt sample(_PopIt _First, _PopIt _Last, _SampleIt _Dest, _Diff _Count, return _Dest; } + +#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)(); +}; +// clang-format on + +namespace ranges { + // VARIABLE ranges::sample + class _Sample_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, weakly_incrementable _Out, class _Urng> + requires (forward_iterator<_It> || random_access_iterator<_Out>) + && indirectly_copyable<_It, _Out> && uniform_random_bit_generator> + _Out operator()(_It _First, _Se _Last, _Out _Result, iter_difference_t<_It> _Count, _Urng&& _Func) const { + _Adl_verify_range(_First, _Last); + if (_Count <= 0) { + return _Result; + } + + _Rng_from_urng, remove_reference_t<_Urng>> _RngFunc(_Func); + if constexpr (forward_iterator<_It>) { + auto _UFirst = _Get_unwrapped(_STD move(_First)); + auto _Pop_size = _RANGES distance(_UFirst, _Get_unwrapped(_STD move(_Last))); + return _Sample_selection_unchecked(_STD move(_UFirst), _Pop_size, _STD move(_Result), _Count, _RngFunc); + } else { + return _Sample_reservoir_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), + _STD move(_Result), _Count, _RngFunc); + } + } + + template + requires (forward_range<_Rng> || random_access_iterator<_Out>) + && indirectly_copyable, _Out> + && uniform_random_bit_generator> + _Out operator()(_Rng&& _Range, _Out _Result, range_difference_t<_Rng> _Count, _Urng&& _Func) const { + if (_Count <= 0) { + return _Result; + } + + _Rng_from_urng, remove_reference_t<_Urng>> _RngFunc(_Func); + if constexpr (forward_range<_Rng>) { + auto _UFirst = _Ubegin(_Range); + auto _Pop_size = _RANGES distance(_UFirst, _Uend(_Range)); + return _Sample_selection_unchecked(_STD move(_UFirst), _Pop_size, _STD move(_Result), _Count, _RngFunc); + } else { + return _Sample_reservoir_unchecked( + _Ubegin(_Range), _Uend(_Range), _STD move(_Result), _Count, _RngFunc); + } + } + // clang-format on + private: + template + _NODISCARD static _Out _Sample_selection_unchecked( + _It _First, iter_difference_t<_It> _Pop_size, _Out _Result, iter_difference_t<_It> _Count, _Rng& _RngFunc) { + // randomly select _Count elements from [_First, _First + _Pop_size) into _Result + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_copyable<_It, _Out>); + + if (_Count > _Pop_size) { + _Count = _Pop_size; + } + + for (; _Pop_size > 0; ++_First, (void) --_Pop_size) { + if (_RngFunc(_Pop_size) < _Count) { + *_Result = *_First; + ++_Result; + if (--_Count == 0) { + break; + } + } + } + + return _Result; + } + + template + _NODISCARD static _Out _Sample_reservoir_unchecked( + _It _First, const _Se _Last, _Out _Result, const iter_difference_t<_It> _Count, _Rng& _RngFunc) { + // randomly select _Count elements from [_First, _Last) into _Result + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_Out>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_copyable<_It, _Out>); + + iter_difference_t<_It> _Pop_size{}; + for (; _Pop_size < _Count; ++_Pop_size, (void) ++_First) { + if (_First == _Last) { + return _Result + _Pop_size; + } + + *(_Result + _Pop_size) = *_First; + } + for (; _First != _Last; ++_First) { + const auto _Idx = _RngFunc(++_Pop_size); + if (_Idx < _Count) { + *(_Result + _Idx) = *_First; + } + } + + return _Result + _Count; + } + }; + + inline constexpr _Sample_fn sample{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts #endif // _HAS_CXX17 // FUNCTION TEMPLATE shuffle WITH URNG @@ -4742,6 +5516,66 @@ void shuffle(_RanIt _First, _RanIt _Last, _Urng&& _Func) { // shuffle [_First, _ _Random_shuffle1(_First, _Last, _RngFunc); } +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::shuffle + class _Shuffle_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Urng> + requires permutable<_It> && uniform_random_bit_generator> + _It operator()(_It _First, _Se _Last, _Urng&& _Func) const { + _Adl_verify_range(_First, _Last); + + _Rng_from_urng, remove_reference_t<_Urng>> _RngFunc(_Func); + auto _UResult = + _Shuffle_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _RngFunc); + + _Seek_wrapped(_First, _STD move(_UResult)); + return _First; + } + + template + requires permutable> && uniform_random_bit_generator> + borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Urng&& _Func) const { + _Rng_from_urng, remove_reference_t<_Urng>> _RngFunc(_Func); + + return _Rewrap_iterator(_Range, _Shuffle_unchecked(_Ubegin(_Range), _Uend(_Range), _RngFunc)); + } + // clang-format on + private: + template + _NODISCARD static _It _Shuffle_unchecked(_It _First, const _Se _Last, _Rng& _Func) { + // shuffle [_First, _Last) using random function _Func + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(permutable<_It>); + + if (_First == _Last) { + return _First; + } + using _Diff = iter_difference_t<_It>; + + auto _Target = _First; + _Diff _Target_index = 1; + for (; ++_Target != _Last; ++_Target_index) { + // randomly place an element from [_First, _Target] at _Target + const _Diff _Off = _Func(_Target_index + 1); + _STL_ASSERT(0 <= _Off && _Off <= _Target_index, "random value out of range"); + if (_Off != _Target_index) { // avoid self-move-assignment + _RANGES iter_swap(_Target, _First + _Off); + } + } + return _Target; + } + }; + + inline constexpr _Shuffle_fn shuffle{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + #if _HAS_AUTO_PTR_ETC // FUNCTION TEMPLATE random_shuffle WITH RANDOM FN template @@ -5294,7 +6128,7 @@ _CONSTEXPR20 void _Push_heap_by_index( _Hole = _Idx; } - *(_First + _Hole) = _STD move(_Val); // drop _Val into final hole + *(_First + _Hole) = _STD forward<_Ty>(_Val); // drop _Val into final hole } template @@ -5320,16 +6154,17 @@ _CONSTEXPR20 void push_heap(_RanIt _First, _RanIt _Last) { #ifdef __cpp_lib_concepts namespace ranges { // VARIABLE ranges::push_heap - template + // clang-format off + template + requires sortable<_It, _Pr, _Pj1> && indirectly_writable<_It, _Ty> + && indirect_strict_weak_order<_Pr, projected<_It, _Pj1>, projected*, _Pj2>> constexpr void _Push_heap_by_index(const _It _First, iter_difference_t<_It> _Hole, - const iter_difference_t<_It> _Top, iter_value_t<_It>&& _Val, _Pr _Pred, _Pj _Proj) { + const iter_difference_t<_It> _Top, _Ty&& _Val, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + // clang-format on // percolate _Hole to _Top or where _Val belongs - _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); - _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); - while (_Top < _Hole) { const auto _Idx = static_cast>((_Hole - 1) >> 1); // shift for codegen - if (!_STD invoke(_Pred, _STD invoke(_Proj, *(_First + _Idx)), _STD invoke(_Proj, _Val))) { + if (!_STD invoke(_Pred, _STD invoke(_Proj1, *(_First + _Idx)), _STD invoke(_Proj2, _Val))) { break; } @@ -5338,7 +6173,7 @@ namespace ranges { _Hole = _Idx; } - *(_First + _Hole) = _STD move(_Val); // drop _Val into final hole + *(_First + _Hole) = _STD forward<_Ty>(_Val); // drop _Val into final hole } class _Push_heap_fn : private _Not_quite_object { @@ -5383,7 +6218,8 @@ namespace ranges { --_Last; iter_value_t<_It> _Val = _RANGES iter_move(_Last); - _RANGES _Push_heap_by_index(_STD move(_First), _Count - 1, 0, _STD move(_Val), _Pred, _Proj); + // NB: if _Proj is a _Ref_fn, this aliases the _Proj1 and _Proj2 parameters of _Push_heap_by_index + _RANGES _Push_heap_by_index(_STD move(_First), _Count - 1, 0, _STD move(_Val), _Pred, _Proj, _Proj); } }; @@ -5419,7 +6255,7 @@ _CONSTEXPR20 void _Pop_heap_hole_by_index( _Hole = _Bottom - 1; } - _Push_heap_by_index(_First, _Hole, _Top, _STD move(_Val), _Pred); + _Push_heap_by_index(_First, _Hole, _Top, _STD forward<_Ty>(_Val), _Pred); } template @@ -5429,7 +6265,8 @@ _CONSTEXPR20 void _Pop_heap_hole_unchecked(_RanIt _First, _RanIt _Last, _RanIt _ // precondition: _First != _Dest *_Dest = _STD move(*_First); using _Diff = _Iter_diff_t<_RanIt>; - _Pop_heap_hole_by_index(_First, static_cast<_Diff>(0), static_cast<_Diff>(_Last - _First), _STD move(_Val), _Pred); + _Pop_heap_hole_by_index( + _First, static_cast<_Diff>(0), static_cast<_Diff>(_Last - _First), _STD forward<_Ty>(_Val), _Pred); } template @@ -5458,12 +6295,14 @@ _CONSTEXPR20 void pop_heap(_RanIt _First, _RanIt _Last) { #ifdef __cpp_lib_concepts namespace ranges { // VARIABLE ranges::pop_heap - template + // clang-format off + template + requires sortable<_It, _Pr, _Pj1> && indirectly_writable<_It, _Ty> + && indirect_strict_weak_order<_Pr, projected<_It, _Pj1>, projected*, _Pj2>> constexpr void _Pop_heap_hole_by_index(_It _First, iter_difference_t<_It> _Hole, - const iter_difference_t<_It> _Bottom, iter_value_t<_It>&& _Val, _Pr _Pred, _Pj _Proj) { + const iter_difference_t<_It> _Bottom, _Ty&& _Val, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + // clang-format on // percolate _Hole to _Bottom, then push _Val - _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); - _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); _STL_INTERNAL_CHECK(_Hole >= 0); _STL_INTERNAL_CHECK(_Bottom > 0); @@ -5477,7 +6316,7 @@ namespace ranges { while (_Idx < _Max_sequence_non_leaf) { // move _Hole down to larger child _Idx = 2 * _Idx + 2; auto _Mid = _First + _Idx; - if (_STD invoke(_Pred, _STD invoke(_Proj, *_Mid), _STD invoke(_Proj, *_RANGES prev(_Mid)))) { + if (_STD invoke(_Pred, _STD invoke(_Proj1, *_Mid), _STD invoke(_Proj1, *_RANGES prev(_Mid)))) { --_Idx; --_Mid; } @@ -5490,36 +6329,39 @@ namespace ranges { _Hole = _Bottom - 1; } - _RANGES _Push_heap_by_index(_STD move(_First), _Hole, _Top, _STD move(_Val), _Pred, _Proj); + _RANGES _Push_heap_by_index(_STD move(_First), _Hole, _Top, _STD forward<_Ty>(_Val), _Pred, _Proj1, _Proj2); } - template + // clang-format off + template + requires sortable<_It, _Pr, _Pj1> && indirectly_writable<_It, _Ty> + && indirect_strict_weak_order<_Pr, projected<_It, _Pj1>, projected*, _Pj2>> constexpr void _Pop_heap_hole_unchecked( - _It _First, const _It _Last, const _It _Dest, iter_value_t<_It>&& _Val, _Pr _Pred, _Pj _Proj) { + _It _First, const _It _Last, const _It _Dest, _Ty&& _Val, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + // clang-format on // pop *_First to *_Dest and reheap - _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); - _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); _STL_INTERNAL_CHECK(_First != _Last); _STL_INTERNAL_CHECK(_First != _Dest); *_Dest = _RANGES iter_move(_First); const auto _Count = _Last - _First; - _RANGES _Pop_heap_hole_by_index(_STD move(_First), 0, _Count, _STD move(_Val), _Pred, _Proj); + _RANGES _Pop_heap_hole_by_index(_STD move(_First), 0, _Count, _STD forward<_Ty>(_Val), _Pred, _Proj1, _Proj2); } - template + // clang-format off + template + requires sortable<_It, _Pr, _Pj> constexpr void _Pop_heap_unchecked(_It _First, _It _Last, _Pr _Pred, _Pj _Proj) { + // clang-format on // pop *_First to *(_Last - 1) and reheap - _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); - _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); - if (_Last - _First < 2) { return; } --_Last; iter_value_t<_It> _Val = _RANGES iter_move(_Last); - _RANGES _Pop_heap_hole_unchecked(_STD move(_First), _Last, _Last, _STD move(_Val), _Pred, _Proj); + // NB: if _Proj is a _Ref_fn, this aliases the _Proj1 and _Proj2 parameters of _Pop_heap_hole_unchecked + _RANGES _Pop_heap_hole_unchecked(_STD move(_First), _Last, _Last, _STD move(_Val), _Pred, _Proj, _Proj); } class _Pop_heap_fn : private _Not_quite_object { @@ -5585,6 +6427,23 @@ _CONSTEXPR20 void make_heap(_RanIt _First, _RanIt _Last) { // make [_First, _Las #ifdef __cpp_lib_concepts namespace ranges { // VARIABLE ranges::make_heap + // clang-format off + template + requires sortable<_It, _Pr, _Pj> + constexpr void _Make_heap_common(_It _First, _It _Last, _Pr _Pred, _Pj _Proj) { + // make nontrivial [_First, _Last) into a heap with respect to _Pred and _Proj + // clang-format on + using _Diff = iter_difference_t<_It>; + const _Diff _Bottom = _Last - _First; + for (_Diff _Hole = _Bottom >> 1; _Hole > 0;) { // shift for codegen + // reheap top half, bottom to top + --_Hole; + iter_value_t<_It> _Val = _RANGES iter_move(_First + _Hole); + // NB: if _Proj is a _Ref_fn, this aliases the _Proj1 and _Proj2 parameters of _Pop_heap_hole_by_index + _RANGES _Pop_heap_hole_by_index(_First, _Hole, _Bottom, _STD move(_Val), _Pred, _Proj, _Proj); + } + } + class _Make_heap_fn : private _Not_quite_object { public: using _Not_quite_object::_Not_quite_object; @@ -5597,7 +6456,7 @@ namespace ranges { auto _UFirst = _Get_unwrapped(_STD move(_First)); auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); _Seek_wrapped(_First, _ULast); - _Make_heap_unchecked(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); + _Make_heap_common(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); return _First; } @@ -5605,31 +6464,15 @@ namespace ranges { requires sortable, _Pr, _Pj> constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { if constexpr (common_range<_Rng>) { - _Make_heap_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + _Make_heap_common(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); return _RANGES end(_Range); } else { auto _ULast = _Get_final_iterator_unwrapped(_Range); - _Make_heap_unchecked(_Ubegin(_Range), _ULast, _Pass_fn(_Pred), _Pass_fn(_Proj)); + _Make_heap_common(_Ubegin(_Range), _ULast, _Pass_fn(_Pred), _Pass_fn(_Proj)); return _Rewrap_iterator(_Range, _STD move(_ULast)); } } // clang-format on - private: - template - static constexpr void _Make_heap_unchecked(_It _First, _It _Last, _Pr _Pred, _Pj _Proj) { - // make nontrivial [_First, _Last) into a heap - _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); - _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); - - using _Diff = iter_difference_t<_It>; - const _Diff _Bottom = _Last - _First; - for (_Diff _Hole = _Bottom >> 1; _Hole > 0;) { // shift for codegen - // reheap top half, bottom to top - --_Hole; - iter_value_t<_It> _Val = _RANGES iter_move(_First + _Hole); - _RANGES _Pop_heap_hole_by_index(_First, _Hole, _Bottom, _STD move(_Val), _Pred, _Proj); - } - } }; inline constexpr _Make_heap_fn make_heap{_Not_quite_object::_Construct_tag{}}; @@ -5818,6 +6661,17 @@ _CONSTEXPR20 void sort_heap(_RanIt _First, _RanIt _Last) { // order heap by repe #ifdef __cpp_lib_concepts namespace ranges { // VARIABLE ranges::sort_heap + // clang-format off + template + requires sortable<_It, _Pr, _Pj> + constexpr void _Sort_heap_common(const _It _First, _It _Last, _Pr _Pred, _Pj _Proj) { + // order heap by repeatedly popping + // clang-format on + for (; _Last - _First >= 2; --_Last) { + _RANGES _Pop_heap_unchecked(_First, _Last, _Pred, _Proj); + } + } + class _Sort_heap_fn : private _Not_quite_object { public: using _Not_quite_object::_Not_quite_object; @@ -5830,7 +6684,7 @@ namespace ranges { auto _UFirst = _Get_unwrapped(_STD move(_First)); auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); _Seek_wrapped(_First, _ULast); - _Sort_heap_unchecked(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); + _Sort_heap_common(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); return _First; } @@ -5838,26 +6692,15 @@ namespace ranges { requires sortable, _Pr, _Pj> constexpr borrowed_iterator_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { if constexpr (common_range<_Rng>) { - _Sort_heap_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + _Sort_heap_common(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); return _RANGES end(_Range); } else { auto _ULast = _Get_final_iterator_unwrapped(_Range); - _Sort_heap_unchecked(_Ubegin(_Range), _ULast, _Pass_fn(_Pred), _Pass_fn(_Proj)); + _Sort_heap_common(_Ubegin(_Range), _ULast, _Pass_fn(_Pred), _Pass_fn(_Proj)); return _Rewrap_iterator(_Range, _STD move(_ULast)); } } // clang-format on - private: - template - static constexpr void _Sort_heap_unchecked(const _It _First, _It _Last, _Pr _Pred, _Pj _Proj) { - // order heap by repeatedly popping - _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); - _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); - - for (; _Last - _First >= 2; --_Last) { - _RANGES _Pop_heap_unchecked(_First, _Last, _Pred, _Proj); - } - } }; inline constexpr _Sort_heap_fn sort_heap{_Not_quite_object::_Construct_tag{}}; @@ -6255,6 +7098,90 @@ _FwdIt3 merge(_ExPo&&, _FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _FwdIt2 _REQUIRE_PARALLEL_ITERATOR(_FwdIt3); return _STD merge(_First1, _Last1, _First2, _Last2, _Dest); } + +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE merge_result + template + using merge_result = in_in_out_result<_In1, _In2, _Out>; + + // VARIABLE ranges::merge + class _Merge_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se1, input_iterator _It2, sentinel_for<_It2> _Se2, + weakly_incrementable _Out, class _Pr = ranges::less, class _Pj1 = identity, class _Pj2 = identity> + requires mergeable<_It1, _It2, _Out, _Pr, _Pj1, _Pj2> + constexpr merge_result<_It1, _It2, _Out> operator()(_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, + _Out _Result, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + // clang-format on + _Adl_verify_range(_First1, _Last1); + _Adl_verify_range(_First2, _Last2); + auto _UResult = _Merge_unchecked(_Get_unwrapped(_STD move(_First1)), _Get_unwrapped(_STD move(_Last1)), + _Get_unwrapped(_STD move(_First2)), _Get_unwrapped(_STD move(_Last2)), _STD move(_Result), + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_UResult.in1)); + _Seek_wrapped(_First2, _STD move(_UResult.in2)); + return {_STD move(_First1), _STD move(_First2), _STD move(_UResult.out)}; + } + + // clang-format off + template + requires mergeable, iterator_t<_Rng2>, _Out, _Pr, _Pj1, _Pj2> + constexpr merge_result, borrowed_iterator_t<_Rng2>, _Out> operator()( + _Rng1&& _Range1, _Rng2&& _Range2, _Out _Result, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + // clang-format on + auto _First1 = _RANGES begin(_Range1); + auto _First2 = _RANGES begin(_Range2); + auto _UResult = + _Merge_unchecked(_Get_unwrapped(_STD move(_First1)), _Uend(_Range1), _Get_unwrapped(_STD move(_First2)), + _Uend(_Range2), _STD move(_Result), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_UResult.in1)); + _Seek_wrapped(_First2, _STD move(_UResult.in2)); + return {_STD move(_First1), _STD move(_First2), _STD move(_UResult.out)}; + } + + private: + template + _NODISCARD static constexpr merge_result<_It1, _It2, _Out> _Merge_unchecked(_It1 _First1, const _Se1 _Last1, + _It2 _First2, const _Se2 _Last2, _Out _Result, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); + _STL_INTERNAL_STATIC_ASSERT(mergeable<_It1, _It2, _Out, _Pr, _Pj1, _Pj2>); + + for (;; ++_Result) { + if (_First1 == _Last1) { + auto _Copy_result = + _RANGES _Copy_unchecked(_STD move(_First2), _STD move(_Last2), _STD move(_Result)); + return {_STD move(_First1), _STD move(_Copy_result.in), _STD move(_Copy_result.out)}; + } + + if (_First2 == _Last2) { + auto _Copy_result = + _RANGES _Copy_unchecked(_STD move(_First1), _STD move(_Last1), _STD move(_Result)); + return {_STD move(_Copy_result.in), _STD move(_First2), _STD move(_Copy_result.out)}; + } + + if (_STD invoke(_Pred, _STD invoke(_Proj2, *_First2), _STD invoke(_Proj1, *_First1))) { + *_Result = *_First2; + ++_First2; + } else { + *_Result = *_First1; + ++_First1; + } + } + } + }; + + inline constexpr _Merge_fn merge{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts #endif // _HAS_CXX17 // FUNCTION TEMPLATE inplace_merge @@ -6523,22 +7450,22 @@ void inplace_merge(_ExPo&&, _BidIt _First, _BidIt _Mid, _BidIt _Last) noexcept / // FUNCTION TEMPLATE sort template -_CONSTEXPR20 _BidIt _Insertion_sort_unchecked(_BidIt _First, const _BidIt _Last, _Pr _Pred) { +_CONSTEXPR20 _BidIt _Insertion_sort_unchecked(const _BidIt _First, const _BidIt _Last, _Pr _Pred) { // insertion sort [_First, _Last) if (_First != _Last) { - for (_BidIt _Next = _First; ++_Next != _Last;) { // order next element - _BidIt _Next1 = _Next; - _Iter_value_t<_BidIt> _Val = _STD move(*_Next); + for (_BidIt _Mid = _First; ++_Mid != _Last;) { // order next element + _BidIt _Hole = _Mid; + _Iter_value_t<_BidIt> _Val = _STD move(*_Mid); if (_DEBUG_LT_PRED(_Pred, _Val, *_First)) { // found new earliest element, move to front - _Move_backward_unchecked(_First, _Next, ++_Next1); + _Move_backward_unchecked(_First, _Mid, ++_Hole); *_First = _STD move(_Val); } else { // look for insertion point after first - for (_BidIt _First1 = _Next1; _DEBUG_LT_PRED(_Pred, _Val, *--_First1); _Next1 = _First1) { - *_Next1 = _STD move(*_First1); // move hole down + for (_BidIt _Prev = _Hole; _DEBUG_LT_PRED(_Pred, _Val, *--_Prev); _Hole = _Prev) { + *_Hole = _STD move(*_Prev); // move hole down } - *_Next1 = _STD move(_Val); // insert element in hole + *_Hole = _STD move(_Val); // insert element in hole } } } @@ -6602,6 +7529,7 @@ _CONSTEXPR20 pair<_RanIt, _RanIt> _Partition_by_median_guess_unchecked(_RanIt _F for (;;) { // partition for (; _Gfirst < _Last; ++_Gfirst) { if (_DEBUG_LT_PRED(_Pred, *_Pfirst, *_Gfirst)) { + continue; } else if (_Pred(*_Gfirst, *_Pfirst)) { break; } else if (_Plast != _Gfirst) { @@ -6614,6 +7542,7 @@ _CONSTEXPR20 pair<_RanIt, _RanIt> _Partition_by_median_guess_unchecked(_RanIt _F for (; _First < _Glast; --_Glast) { if (_DEBUG_LT_PRED(_Pred, *_Prev_iter(_Glast), *_Pfirst)) { + continue; } else if (_Pred(*_Pfirst, *_Prev_iter(_Glast))) { break; } else if (--_Pfirst != _Prev_iter(_Glast)) { @@ -6699,6 +7628,156 @@ void sort(_ExPo&& _Exec, const _RanIt _First, const _RanIt _Last) noexcept /* te // order [_First, _Last) _STD sort(_STD forward<_ExPo>(_Exec), _First, _Last, less{}); } + +#ifdef __cpp_lib_concepts +namespace ranges { + // clang-format off + template + requires sortable<_It, _Pr, _Pj> + constexpr void _Insertion_sort_common(const _It _First, const _It _Last, _Pr _Pred, _Pj _Proj) { + // insertion sort [_First, _Last) + + if (_First == _Last) { // empty range is sorted + return; + } + + for (auto _Mid = _First; ++_Mid != _Last;) { // order next element + iter_value_t<_It> _Val = _RANGES iter_move(_Mid); + auto _Hole = _Mid; + + for (auto _Prev = _Hole;;) { + --_Prev; + if (!_STD invoke(_Pred, _STD invoke(_Proj, _Val), _STD invoke(_Proj, *_Prev))) { + break; + } + *_Hole = _RANGES iter_move(_Prev); // move hole down + if (--_Hole == _First) { + break; + } + } + + *_Hole = _STD move(_Val); // insert element in hole + } + } + + template + requires sortable<_It, _Pr, _Pj> + constexpr void _Med3_unchecked(_It _First, _It _Mid, _It _Last, _Pr _Pred, _Pj _Proj) { + // sort median of three elements to middle + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Mid), _STD invoke(_Proj, *_First))) { + _RANGES iter_swap(_Mid, _First); + } + + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_Last), _STD invoke(_Proj, *_Mid))) { + return; + } + + // swap middle and last, then test first again + _RANGES iter_swap(_Last, _Mid); + + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Mid), _STD invoke(_Proj, *_First))) { + _RANGES iter_swap(_Mid, _First); + } + } + + template + requires sortable<_It, _Pr, _Pj> + constexpr void _Guess_median_unchecked(_It _First, _It _Mid, _It _Last, _Pr _Pred, _Pj _Proj) { + // sort median element to middle + using _Diff = iter_difference_t<_It>; + const _Diff _Count = _Last - _First; + if (_Count > 40) { // Tukey's ninther + const _Diff _Step = (_Count + 1) >> 3; // +1 can't overflow because range was made inclusive in caller + const _Diff _Two_step = _Step << 1; // note: intentionally discards low-order bit + _Med3_unchecked(_First, _First + _Step, _First + _Two_step, _Pred, _Proj); + _Med3_unchecked(_Mid - _Step, _Mid, _Mid + _Step, _Pred, _Proj); + _Med3_unchecked(_Last - _Two_step, _Last - _Step, _Last, _Pred, _Proj); + _Med3_unchecked(_First + _Step, _Mid, _Last - _Step, _Pred, _Proj); + } else { + _Med3_unchecked(_First, _Mid, _Last, _Pred, _Proj); + } + } + + template + requires sortable<_It, _Pr, _Pj> + _NODISCARD constexpr subrange<_It> _Partition_by_median_guess_unchecked( + _It _First, _It _Last, _Pr _Pred, _Pj _Proj) { + // Choose a pivot, partition [_First, _Last) into elements less than pivot, elements equal to pivot, and + // elements greater than pivot; return the equal partition as a subrange. + + _It _Mid = _First + ((_Last - _First) >> 1); // shift for codegen + _RANGES _Guess_median_unchecked(_First, _Mid, _RANGES prev(_Last), _Pred, _Proj); + _It _Pfirst = _Mid; + _It _Plast = _RANGES next(_Pfirst); + + while (_First < _Pfirst + && !_STD invoke(_Pred, _STD invoke(_Proj, *_RANGES prev(_Pfirst)), _STD invoke(_Proj, *_Pfirst)) + && !_STD invoke(_Pred, _STD invoke(_Proj, *_Pfirst), _STD invoke(_Proj, *_RANGES prev(_Pfirst)))) { + --_Pfirst; + } + + while (_Plast < _Last + && !_STD invoke(_Pred, _STD invoke(_Proj, *_Plast), _STD invoke(_Proj, *_Pfirst)) + && !_STD invoke(_Pred, _STD invoke(_Proj, *_Pfirst), _STD invoke(_Proj, *_Plast))) { + ++_Plast; + } + + _It _Gfirst = _Plast; + _It _Glast = _Pfirst; + + for (;;) { // partition + for (; _Gfirst < _Last; ++_Gfirst) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Pfirst), _STD invoke(_Proj, *_Gfirst))) { + continue; + } else if (_STD invoke(_Pred, _STD invoke(_Proj, *_Gfirst), _STD invoke(_Proj, *_Pfirst))) { + break; + } else if (_Plast != _Gfirst) { + _RANGES iter_swap(_Plast, _Gfirst); + ++_Plast; + } else { + ++_Plast; + } + } + + for (; _First < _Glast; --_Glast) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_RANGES prev(_Glast)), _STD invoke(_Proj, *_Pfirst))) { + continue; + } else if (_STD invoke( + _Pred, _STD invoke(_Proj, *_Pfirst), _STD invoke(_Proj, *_RANGES prev(_Glast)))) { + break; + } else if (--_Pfirst != _RANGES prev(_Glast)) { + _RANGES iter_swap(_Pfirst, _RANGES prev(_Glast)); + } + } + + if (_Glast == _First && _Gfirst == _Last) { + return {_STD move(_Pfirst), _STD move(_Plast)}; + } + + if (_Glast == _First) { // no room at bottom, rotate pivot upward + if (_Plast != _Gfirst) { + _RANGES iter_swap(_Pfirst, _Plast); + } + + ++_Plast; + _RANGES iter_swap(_Pfirst, _Gfirst); + ++_Pfirst; + ++_Gfirst; + } else if (_Gfirst == _Last) { // no room at top, rotate pivot downward + if (--_Glast != --_Pfirst) { + _RANGES iter_swap(_Glast, _Pfirst); + } + + _RANGES iter_swap(_Pfirst, --_Plast); + } else { + _RANGES iter_swap(_Gfirst, --_Glast); + ++_Gfirst; + } + } + } + // clang-format on +} // namespace ranges +#endif // __cpp_lib_concepts #endif // _HAS_CXX17 // FUNCTION TEMPLATE stable_sort @@ -6963,8 +8042,8 @@ _CONSTEXPR20 _RanIt partial_sort_copy(_InIt _First1, _InIt _Last1, _RanIt _First if (_DEBUG_LT_PRED(_Pred, *_UFirst1, *_UFirst2)) { // replace top with new largest: using _Diff = _Iter_diff_t<_RanIt>; - _Pop_heap_hole_by_index(_UFirst2, static_cast<_Diff>(0), static_cast<_Diff>(_UMid2 - _UFirst2), - static_cast<_Iter_value_t<_InIt>>(*_UFirst1), _Pass_fn(_Pred)); + _Pop_heap_hole_by_index( + _UFirst2, static_cast<_Diff>(0), static_cast<_Diff>(_UMid2 - _UFirst2), *_UFirst1, _Pass_fn(_Pred)); } } @@ -7020,7 +8099,7 @@ _CONSTEXPR20 void nth_element(_RanIt _First, _RanIt _Nth, _RanIt _Last, _Pr _Pre if (_UMid.second <= _UNth) { _UFirst = _UMid.second; } else if (_UMid.first <= _UNth) { - return; // Nth inside fat pivot, done + return; // _Nth is in the subrange of elements equal to the pivot; done } else { _ULast = _UMid.first; } @@ -7048,6 +8127,82 @@ void nth_element(_ExPo&&, _RanIt _First, _RanIt _Nth, _RanIt _Last) noexcept /* // not parallelized at present, parallelism expected to be feasible in a future release _STD nth_element(_First, _Nth, _Last); } + +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::nth_element + class _Nth_element_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Pr = ranges::less, class _Pj = identity> + requires sortable<_It, _Pr, _Pj> + constexpr _It operator()(_It _First, _It _Nth, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Nth); + _Adl_verify_range(_Nth, _Last); + auto _UNth = _Get_unwrapped(_Nth); + auto _UFinal = _Get_final_iterator_unwrapped<_It>(_UNth, _STD move(_Last)); + _Seek_wrapped(_Nth, _UFinal); + + _Nth_element_common(_Get_unwrapped(_STD move(_First)), _STD move(_UNth), _STD move(_UFinal), + _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Nth; + } + + template + requires sortable, _Pr, _Pj> + constexpr borrowed_iterator_t<_Rng> operator()( + _Rng&& _Range, iterator_t<_Rng> _Nth, _Pr _Pred = {}, _Pj _Proj = {}) const { + _Adl_verify_range(_RANGES begin(_Range), _Nth); + _Adl_verify_range(_Nth, _RANGES end(_Range)); + auto _UNth = _Get_unwrapped(_Nth); + auto _UFinal = [&] { + if constexpr (common_range<_Rng>) { + return _Uend(_Range); + } else if constexpr (sized_range<_Rng>) { + return _RANGES next(_Ubegin(_Range), _RANGES distance(_Range)); + } else { + return _RANGES next(_UNth, _Uend(_Range)); + } + }(); + _Seek_wrapped(_Nth, _UFinal); + + _Nth_element_common( + _Ubegin(_Range), _STD move(_UNth), _STD move(_UFinal), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Nth; + } + // clang-format on + private: + template + static constexpr void _Nth_element_common(_It _First, _It _Nth, _It _Last, _Pr _Pred, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + + if (_Nth == _Last) { + return; // nothing to do + } + + while (_ISORT_MAX < _Last - _First) { // divide and conquer, ordering partition containing Nth + subrange<_It> _Mid = _RANGES _Partition_by_median_guess_unchecked(_First, _Last, _Pred, _Proj); + + if (_Mid.end() <= _Nth) { + _First = _Mid.end(); + } else if (_Mid.begin() <= _Nth) { + return; // _Nth is in the subrange of elements equal to the pivot; done + } else { + _Last = _Mid.begin(); + } + } + + // sort any remainder + _RANGES _Insertion_sort_common(_STD move(_First), _STD move(_Last), _STD move(_Pred), _STD move(_Proj)); + } + }; + + inline constexpr _Nth_element_fn nth_element{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts #endif // _HAS_CXX17 // FUNCTION TEMPLATE includes @@ -7102,6 +8257,79 @@ _NODISCARD bool includes(_ExPo&&, _FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _Firs _REQUIRE_PARALLEL_ITERATOR(_FwdIt2); return _STD includes(_First1, _Last1, _First2, _Last2); } + +#ifdef __cpp_lib_concepts +namespace ranges { + // VARIABLE ranges::includes + class _Includes_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se1, input_iterator _It2, sentinel_for<_It2> _Se2, + class _Pj1 = identity, class _Pj2 = identity, + indirect_strict_weak_order, projected<_It2, _Pj2>> _Pr = ranges::less> + _NODISCARD constexpr bool 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); + return _Includes_unchecked(_Get_unwrapped(_STD move(_First1)), _Get_unwrapped(_STD move(_Last1)), + _Get_unwrapped(_STD move(_First2)), _Get_unwrapped(_STD move(_Last2)), _Pass_fn(_Pred), + _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } + + template , _Pj1>, projected, _Pj2>> _Pr = + ranges::less> + _NODISCARD constexpr bool operator()( + _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + return _Includes_unchecked(_Ubegin(_Range1), _Uend(_Range1), _Ubegin(_Range2), _Uend(_Range2), + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } + + private: + template + _NODISCARD static constexpr bool _Includes_unchecked( + _It1 _First1, const _Se1 _Last1, _It2 _First2, const _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(indirect_strict_weak_order<_Pr, projected<_It1, _Pj1>, projected<_It2, _Pj2>>); + + if (_First2 == _Last2) { + return true; + } else if (_First1 == _Last1) { + return false; + } + + for (;;) { + if (_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { + ++_First1; + if (_First1 == _Last1) { + return false; + } + + continue; + } + + if (_STD invoke(_Pred, _STD invoke(_Proj2, *_First2), _STD invoke(_Proj1, *_First1))) { + return false; + } + + ++_First1; + ++_First2; + if (_First2 == _Last2) { + return true; + } else if (_First1 == _Last1) { + return false; + } + } + } + }; + + inline constexpr _Includes_fn includes{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts #endif // _HAS_CXX17 // FUNCTION TEMPLATE set_union @@ -7164,6 +8392,84 @@ _FwdIt3 set_union(_ExPo&&, _FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _Fw _REQUIRE_PARALLEL_ITERATOR(_FwdIt3); return _STD set_union(_First1, _Last1, _First2, _Last2, _Dest); } + +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE set_union_result + template + using set_union_result = in_in_out_result<_In1, _In2, _Out>; + + // VARIABLE ranges::set_union + class _Set_union_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se1, input_iterator _It2, sentinel_for<_It2> _Se2, + weakly_incrementable _Out, class _Pr = ranges::less, class _Pj1 = identity, class _Pj2 = identity> + requires mergeable<_It1, _It2, _Out, _Pr, _Pj1, _Pj2> + constexpr set_union_result<_It1, _It2, _Out> operator()(_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, + _Out _Result, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + _Adl_verify_range(_First1, _Last1); + _Adl_verify_range(_First2, _Last2); + auto _UResult = _Set_union_unchecked(_Get_unwrapped(_STD move(_First1)), _Get_unwrapped(_STD move(_Last1)), + _Get_unwrapped(_STD move(_First2)), _Get_unwrapped(_STD move(_Last2)), + _Get_unwrapped_unverified(_STD move(_Result)), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_UResult.in1)); + _Seek_wrapped(_First2, _STD move(_UResult.in2)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First1), _STD move(_First2), _STD move(_Result)}; + } + + template + requires mergeable, iterator_t<_Rng2>, _Out, _Pr, _Pj1, _Pj2> + constexpr set_union_result, borrowed_iterator_t<_Rng2>, _Out> operator()( + _Rng1&& _Range1, _Rng2&& _Range2, _Out _Result, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + auto _First1 = _RANGES begin(_Range1); + auto _First2 = _RANGES begin(_Range2); + auto _UResult = _Set_union_unchecked(_Get_unwrapped(_STD move(_First1)), _Uend(_Range1), + _Get_unwrapped(_STD move(_First2)), _Uend(_Range2), _Get_unwrapped_unverified(_STD move(_Result)), + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_UResult.in1)); + _Seek_wrapped(_First2, _STD move(_UResult.in2)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First1), _STD move(_First2), _STD move(_Result)}; + } + // clang-format on + private: + template + _NODISCARD static constexpr set_union_result<_It1, _It2, _Out> _Set_union_unchecked(_It1 _First1, + const _Se1 _Last1, _It2 _First2, const _Se2 _Last2, _Out _Result, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); + _STL_INTERNAL_STATIC_ASSERT(mergeable<_It1, _It2, _Out, _Pr, _Pj1, _Pj2>); + + for (; _First1 != _Last1 && _First2 != _Last2; ++_Result) { + if (_STD invoke(_Pred, _STD invoke(_Proj2, *_First2), _STD invoke(_Proj1, *_First1))) { + *_Result = *_First2; + ++_First2; + } else { + *_Result = *_First1; + if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { + ++_First2; + } + ++_First1; + } + } + + auto _UResult1 = _RANGES _Copy_unchecked(_STD move(_First1), _STD move(_Last1), _STD move(_Result)); + auto _UResult2 = _RANGES _Copy_unchecked(_STD move(_First2), _STD move(_Last2), _STD move(_UResult1.out)); + return {_STD move(_UResult1.in), _STD move(_UResult2.in), _STD move(_UResult2.out)}; + } + }; + + inline constexpr _Set_union_fn set_union{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts #endif // _HAS_CXX17 // FUNCTION TEMPLATE set_intersection @@ -7191,29 +8497,114 @@ _CONSTEXPR20 _OutIt set_intersection( ++_UFirst1; ++_UFirst2; } - } + } + + _Seek_wrapped(_Dest, _UDest); + return _Dest; +} + +template +_CONSTEXPR20 _OutIt set_intersection(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2, _OutIt _Dest) { + // AND sets [_First1, _Last1) and [_First2, _Last2) + return _STD set_intersection(_First1, _Last1, _First2, _Last2, _Dest, less<>{}); +} + +#if _HAS_CXX17 +template = 0> +_FwdIt3 set_intersection(_ExPo&&, _FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _FwdIt2 _Last2, _FwdIt3 _Dest, + _Pr _Pred) noexcept; // terminates + +template = 0> +_FwdIt3 set_intersection(_ExPo&& _Exec, _FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _FwdIt2 _Last2, + _FwdIt3 _Dest) noexcept /* terminates */ { + // AND sets [_First1, _Last1) and [_First2, _Last2) + return _STD set_intersection(_STD forward<_ExPo>(_Exec), _First1, _Last1, _First2, _Last2, _Dest, less{}); +} + +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE set_intersection_result + template + using set_intersection_result = in_in_out_result<_In1, _In2, _Out>; + + // VARIABLE ranges::set_intersection + class _Set_intersection_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se1, input_iterator _It2, sentinel_for<_It2> _Se2, + weakly_incrementable _Out, class _Pr = ranges::less, class _Pj1 = identity, class _Pj2 = identity> + requires mergeable<_It1, _It2, _Out, _Pr, _Pj1, _Pj2> + constexpr set_intersection_result<_It1, _It2, _Out> operator()(_It1 _First1, _Se1 _Last1, _It2 _First2, + _Se2 _Last2, _Out _Result, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + _Adl_verify_range(_First1, _Last1); + _Adl_verify_range(_First2, _Last2); + auto _UResult = + _Set_intersection_unchecked(_Get_unwrapped(_STD move(_First1)), _Get_unwrapped(_STD move(_Last1)), + _Get_unwrapped(_STD move(_First2)), _Get_unwrapped(_STD move(_Last2)), + _Get_unwrapped_unverified(_STD move(_Result)), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_UResult.in1)); + _Seek_wrapped(_First2, _STD move(_UResult.in2)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First1), _STD move(_First2), _STD move(_Result)}; + } + + template + requires mergeable, iterator_t<_Rng2>, _Out, _Pr, _Pj1, _Pj2> + constexpr set_intersection_result, borrowed_iterator_t<_Rng2>, _Out> operator()( + _Rng1&& _Range1, _Rng2&& _Range2, _Out _Result, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + auto _First1 = _RANGES begin(_Range1); + auto _First2 = _RANGES begin(_Range2); + auto _UResult = _Set_intersection_unchecked(_Get_unwrapped(_STD move(_First1)), _Uend(_Range1), + _Get_unwrapped(_STD move(_First2)), _Uend(_Range2), _Get_unwrapped_unverified(_STD move(_Result)), + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_UResult.in1)); + _Seek_wrapped(_First2, _STD move(_UResult.in2)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First1), _STD move(_First2), _STD move(_Result)}; + } + // clang-format on + private: + template + _NODISCARD static constexpr set_intersection_result<_It1, _It2, _Out> _Set_intersection_unchecked(_It1 _First1, + const _Se1 _Last1, _It2 _First2, const _Se2 _Last2, _Out _Result, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); + _STL_INTERNAL_STATIC_ASSERT(mergeable<_It1, _It2, _Out, _Pr, _Pj1, _Pj2>); - _Seek_wrapped(_Dest, _UDest); - return _Dest; -} + for (;;) { + if (_First1 == _Last1) { + _RANGES advance(_First2, _Last2); + break; + } else if (_First2 == _Last2) { + _RANGES advance(_First1, _Last1); + break; + } -template -_CONSTEXPR20 _OutIt set_intersection(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2, _OutIt _Dest) { - // AND sets [_First1, _Last1) and [_First2, _Last2) - return _STD set_intersection(_First1, _Last1, _First2, _Last2, _Dest, less<>{}); -} + if (_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { + ++_First1; + } else if (_STD invoke(_Pred, _STD invoke(_Proj2, *_First2), _STD invoke(_Proj1, *_First1))) { + ++_First2; + } else { + *_Result = *_First1; + ++_Result; + ++_First1; + ++_First2; + } + } -#if _HAS_CXX17 -template = 0> -_FwdIt3 set_intersection(_ExPo&&, _FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _FwdIt2 _Last2, _FwdIt3 _Dest, - _Pr _Pred) noexcept; // terminates + return {_STD move(_First1), _STD move(_First2), _STD move(_Result)}; + } + }; -template = 0> -_FwdIt3 set_intersection(_ExPo&& _Exec, _FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _FwdIt2 _Last2, - _FwdIt3 _Dest) noexcept /* terminates */ { - // AND sets [_First1, _Last1) and [_First2, _Last2) - return _STD set_intersection(_STD forward<_ExPo>(_Exec), _First1, _Last1, _First2, _Last2, _Dest, less{}); -} + inline constexpr _Set_intersection_fn set_intersection{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts #endif // _HAS_CXX17 // FUNCTION TEMPLATE set_difference @@ -7265,6 +8656,87 @@ _FwdIt3 set_difference(_ExPo&& _Exec, _FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _ // take set [_First2, _Last2) from [_First1, _Last1) return _STD set_difference(_STD forward<_ExPo>(_Exec), _First1, _Last1, _First2, _Last2, _Dest, less{}); } + +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE set_difference_result + template + using set_difference_result = in_out_result<_In, _Out>; + + // VARIABLE ranges::set_difference + class _Set_difference_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se1, input_iterator _It2, sentinel_for<_It2> _Se2, + weakly_incrementable _Out, class _Pr = ranges::less, class _Pj1 = identity, class _Pj2 = identity> + requires mergeable<_It1, _It2, _Out, _Pr, _Pj1, _Pj2> + constexpr set_difference_result<_It1, _Out> operator()(_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, + _Out _Result, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + _Adl_verify_range(_First1, _Last1); + _Adl_verify_range(_First2, _Last2); + auto _UResult = + _Set_difference_unchecked(_Get_unwrapped(_STD move(_First1)), _Get_unwrapped(_STD move(_Last1)), + _Get_unwrapped(_STD move(_First2)), _Get_unwrapped(_STD move(_Last2)), + _Get_unwrapped_unverified(_STD move(_Result)), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_UResult.in)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First1), _STD move(_Result)}; + } + + template + requires mergeable, iterator_t<_Rng2>, _Out, _Pr, _Pj1, _Pj2> + constexpr set_difference_result, _Out> operator()( + _Rng1&& _Range1, _Rng2&& _Range2, _Out _Result, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + auto _First1 = _RANGES begin(_Range1); + auto _UResult = _Set_difference_unchecked(_Get_unwrapped(_STD move(_First1)), _Uend(_Range1), + _Ubegin(_Range2), _Uend(_Range2), _Get_unwrapped_unverified(_STD move(_Result)), _Pass_fn(_Pred), + _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_UResult.in)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First1), _STD move(_Result)}; + } + // clang-format on + private: + template + _NODISCARD static constexpr set_difference_result<_It1, _Out> _Set_difference_unchecked(_It1 _First1, + const _Se1 _Last1, _It2 _First2, const _Se2 _Last2, _Out _Result, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); + _STL_INTERNAL_STATIC_ASSERT(mergeable<_It1, _It2, _Out, _Pr, _Pj1, _Pj2>); + + for (;;) { + if (_First1 == _Last1) { + return {_STD move(_First1), _STD move(_Result)}; + } + + if (_First2 == _Last2) { + return _RANGES _Copy_unchecked(_STD move(_First1), _STD move(_Last1), _STD move(_Result)); + } + + if (_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { + *_Result = *_First1; + ++_Result; + ++_First1; + } else { + if (!_STD invoke(_Pred, _STD invoke(_Proj2, *_First2), _STD invoke(_Proj1, *_First1))) { + ++_First1; + } + + ++_First2; + } + } + } + }; + + inline constexpr _Set_difference_fn set_difference{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts #endif // _HAS_CXX17 // FUNCTION TEMPLATE set_symmetric_difference @@ -7330,6 +8802,95 @@ _FwdIt3 set_symmetric_difference(_ExPo&&, _FwdIt1 _First1, _FwdIt1 _Last1, _FwdI _REQUIRE_PARALLEL_ITERATOR(_FwdIt3); return _STD set_symmetric_difference(_First1, _Last1, _First2, _Last2, _Dest); } + +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE set_symmetric_difference_result + template + using set_symmetric_difference_result = in_in_out_result<_In1, _In2, _Out>; + + // VARIABLE ranges::set_symmetric_difference + class _Set_symmetric_difference_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se1, input_iterator _It2, sentinel_for<_It2> _Se2, + weakly_incrementable _Out, class _Pr = ranges::less, class _Pj1 = identity, class _Pj2 = identity> + requires mergeable<_It1, _It2, _Out, _Pr, _Pj1, _Pj2> + constexpr set_symmetric_difference_result<_It1, _It2, _Out> operator()(_It1 _First1, _Se1 _Last1, _It2 _First2, + _Se2 _Last2, _Out _Result, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + _Adl_verify_range(_First1, _Last1); + _Adl_verify_range(_First2, _Last2); + auto _UResult = _Set_symmetric_difference_unchecked(_Get_unwrapped(_STD move(_First1)), + _Get_unwrapped(_STD move(_Last1)), _Get_unwrapped(_STD move(_First2)), + _Get_unwrapped(_STD move(_Last2)), _Get_unwrapped_unverified(_STD move(_Result)), _Pass_fn(_Pred), + _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_UResult.in1)); + _Seek_wrapped(_First2, _STD move(_UResult.in2)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First1), _STD move(_First2), _STD move(_Result)}; + } + + template + requires mergeable, iterator_t<_Rng2>, _Out, _Pr, _Pj1, _Pj2> + constexpr set_symmetric_difference_result, borrowed_iterator_t<_Rng2>, _Out> + operator()(_Rng1&& _Range1, _Rng2&& _Range2, _Out _Result, _Pr _Pred = {}, _Pj1 _Proj1 = {}, + _Pj2 _Proj2 = {}) const { + auto _First1 = _RANGES begin(_Range1); + auto _First2 = _RANGES begin(_Range2); + auto _UResult = _Set_symmetric_difference_unchecked(_Get_unwrapped(_STD move(_First1)), _Uend(_Range1), + _Get_unwrapped(_STD move(_First2)), _Uend(_Range2), _Get_unwrapped_unverified(_STD move(_Result)), + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + _Seek_wrapped(_First1, _STD move(_UResult.in1)); + _Seek_wrapped(_First2, _STD move(_UResult.in2)); + _Seek_wrapped(_Result, _STD move(_UResult.out)); + return {_STD move(_First1), _STD move(_First2), _STD move(_Result)}; + } + // clang-format on + private: + template + _NODISCARD static constexpr set_symmetric_difference_result<_It1, _It2, _Out> + _Set_symmetric_difference_unchecked(_It1 _First1, const _Se1 _Last1, _It2 _First2, const _Se2 _Last2, + _Out _Result, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(weakly_incrementable<_Out>); + _STL_INTERNAL_STATIC_ASSERT(mergeable<_It1, _It2, _Out, _Pr, _Pj1, _Pj2>); + + for (;;) { + if (_First1 == _Last1) { + auto _UResult = _RANGES _Copy_unchecked(_STD move(_First2), _STD move(_Last2), _STD move(_Result)); + return {_STD move(_First1), _STD move(_UResult.in), _STD move(_UResult.out)}; + } + + if (_First2 == _Last2) { + auto _UResult = _RANGES _Copy_unchecked(_STD move(_First1), _STD move(_Last1), _STD move(_Result)); + return {_STD move(_UResult.in), _STD move(_First2), _STD move(_UResult.out)}; + } + + if (_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { + *_Result = *_First1; + ++_Result; + ++_First1; + } else if (_STD invoke(_Pred, _STD invoke(_Proj2, *_First2), _STD invoke(_Proj1, *_First1))) { + *_Result = *_First2; + ++_Result; + ++_First2; + } else { + ++_First1; + ++_First2; + } + } + } + }; + + inline constexpr _Set_symmetric_difference_fn set_symmetric_difference{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts #endif // _HAS_CXX17 // FUNCTION TEMPLATE max_element @@ -8003,6 +9564,79 @@ _CONSTEXPR20 bool next_permutation(_BidIt _First, _BidIt _Last) { return _STD next_permutation(_First, _Last, less<>{}); } +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE next_permutation_result + template + using next_permutation_result = in_found_result<_In>; + + // VARIABLE ranges::next_permutation + class _Next_permutation_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Pr = ranges::less, class _Pj = identity> + requires sortable<_It, _Pr, _Pj> + constexpr next_permutation_result<_It> operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + // clang-format on + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); + _Seek_wrapped(_First, _ULast); + const bool _Found = + _Next_permutation_common(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return {_STD move(_First), _Found}; + } + + // clang-format off + template + requires sortable, _Pr, _Pj> + constexpr next_permutation_result> operator()( + _Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + // clang-format on + auto _ULast = _Get_final_iterator_unwrapped(_Range); + const bool _Found = _Next_permutation_common(_Ubegin(_Range), _ULast, _Pass_fn(_Pred), _Pass_fn(_Proj)); + return {_Rewrap_iterator(_Range, _STD move(_ULast)), _Found}; + } + + private: + template + _NODISCARD static constexpr bool _Next_permutation_common(_It _First, _It _Last, _Pr _Pred, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + + auto _Next = _Last; + if (_First == _Last || _First == --_Next) { + return false; + } + + for (;;) { // find rightmost element smaller than successor + auto _Next1 = _Next; + if (_STD invoke(_Pred, _STD invoke(_Proj, *--_Next), _STD invoke(_Proj, *_Next1))) { + // swap with rightmost element that's smaller, flip suffix + auto _Mid = _Last; + do { + --_Mid; + } while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Next), _STD invoke(_Proj, *_Mid))); + + _RANGES iter_swap(_Next, _Mid); + _Reverse_common(_STD move(_Next1), _STD move(_Last)); + return true; + } + + if (_Next == _First) { // pure descending, flip all + _Reverse_common(_STD move(_First), _STD move(_Last)); + return false; + } + } + } + }; + + inline constexpr _Next_permutation_fn next_permutation{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE prev_permutation template _CONSTEXPR20 bool prev_permutation(_BidIt _First, _BidIt _Last, _Pr _Pred) { @@ -8041,6 +9675,79 @@ _CONSTEXPR20 bool prev_permutation(_BidIt _First, _BidIt _Last) { return _STD prev_permutation(_First, _Last, less<>{}); } +#ifdef __cpp_lib_concepts +namespace ranges { + // ALIAS TEMPLATE prev_permutation_result + template + using prev_permutation_result = in_found_result<_In>; + + // VARIABLE ranges::prev_permutation + class _Prev_permutation_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se, class _Pr = ranges::less, class _Pj = identity> + requires sortable<_It, _Pr, _Pj> + constexpr prev_permutation_result<_It> operator()(_It _First, _Se _Last, _Pr _Pred = {}, _Pj _Proj = {}) const { + // clang-format on + _Adl_verify_range(_First, _Last); + auto _UFirst = _Get_unwrapped(_STD move(_First)); + auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); + _Seek_wrapped(_First, _ULast); + const bool _Found = + _Prev_permutation_common(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return {_STD move(_First), _Found}; + } + + // clang-format off + template + requires sortable, _Pr, _Pj> + constexpr prev_permutation_result> operator()( + _Rng&& _Range, _Pr _Pred = {}, _Pj _Proj = {}) const { + // clang-format on + auto _ULast = _Get_final_iterator_unwrapped(_Range); + const bool _Found = _Prev_permutation_common(_Ubegin(_Range), _ULast, _Pass_fn(_Pred), _Pass_fn(_Proj)); + return {_Rewrap_iterator(_Range, _STD move(_ULast)), _Found}; + } + + private: + template + _NODISCARD static constexpr bool _Prev_permutation_common(_It _First, _It _Last, _Pr _Pred, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sortable<_It, _Pr, _Pj>); + + auto _Next = _Last; + if (_First == _Last || _First == --_Next) { + return false; + } + + for (;;) { // find rightmost element not smaller than successor + auto _Next1 = _Next; + if (_STD invoke(_Pred, _STD invoke(_Proj, *_Next1), _STD invoke(_Proj, *--_Next))) { + // swap with rightmost element that's not smaller, flip suffix + auto _Mid = _Last; + do { + --_Mid; + } while (!_STD invoke(_Pred, _STD invoke(_Proj, *_Mid), _STD invoke(_Proj, *_Next))); + + _RANGES iter_swap(_Next, _Mid); + _Reverse_common(_STD move(_Next1), _STD move(_Last)); + return true; + } + + if (_Next == _First) { // pure ascending, flip all + _Reverse_common(_STD move(_First), _STD move(_Last)); + return false; + } + } + } + }; + + inline constexpr _Prev_permutation_fn prev_permutation{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATES is_sorted AND is_sorted_until template _NODISCARD _CONSTEXPR20 _FwdIt is_sorted_until(const _FwdIt _First, _FwdIt _Last, _Pr _Pred) { @@ -8237,6 +9944,59 @@ namespace ranges { }; inline constexpr _Clamp_fn clamp{_Not_quite_object::_Construct_tag{}}; + + // VARIABLE ranges::lexicographical_compare + class _Lexicographical_compare_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se1, input_iterator _It2, sentinel_for<_It2> _Se2, + class _Pj1 = identity, class _Pj2 = identity, + indirect_strict_weak_order, projected<_It2, _Pj2>> _Pr = ranges::less> + _NODISCARD constexpr bool 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); + + return _Lexicographical_compare_unchecked(_Get_unwrapped(_STD move(_First1)), + _Get_unwrapped(_STD move(_Last1)), _Get_unwrapped(_STD move(_First2)), + _Get_unwrapped(_STD move(_Last2)), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } + + template , _Pj1>, projected, _Pj2>> _Pr = + ranges::less> + _NODISCARD constexpr bool operator()( + _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + return _Lexicographical_compare_unchecked(_Ubegin(_Range1), _Uend(_Range1), _Ubegin(_Range2), + _Uend(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + } + + private: + template + _NODISCARD static constexpr bool _Lexicographical_compare_unchecked( + _It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(input_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(indirect_strict_weak_order<_Pr, projected<_It1, _Pj1>, projected<_It2, _Pj2>>); + + for (;; ++_First1, (void) ++_First2) { + if (_First2 == _Last2) { + return false; + } else if (_First1 == _Last1) { + return true; + } else if (_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { + return true; + } else if (_STD invoke(_Pred, _STD invoke(_Proj2, *_First2), _STD invoke(_Proj1, *_First1))) { + return false; + } + } + } + }; + + inline constexpr _Lexicographical_compare_fn lexicographical_compare{_Not_quite_object::_Construct_tag{}}; } // namespace ranges #endif // __cpp_lib_concepts #endif // _HAS_CXX17 diff --git a/stl/inc/atomic b/stl/inc/atomic index 8881fae37a8..633280f5d53 100644 --- a/stl/inc/atomic +++ b/stl/inc/atomic @@ -17,6 +17,9 @@ #include #include #include +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) @@ -115,7 +118,52 @@ _NODISCARD extern "C" bool __cdecl __std_atomic_has_cmpxchg16b() noexcept; #define ATOMIC_LLONG_LOCK_FREE 2 #define ATOMIC_POINTER_LOCK_FREE 2 +// Padding bits should not participate in cmpxchg comparison starting in C++20. +// Clang does not have __builtin_zero_non_value_bits to exclude these bits to implement this C++20 feature. +// The EDG front-end substitutes everything and runs into incomplete types passed to atomic. +#if _HAS_CXX20 && !defined(__clang__) /* TRANSITION, LLVM-46685 */ && !defined(__EDG__) +#define _CMPXCHG_MASK_OUT_PADDING_BITS 1 +#else +#define _CMPXCHG_MASK_OUT_PADDING_BITS 0 +#endif + _STD_BEGIN +// STRUCT TEMPLATE _Storage_for +#if _CMPXCHG_MASK_OUT_PADDING_BITS +struct _Form_mask_t {}; +_INLINE_VAR constexpr _Form_mask_t _Form_mask{}; +#endif // _CMPXCHG_MASK_OUT_PADDING_BITS + +template +struct _Storage_for { + // uninitialized space to store a _Ty + alignas(_Ty) unsigned char _Storage[sizeof(_Ty)]; + + _Storage_for() = default; + _Storage_for(const _Storage_for&) = delete; + _Storage_for& operator=(const _Storage_for&) = delete; + +#if _CMPXCHG_MASK_OUT_PADDING_BITS + explicit _Storage_for(_Form_mask_t) noexcept { + _CSTD memset(_Storage, 0xff, sizeof(_Ty)); + __builtin_zero_non_value_bits(_Ptr()); + } +#endif // _CMPXCHG_MASK_OUT_PADDING_BITS + + _NODISCARD _Ty& _Ref() noexcept { + return reinterpret_cast<_Ty&>(_Storage); + } + + _NODISCARD _Ty* _Ptr() noexcept { + return reinterpret_cast<_Ty*>(&_Storage); + } +}; + +#if _CMPXCHG_MASK_OUT_PADDING_BITS +template +inline constexpr bool _Might_have_non_value_bits = + !has_unique_object_representations_v<_Ty> && !is_floating_point_v<_Ty>; +#endif // _CMPXCHG_MASK_OUT_PADDING_BITS // FENCES extern "C" inline void atomic_thread_fence(const memory_order _Order) noexcept { @@ -312,6 +360,83 @@ template #else // ^^^ don't break ABI / break ABI vvv template ::_Storage_size> #endif // TRANSITION, ABI +struct _Atomic_storage; + +#if _HAS_CXX20 +template +void _Atomic_wait_direct( + const _Atomic_storage<_Ty>* const _This, _Value_type _Expected_bytes, const memory_order _Order) noexcept { + const auto _Storage_ptr = _STD addressof(_This->_Storage); + for (;;) { + const _Value_type _Observed_bytes = _Atomic_reinterpret_as<_Value_type>(_This->load(_Order)); + if (_Expected_bytes != _Observed_bytes) { +#if _CMPXCHG_MASK_OUT_PADDING_BITS + if constexpr (_Might_have_non_value_bits<_Ty>) { + _Storage_for<_Ty> _Mask{_Form_mask}; + const _Value_type _Mask_val = _Atomic_reinterpret_as<_Value_type>(_Mask._Ref()); + + if (((_Expected_bytes ^ _Observed_bytes) & _Mask_val) == 0) { + _Expected_bytes = _Observed_bytes; + continue; + } + } +#endif // _CMPXCHG_MASK_OUT_PADDING_BITS + + return; + } + + __std_atomic_wait_direct(_Storage_ptr, &_Expected_bytes, sizeof(_Value_type), _Atomic_wait_no_timeout); + } +} +#endif // _HAS_CXX20 + +#if 1 // TRANSITION, ABI +inline void _Atomic_lock_spinlock(long& _Spinlock) noexcept { + while (_InterlockedExchange(&_Spinlock, 1)) { + _YIELD_PROCESSOR(); + } +} + +inline void _Atomic_unlock_spinlock(long& _Spinlock) noexcept { +#if defined(_M_ARM) || defined(_M_ARM64) + _Memory_barrier(); + __iso_volatile_store32(reinterpret_cast(&_Spinlock), 0); + _Memory_barrier(); +#else // ^^^ ARM32/ARM64 hardware / x86/x64 hardware vvv + _InterlockedExchange(&_Spinlock, 0); +#endif // hardware +} + +class _Spinlock_guard { +public: + explicit _Spinlock_guard(long& _Spinlock_) noexcept : _Spinlock(_Spinlock_) { + _Atomic_lock_spinlock(_Spinlock); + } + + ~_Spinlock_guard() { + _Atomic_unlock_spinlock(_Spinlock); + } + + _Spinlock_guard(const _Spinlock_guard&) = delete; + _Spinlock_guard& operator=(const _Spinlock_guard&) = delete; + +private: + long& _Spinlock; +}; + +#if _HAS_CXX20 +inline bool __stdcall _Atomic_wait_compare_non_lock_free( + const void* _Storage, void* _Comparand, size_t _Size, void* _Spinlock_raw) noexcept { + long& _Spinlock = *static_cast(_Spinlock_raw); + _Atomic_lock_spinlock(_Spinlock); + const auto _Cmp_result = _CSTD memcmp(_Storage, _Comparand, _Size); + _Atomic_unlock_spinlock(_Spinlock); + return _Cmp_result == 0; +} +#endif // _HAS_CXX20 +#endif // TRANSITION, ABI + +template struct _Atomic_storage { // Provides operations common to all specializations of std::atomic, load, store, exchange, and CAS. // Locking version used when hardware has no atomic operations for sizeof(_Ty). @@ -355,34 +480,86 @@ struct _Atomic_storage { const auto _Storage_ptr = _STD addressof(_Storage); const auto _Expected_ptr = _STD addressof(_Expected); bool _Result; +#if _CMPXCHG_MASK_OUT_PADDING_BITS + __builtin_zero_non_value_bits(_Expected_ptr); +#endif // _CMPXCHG_MASK_OUT_PADDING_BITS _Lock(); - if (_CSTD memcmp(_Storage_ptr, _Expected_ptr, sizeof(_Ty)) == 0) { +#if _CMPXCHG_MASK_OUT_PADDING_BITS + if constexpr (_Might_have_non_value_bits<_Ty>) { + _Storage_for<_Ty> _Local; + const auto _Local_ptr = _Local._Ptr(); + _CSTD memcpy(_Local_ptr, _Storage_ptr, sizeof(_Ty)); + __builtin_zero_non_value_bits(_Local_ptr); + _Result = _CSTD memcmp(_Local_ptr, _Expected_ptr, sizeof(_Ty)) == 0; + } else { + _Result = _CSTD memcmp(_Storage_ptr, _Expected_ptr, sizeof(_Ty)) == 0; + } +#else // _CMPXCHG_MASK_OUT_PADDING_BITS + _Result = _CSTD memcmp(_Storage_ptr, _Expected_ptr, sizeof(_Ty)) == 0; +#endif // _CMPXCHG_MASK_OUT_PADDING_BITS + if (_Result) { _CSTD memcpy(_Storage_ptr, _STD addressof(_Desired), sizeof(_Ty)); - _Result = true; } else { _CSTD memcpy(_Expected_ptr, _Storage_ptr, sizeof(_Ty)); - _Result = false; } _Unlock(); return _Result; } +#if _HAS_CXX20 + void wait(_Ty _Expected, memory_order = memory_order_seq_cst) const noexcept { + const auto _Storage_ptr = _STD addressof(_Storage); + const auto _Expected_ptr = _STD addressof(_Expected); + for (;;) { + { + _Spinlock_guard _Lock{_Spinlock}; + if (_CSTD memcmp(_Storage_ptr, _Expected_ptr, sizeof(_Ty)) != 0) { + // contents differed, we might be done, check for padding +#if _CMPXCHG_MASK_OUT_PADDING_BITS + if constexpr (_Might_have_non_value_bits<_Ty>) { + _Storage_for<_Ty> _Local; + const auto _Local_ptr = _Local._Ptr(); + _CSTD memcpy(_Local_ptr, _Storage_ptr, sizeof(_Ty)); + __builtin_zero_non_value_bits(_Local_ptr); + __builtin_zero_non_value_bits(_Expected_ptr); + if (_CSTD memcmp(_Local_ptr, _Expected_ptr, sizeof(_Ty)) == 0) { + // _Storage differs from _Expected only by padding; copy the padding from _Storage into + // _Expected + _CSTD memcpy(_Expected_ptr, _Storage_ptr, sizeof(_Ty)); + } else { + // truly different, we're done + return; + } + } else +#endif // #if _CMPXCHG_MASK_OUT_PADDING_BITS + { + return; + } + } + } // unlock + + __std_atomic_wait_indirect(_Storage_ptr, _Expected_ptr, sizeof(_Ty), &_Spinlock, + &_Atomic_wait_compare_non_lock_free, _Atomic_wait_no_timeout); + } + } + + void notify_one() noexcept { + __std_atomic_notify_one_indirect(_STD addressof(_Storage)); + } + + void notify_all() noexcept { + __std_atomic_notify_all_indirect(_STD addressof(_Storage)); + } +#endif // _HAS_CXX20 + #if 1 // TRANSITION, ABI void _Lock() const noexcept { // lock the spinlock - while (_InterlockedExchange(&_Spinlock, 1)) { - _YIELD_PROCESSOR(); - } + _Atomic_lock_spinlock(_Spinlock); } void _Unlock() const noexcept { // unlock the spinlock -#if defined(_M_ARM) || defined(_M_ARM64) - _Memory_barrier(); - __iso_volatile_store32(reinterpret_cast(&_Spinlock), 0); - _Memory_barrier(); -#else // ^^^ ARM32/ARM64 hardware / x86/x64 hardware vvv - _InterlockedExchange(&_Spinlock, 0); -#endif // hardware + _Atomic_unlock_spinlock(_Spinlock); } private: @@ -393,23 +570,15 @@ public: #else // ^^^ don't break ABI / break ABI vvv void _Lock() const noexcept { // lock the spinlock - while (_InterlockedExchange8(&_Spinlock, 1)) { - _YIELD_PROCESSOR(); - } + _Smtx_lock_exclusive(&_Spinlock); } void _Unlock() const noexcept { // unlock the spinlock -#if defined(_M_ARM) || defined(_M_ARM64) - _Memory_barrier(); - __iso_volatile_store8(&_Spinlock, 0); - _Memory_barrier(); -#else // ^^^ ARM32/ARM64 hardware / x86/x64 hardware vvv - _InterlockedExchange8(&_Spinlock, 0); -#endif // hardware + _Smtx_unlock_exclusive(&_Spinlock); } _Ty _Storage; - mutable char _Spinlock = 0; + mutable _Smtx_t _Spinlock = 0; #endif // TRANSITION, ABI }; @@ -480,8 +649,29 @@ struct _Atomic_storage<_Ty, 1> { // lock-free using 1-byte intrinsics bool compare_exchange_strong(_Ty& _Expected, const _Ty _Desired, const memory_order _Order = memory_order_seq_cst) noexcept { // CAS with given memory order - const char _Expected_bytes = _Atomic_reinterpret_as(_Expected); // read before atomic operation + char _Expected_bytes = _Atomic_reinterpret_as(_Expected); // read before atomic operation char _Prev_bytes; + +#if _CMPXCHG_MASK_OUT_PADDING_BITS + if constexpr (_Might_have_non_value_bits<_Ty>) { + _Storage_for<_Ty> _Mask{_Form_mask}; + const char _Mask_val = _Atomic_reinterpret_as(_Mask._Ref()); + + for (;;) { + _ATOMIC_CHOOSE_INTRINSIC(_Order, _Prev_bytes, _InterlockedCompareExchange8, + _Atomic_address_as(_Storage), _Atomic_reinterpret_as(_Desired), _Expected_bytes); + if (_Prev_bytes == _Expected_bytes) { + return true; + } + + if ((_Prev_bytes ^ _Expected_bytes) & _Mask_val) { + reinterpret_cast(_Expected) = _Prev_bytes; + return false; + } + _Expected_bytes = (_Expected_bytes & _Mask_val) | (_Prev_bytes & ~_Mask_val); + } + } +#endif // _CMPXCHG_MASK_OUT_PADDING_BITS _ATOMIC_CHOOSE_INTRINSIC(_Order, _Prev_bytes, _InterlockedCompareExchange8, _Atomic_address_as(_Storage), _Atomic_reinterpret_as(_Desired), _Expected_bytes); if (_Prev_bytes == _Expected_bytes) { @@ -492,6 +682,20 @@ struct _Atomic_storage<_Ty, 1> { // lock-free using 1-byte intrinsics return false; } +#if _HAS_CXX20 + void wait(const _Ty _Expected, const memory_order _Order = memory_order_seq_cst) const noexcept { + _Atomic_wait_direct(this, _Atomic_reinterpret_as(_Expected), _Order); + } + + void notify_one() noexcept { + __std_atomic_notify_one_direct(_STD addressof(_Storage)); + } + + void notify_all() noexcept { + __std_atomic_notify_all_direct(_STD addressof(_Storage)); + } +#endif // _HAS_CXX20 + _Atomic_padded<_Ty> _Storage; }; @@ -562,8 +766,28 @@ struct _Atomic_storage<_Ty, 2> { // lock-free using 2-byte intrinsics bool compare_exchange_strong(_Ty& _Expected, const _Ty _Desired, const memory_order _Order = memory_order_seq_cst) noexcept { // CAS with given memory order - const short _Expected_bytes = _Atomic_reinterpret_as(_Expected); // read before atomic operation + short _Expected_bytes = _Atomic_reinterpret_as(_Expected); // read before atomic operation short _Prev_bytes; +#if _CMPXCHG_MASK_OUT_PADDING_BITS + if constexpr (_Might_have_non_value_bits<_Ty>) { + _Storage_for<_Ty> _Mask{_Form_mask}; + const short _Mask_val = _Atomic_reinterpret_as(_Mask._Ref()); + + for (;;) { + _ATOMIC_CHOOSE_INTRINSIC(_Order, _Prev_bytes, _InterlockedCompareExchange16, + _Atomic_address_as(_Storage), _Atomic_reinterpret_as(_Desired), _Expected_bytes); + if (_Prev_bytes == _Expected_bytes) { + return true; + } + + if ((_Prev_bytes ^ _Expected_bytes) & _Mask_val) { + _CSTD memcpy(_STD addressof(_Expected), &_Prev_bytes, sizeof(_Ty)); + return false; + } + _Expected_bytes = (_Expected_bytes & _Mask_val) | (_Prev_bytes & ~_Mask_val); + } + } +#endif // _CMPXCHG_MASK_OUT_PADDING_BITS _ATOMIC_CHOOSE_INTRINSIC(_Order, _Prev_bytes, _InterlockedCompareExchange16, _Atomic_address_as(_Storage), _Atomic_reinterpret_as(_Desired), _Expected_bytes); if (_Prev_bytes == _Expected_bytes) { @@ -574,6 +798,20 @@ struct _Atomic_storage<_Ty, 2> { // lock-free using 2-byte intrinsics return false; } +#if _HAS_CXX20 + void wait(const _Ty _Expected, const memory_order _Order = memory_order_seq_cst) const noexcept { + _Atomic_wait_direct(this, _Atomic_reinterpret_as(_Expected), _Order); + } + + void notify_one() noexcept { + __std_atomic_notify_one_direct(_STD addressof(_Storage)); + } + + void notify_all() noexcept { + __std_atomic_notify_all_direct(_STD addressof(_Storage)); + } +#endif // _HAS_CXX20 + _Atomic_padded<_Ty> _Storage; }; @@ -642,8 +880,28 @@ struct _Atomic_storage<_Ty, 4> { // lock-free using 4-byte intrinsics bool compare_exchange_strong(_Ty& _Expected, const _Ty _Desired, const memory_order _Order = memory_order_seq_cst) noexcept { // CAS with given memory order - const long _Expected_bytes = _Atomic_reinterpret_as(_Expected); // read before atomic operation + long _Expected_bytes = _Atomic_reinterpret_as(_Expected); // read before atomic operation long _Prev_bytes; +#if _CMPXCHG_MASK_OUT_PADDING_BITS + if constexpr (_Might_have_non_value_bits<_Ty>) { + _Storage_for<_Ty> _Mask{_Form_mask}; + const long _Mask_val = _Atomic_reinterpret_as(_Mask); + + for (;;) { + _ATOMIC_CHOOSE_INTRINSIC(_Order, _Prev_bytes, _InterlockedCompareExchange, + _Atomic_address_as(_Storage), _Atomic_reinterpret_as(_Desired), _Expected_bytes); + if (_Prev_bytes == _Expected_bytes) { + return true; + } + + if ((_Prev_bytes ^ _Expected_bytes) & _Mask_val) { + _CSTD memcpy(_STD addressof(_Expected), &_Prev_bytes, sizeof(_Ty)); + return false; + } + _Expected_bytes = (_Expected_bytes & _Mask_val) | (_Prev_bytes & ~_Mask_val); + } + } +#endif // _CMPXCHG_MASK_OUT_PADDING_BITS _ATOMIC_CHOOSE_INTRINSIC(_Order, _Prev_bytes, _InterlockedCompareExchange, _Atomic_address_as(_Storage), _Atomic_reinterpret_as(_Desired), _Expected_bytes); if (_Prev_bytes == _Expected_bytes) { @@ -654,6 +912,20 @@ struct _Atomic_storage<_Ty, 4> { // lock-free using 4-byte intrinsics return false; } +#if _HAS_CXX20 + void wait(const _Ty _Expected, const memory_order _Order = memory_order_seq_cst) const noexcept { + _Atomic_wait_direct(this, _Atomic_reinterpret_as(_Expected), _Order); + } + + void notify_one() noexcept { + __std_atomic_notify_one_direct(_STD addressof(_Storage)); + } + + void notify_all() noexcept { + __std_atomic_notify_all_direct(_STD addressof(_Storage)); + } +#endif // _HAS_CXX20 + _Atomic_padded<_Ty> _Storage; }; @@ -749,8 +1021,30 @@ struct _Atomic_storage<_Ty, 8> { // lock-free using 8-byte intrinsics bool compare_exchange_strong(_Ty& _Expected, const _Ty _Desired, const memory_order _Order = memory_order_seq_cst) noexcept { // CAS with given memory order - const long long _Expected_bytes = _Atomic_reinterpret_as(_Expected); // read before atomic operation + long long _Expected_bytes = _Atomic_reinterpret_as(_Expected); // read before atomic operation long long _Prev_bytes; + +#if _CMPXCHG_MASK_OUT_PADDING_BITS + if constexpr (_Might_have_non_value_bits<_Ty>) { + _Storage_for<_Ty> _Mask{_Form_mask}; + const long long _Mask_val = _Atomic_reinterpret_as(_Mask); + + for (;;) { + _ATOMIC_CHOOSE_INTRINSIC(_Order, _Prev_bytes, _InterlockedCompareExchange64, + _Atomic_address_as(_Storage), _Atomic_reinterpret_as(_Desired), + _Expected_bytes); + if (_Prev_bytes == _Expected_bytes) { + return true; + } + + if ((_Prev_bytes ^ _Expected_bytes) & _Mask_val) { + _CSTD memcpy(_STD addressof(_Expected), &_Prev_bytes, sizeof(_Ty)); + return false; + } + _Expected_bytes = (_Expected_bytes & _Mask_val) | (_Prev_bytes & ~_Mask_val); + } + } +#endif // _CMPXCHG_MASK_OUT_PADDING_BITS _ATOMIC_CHOOSE_INTRINSIC(_Order, _Prev_bytes, _InterlockedCompareExchange64, _Atomic_address_as(_Storage), _Atomic_reinterpret_as(_Desired), _Expected_bytes); if (_Prev_bytes == _Expected_bytes) { @@ -761,6 +1055,20 @@ struct _Atomic_storage<_Ty, 8> { // lock-free using 8-byte intrinsics return false; } +#if _HAS_CXX20 + void wait(const _Ty _Expected, const memory_order _Order = memory_order_seq_cst) const noexcept { + _Atomic_wait_direct(this, _Atomic_reinterpret_as(_Expected), _Order); + } + + void notify_one() noexcept { + __std_atomic_notify_one_direct(_STD addressof(_Storage)); + } + + void notify_all() noexcept { + __std_atomic_notify_all_direct(_STD addressof(_Storage)); + } +#endif // _HAS_CXX20 + _Atomic_padded<_Ty> _Storage; }; @@ -856,6 +1164,16 @@ struct _Atomic_storage<_Ty, 16> { // lock-free using 16-byte intrinsics return _Result != 0; } +#if _HAS_CXX20 + void notify_one() noexcept { + __std_atomic_notify_one_indirect(_STD addressof(_Storage)); + } + + void notify_all() noexcept { + __std_atomic_notify_all_indirect(_STD addressof(_Storage)); + } +#endif // _HAS_CXX20 + struct _Int128 { alignas(16) long long _Low; long long _High; @@ -1660,6 +1978,23 @@ public: return this->compare_exchange_strong(_Expected, _Desired, _Combine_cas_memory_orders(_Success, _Failure)); } +#if _HAS_CXX20 + using _Base::wait; + void wait(const _Ty _Expected, const memory_order _Order = memory_order_seq_cst) const volatile noexcept { + const_cast(this)->_Base::wait(_Expected, _Order); + } + + using _Base::notify_one; + void notify_one() volatile noexcept { + const_cast(this)->_Base::notify_one(); + } + + using _Base::notify_all; + void notify_all() volatile noexcept { + const_cast(this)->_Base::notify_all(); + } +#endif // _HAS_CXX20 + operator _Ty() const volatile noexcept { static_assert(_Deprecate_non_lock_free_volatile<_Ty>, "Never fails"); return this->load(); @@ -1946,6 +2281,52 @@ _Ty atomic_fetch_xor_explicit( return _Mem->fetch_xor(_Value, _Order); } +#if _HAS_CXX20 +template +void atomic_wait(const volatile atomic<_Ty>* const _Mem, const typename atomic<_Ty>::value_type _Expected) noexcept +/* strengthened */ { + _Mem->wait(_Expected); +} + +template +void atomic_wait(const atomic<_Ty>* const _Mem, const typename atomic<_Ty>::value_type _Expected) noexcept +/* strengthened */ { + _Mem->wait(_Expected); +} + +template +void atomic_wait_explicit(const volatile atomic<_Ty>* const _Mem, const typename atomic<_Ty>::value_type _Expected, + const memory_order _Order) noexcept /* strengthened */ { + _Mem->wait(_Expected, _Order); +} + +template +void atomic_wait_explicit(const atomic<_Ty>* const _Mem, const typename atomic<_Ty>::value_type _Expected, + const memory_order _Order) noexcept /* strengthened */ { + _Mem->wait(_Expected, _Order); +} + +template +void atomic_notify_one(volatile atomic<_Ty>* const _Mem) noexcept /* strengthened */ { + _Mem->notify_one(); +} + +template +void atomic_notify_one(atomic<_Ty>* const _Mem) noexcept /* strengthened */ { + _Mem->notify_one(); +} + +template +void atomic_notify_all(volatile atomic<_Ty>* const _Mem) noexcept /* strengthened */ { + _Mem->notify_all(); +} + +template +void atomic_notify_all(atomic<_Ty>* const _Mem) noexcept /* strengthened */ { + _Mem->notify_all(); +} +#endif // _HAS_CXX20 + // ATOMIC TYPEDEFS using atomic_bool = atomic; @@ -2041,6 +2422,32 @@ struct atomic_flag { // flag with test-and-set semantics constexpr atomic_flag() noexcept = default; +#if _HAS_CXX20 + void wait(const bool _Expected, const memory_order _Order = memory_order_seq_cst) const noexcept { + _Storage.wait(static_cast(_Expected), _Order); + } + + void wait(const bool _Expected, const memory_order _Order = memory_order_seq_cst) const volatile noexcept { + _Storage.wait(static_cast(_Expected), _Order); + } + + void notify_one() noexcept { + _Storage.notify_one(); + } + + void notify_one() volatile noexcept { + _Storage.notify_one(); + } + + void notify_all() noexcept { + _Storage.notify_all(); + } + + void notify_all() volatile noexcept { + _Storage.notify_all(); + } +#endif // _HAS_CXX20 + #if 1 // TRANSITION, ABI atomic _Storage; #else // ^^^ don't break ABI / break ABI vvv @@ -2069,40 +2476,78 @@ _NODISCARD inline bool atomic_flag_test_explicit(const atomic_flag* const _Flag, } #endif // _HAS_CXX20 -inline bool atomic_flag_test_and_set(atomic_flag* _Flag) noexcept { +inline bool atomic_flag_test_and_set(atomic_flag* const _Flag) noexcept { return _Flag->test_and_set(); } -inline bool atomic_flag_test_and_set(volatile atomic_flag* _Flag) noexcept { +inline bool atomic_flag_test_and_set(volatile atomic_flag* const _Flag) noexcept { return _Flag->test_and_set(); } -inline bool atomic_flag_test_and_set_explicit(atomic_flag* _Flag, memory_order _Order) noexcept { +inline bool atomic_flag_test_and_set_explicit(atomic_flag* const _Flag, const memory_order _Order) noexcept { return _Flag->test_and_set(_Order); } -inline bool atomic_flag_test_and_set_explicit(volatile atomic_flag* _Flag, memory_order _Order) noexcept { +inline bool atomic_flag_test_and_set_explicit(volatile atomic_flag* const _Flag, const memory_order _Order) noexcept { return _Flag->test_and_set(_Order); } -inline void atomic_flag_clear(atomic_flag* _Flag) noexcept { +inline void atomic_flag_clear(atomic_flag* const _Flag) noexcept { _Flag->clear(); } -inline void atomic_flag_clear(volatile atomic_flag* _Flag) noexcept { +inline void atomic_flag_clear(volatile atomic_flag* const _Flag) noexcept { _Flag->clear(); } -inline void atomic_flag_clear_explicit(atomic_flag* _Flag, memory_order _Order) noexcept { +inline void atomic_flag_clear_explicit(atomic_flag* const _Flag, const memory_order _Order) noexcept { _Flag->clear(_Order); } -inline void atomic_flag_clear_explicit(volatile atomic_flag* _Flag, memory_order _Order) noexcept { +inline void atomic_flag_clear_explicit(volatile atomic_flag* const _Flag, const memory_order _Order) noexcept { _Flag->clear(_Order); } +#if _HAS_CXX20 +inline void atomic_flag_wait(const volatile atomic_flag* const _Flag, const bool _Expected) noexcept { + return _Flag->wait(_Expected); +} + +inline void atomic_flag_wait(const atomic_flag* const _Flag, const bool _Expected) noexcept { + return _Flag->wait(_Expected); +} + +inline void atomic_flag_wait_explicit( + const volatile atomic_flag* const _Flag, const bool _Expected, const memory_order _Order) noexcept { + return _Flag->wait(_Expected, _Order); +} + +inline void atomic_flag_wait_explicit( + const atomic_flag* const _Flag, const bool _Expected, const memory_order _Order) noexcept { + return _Flag->wait(_Expected, _Order); +} + +inline void atomic_flag_notify_one(volatile atomic_flag* const _Flag) noexcept { + return _Flag->notify_one(); +} + +inline void atomic_flag_notify_one(atomic_flag* const _Flag) noexcept { + return _Flag->notify_one(); +} + +inline void atomic_flag_notify_all(volatile atomic_flag* const _Flag) noexcept { + return _Flag->notify_all(); +} + +inline void atomic_flag_notify_all(atomic_flag* const _Flag) noexcept { + return _Flag->notify_all(); +} +#endif // _HAS_CXX20 + _STD_END +#undef _CMPXCHG_MASK_OUT_PADDING_BITS + #undef _ATOMIC_CHOOSE_INTRINSIC #undef _ATOMIC_HAS_DCAS diff --git a/stl/inc/bit b/stl/inc/bit index 0aa8f4451d1..2bdeec948fa 100644 --- a/stl/inc/bit +++ b/stl/inc/bit @@ -115,33 +115,14 @@ _NODISCARD constexpr int _Popcount_fallback(_Ty _Val) noexcept { #if defined(_M_IX86) || defined(_M_X64) -// TRANSITION, VS 2019 16.8 Preview 1, intrin0.h will declare __lzcnt* and __popcnt* extern "C" { -__MACHINEX86_X64(unsigned int __lzcnt(unsigned int)) -__MACHINEX86_X64(unsigned short __lzcnt16(unsigned short)) -__MACHINEX64(unsigned __int64 __lzcnt64(unsigned __int64)) -__MACHINEX86_X64(unsigned int __popcnt(unsigned int)) -__MACHINEX86_X64(unsigned short __popcnt16(unsigned short)) -__MACHINEX64(unsigned __int64 __popcnt64(unsigned __int64)) extern int __isa_available; } template -_NODISCARD int _Checked_x86_x64_countl_zero(const _Ty _Val) noexcept { - constexpr int _Digits = numeric_limits<_Ty>::digits; - -#ifndef __AVX2__ - const bool _Have_lzcnt = __isa_available >= __ISA_AVAILABLE_AVX2; - // lzcnt (when it doesn't fall back to bsr) is defined correctly for zero - // bsr has undefined output for zero - if (!_Have_lzcnt && _Val == 0) { - return _Digits; - } -#endif // __AVX2__ +_NODISCARD int _Countl_zero_lzcnt(const _Ty _Val) noexcept { + constexpr int _Digits = numeric_limits<_Ty>::digits; - // We use lzcnt (actually bsr if lzcnt is not supported) now that we know - // we're not zero. We can do this because lzcnt and bsr share the same instruction - // encoding. if constexpr (_Digits <= 16) { return static_cast(__lzcnt16(_Val) - (16 - _Digits)); } else if constexpr (_Digits == 32) { @@ -151,24 +132,65 @@ _NODISCARD int _Checked_x86_x64_countl_zero(const _Ty _Val) noexcept { const unsigned int _High = _Val >> 32; const auto _Low = static_cast(_Val); if (_High == 0) { - return 32 + _Checked_x86_x64_countl_zero(_Low); + return 32 + _Countl_zero_lzcnt(_Low); } else { - return _Checked_x86_x64_countl_zero(_High); + return _Countl_zero_lzcnt(_High); } #else // ^^^ _M_IX86 / !_M_IX86 vvv return static_cast(__lzcnt64(_Val)); #endif // _M_IX86 } - // note: we don't need to call a fallback here because - // all supported x86 processors at least have bsr/bsf +} + +template +_NODISCARD int _Countl_zero_bsr(const _Ty _Val) noexcept { + constexpr int _Digits = numeric_limits<_Ty>::digits; + + unsigned long _Result; + if constexpr (_Digits <= 32) { + if (!_BitScanReverse(&_Result, _Val)) { + return _Digits; + } + } else { +#ifdef _M_IX86 + const unsigned int _High = _Val >> 32; + if (_BitScanReverse(&_Result, _High)) { + return static_cast(31 - _Result); + } + + const auto _Low = static_cast(_Val); + if (!_BitScanReverse(&_Result, _Low)) { + return _Digits; + } +#else // ^^^ _M_IX86 / !_M_IX86 vvv + if (!_BitScanReverse64(&_Result, _Val)) { + return _Digits; + } +#endif // _M_IX86 + } + return static_cast(_Digits - 1 - _Result); +} + +template +_NODISCARD int _Checked_x86_x64_countl_zero(const _Ty _Val) noexcept { +#ifdef __AVX2__ + return _Countl_zero_lzcnt(_Val); +#else // __AVX2__ + const bool _Definitely_have_lzcnt = __isa_available >= __ISA_AVAILABLE_AVX2; + if (_Definitely_have_lzcnt) { + return _Countl_zero_lzcnt(_Val); + } else { + return _Countl_zero_bsr(_Val); + } +#endif // __AVX2__ } template _NODISCARD int _Checked_x86_x64_popcount(const _Ty _Val) noexcept { - constexpr int _Digits = numeric_limits<_Ty>::digits; + constexpr int _Digits = numeric_limits<_Ty>::digits; #ifndef __AVX__ - const bool _Have_popcnt = __isa_available >= __ISA_AVAILABLE_SSE42; - if (!_Have_popcnt) { + const bool _Definitely_have_popcnt = __isa_available >= __ISA_AVAILABLE_SSE42; + if (!_Definitely_have_popcnt) { return _Popcount_fallback(_Val); } #endif // !defined(__AVX__) diff --git a/stl/inc/cmath b/stl/inc/cmath index cf07a5b9424..cdc55d438d4 100644 --- a/stl/inc/cmath +++ b/stl/inc/cmath @@ -12,6 +12,10 @@ #include #include +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 + #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) @@ -1238,13 +1242,12 @@ _NODISCARD auto hypot(const _Ty1 _Dx, const _Ty2 _Dy, const _Ty3 _Dz) { #if _HAS_CXX20 // FUNCTION lerp -// TRANSITION, P0553: lerp is not yet constexpr template -_NODISCARD /* constexpr */ _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _Ty _ArgT) noexcept { +_NODISCARD constexpr _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, const _Ty _ArgT) noexcept { // on a line intersecting {(0.0, _ArgA), (1.0, _ArgB)}, return the Y value for X == _ArgT - const int _Finite_mask = (int{isfinite(_ArgA)} << 2) | (int{isfinite(_ArgB)} << 1) | int{isfinite(_ArgT)}; - if (_Finite_mask == 0b111) { + const bool _T_is_finite = _Is_finite(_ArgT); + if (_T_is_finite && _Is_finite(_ArgA) && _Is_finite(_ArgB)) { // 99% case, put it first; this block comes from P0811R3 if ((_ArgA <= 0 && _ArgB >= 0) || (_ArgA >= 0 && _ArgB <= 0)) { // exact, monotonic, bounded, determinate, and (for _ArgA == _ArgB == 0) consistent: @@ -1272,96 +1275,61 @@ _NODISCARD /* constexpr */ _Ty _Common_lerp(const _Ty _ArgA, const _Ty _ArgB, co return _Candidate; } - if (isnan(_ArgA)) { - return _ArgA; - } - - if (isnan(_ArgB)) { - return _ArgB; - } - - if (isnan(_ArgT)) { - return _ArgT; - } - - switch (_Finite_mask) { - case 0b000: - // All values are infinities - if (_ArgT >= 1) { - return _ArgB; - } - - return _ArgA; - case 0b010: - case 0b100: - case 0b110: - // _ArgT is an infinity; return infinity in the "direction" of _ArgA and _ArgB - return _ArgT * (_ArgB - _ArgA); - case 0b001: - // Here _ArgA and _ArgB are infinities - if (_ArgA == _ArgB) { - // same sign, so T doesn't matter - return _ArgA; - } - - // Opposite signs, choose the "infinity direction" according to T if it makes sense. - if (_ArgT <= 0) { + if (_STD is_constant_evaluated()) { + if (_Is_nan(_ArgA)) { return _ArgA; } - if (_ArgT >= 1) { + if (_Is_nan(_ArgB)) { return _ArgB; } - // Interpolating between infinities of opposite signs doesn't make sense, NaN - if constexpr (sizeof(_Ty) == sizeof(float)) { - return __builtin_nanf("0"); - } else { - return __builtin_nan("0"); - } - case 0b011: - // _ArgA is an infinity but _ArgB is not - if (_ArgT == 1) { - return _ArgB; + if (_Is_nan(_ArgT)) { + return _ArgT; } - - if (_ArgT < 1) { - // towards the infinity, return it - return _ArgA; + } else { + // raise FE_INVALID if at least one of _ArgA, _ArgB, and _ArgT is signaling NaN + if (_Is_nan(_ArgA) || _Is_nan(_ArgB)) { + return (_ArgA + _ArgB) + _ArgT; } - // away from the infinity - return -_ArgA; - case 0b101: - // _ArgA is finite and _ArgB is an infinity - if (_ArgT == 0) { - return _ArgA; + if (_Is_nan(_ArgT)) { + return _ArgT + _ArgT; } + } - if (_ArgT > 0) { - // toward the infinity - return _ArgB; + if (_T_is_finite) { + // _ArgT is finite, _ArgA and/or _ArgB is infinity + if (_ArgT < 0) { + // if _ArgT < 0: return infinity in the "direction" of _ArgA if that exists, NaN otherwise + return _ArgA - _ArgB; + } else if (_ArgT <= 1) { + // if _ArgT == 0: return _ArgA (infinity) if _ArgB is finite, NaN otherwise + // if 0 < _ArgT < 1: return infinity "between" _ArgA and _ArgB if that exists, NaN otherwise + // if _ArgT == 1: return _ArgB (infinity) if _ArgA is finite, NaN otherwise + return _ArgT * _ArgB + (1 - _ArgT) * _ArgA; + } else { + // if _ArgT > 1: return infinity in the "direction" of _ArgB if that exists, NaN otherwise + return _ArgB - _ArgA; } - - return -_ArgB; - case 0b111: // impossible; handled in fast path - default: - _CSTD abort(); + } else { + // _ArgT is an infinity; return infinity in the "direction" of _ArgA and _ArgB if that exists, NaN otherwise + return _ArgT * (_ArgB - _ArgA); } } // As of 2019-06-17 it is unclear whether the "sufficient additional overloads" clause is intended to target lerp; // LWG-3223 is pending. -_NODISCARD /* constexpr */ inline float lerp(const float _ArgA, const float _ArgB, const float _ArgT) noexcept { +_NODISCARD constexpr inline float lerp(const float _ArgA, const float _ArgB, const float _ArgT) noexcept { return _Common_lerp(_ArgA, _ArgB, _ArgT); } -_NODISCARD /* constexpr */ inline double lerp(const double _ArgA, const double _ArgB, const double _ArgT) noexcept { +_NODISCARD constexpr inline double lerp(const double _ArgA, const double _ArgB, const double _ArgT) noexcept { return _Common_lerp(_ArgA, _ArgB, _ArgT); } -_NODISCARD /* constexpr */ inline long double lerp( +_NODISCARD constexpr inline long double lerp( const long double _ArgA, const long double _ArgB, const long double _ArgT) noexcept { return _Common_lerp(_ArgA, _ArgB, _ArgT); } diff --git a/stl/inc/compare b/stl/inc/compare index 717e7b4b789..b50e3518cc2 100644 --- a/stl/inc/compare +++ b/stl/inc/compare @@ -35,15 +35,7 @@ enum class _Compare_ord : _Compare_t { less = -1, greater = 1 }; enum class _Compare_ncmp : _Compare_t { unordered = -128 }; // CLASS partial_ordering -class partial_ordering { -public: - _NODISCARD constexpr explicit partial_ordering(const _Compare_eq _Value_) noexcept - : _Value(static_cast<_Compare_t>(_Value_)) {} - _NODISCARD constexpr explicit partial_ordering(const _Compare_ord _Value_) noexcept - : _Value(static_cast<_Compare_t>(_Value_)) {} - _NODISCARD constexpr explicit partial_ordering(const _Compare_ncmp _Value_) noexcept - : _Value(static_cast<_Compare_t>(_Value_)) {} - +struct partial_ordering { static const partial_ordering less; static const partial_ordering equivalent; static const partial_ordering greater; @@ -98,32 +90,25 @@ public: // The stored value is either less (0xff), equivalent (0x00), greater (0x01), or unordered (0x80). // Subtracting from 0 produces either 0x01, 0x00, 0xff, or 0x80. Note that the effect is to // exchange less for greater (and vice versa), while leaving equivalent and unordered unchanged. - return partial_ordering{static_cast<_Compare_ord>(0 - static_cast(_Val._Value))}; + return {static_cast<_Compare_t>(0 - static_cast(_Val._Value))}; } -private: _Compare_t _Value; }; -inline constexpr partial_ordering partial_ordering::less(_Compare_ord::less); -inline constexpr partial_ordering partial_ordering::equivalent(_Compare_eq::equivalent); -inline constexpr partial_ordering partial_ordering::greater(_Compare_ord::greater); -inline constexpr partial_ordering partial_ordering::unordered(_Compare_ncmp::unordered); +inline constexpr partial_ordering partial_ordering::less{static_cast<_Compare_t>(_Compare_ord::less)}; +inline constexpr partial_ordering partial_ordering::equivalent{static_cast<_Compare_t>(_Compare_eq::equivalent)}; +inline constexpr partial_ordering partial_ordering::greater{static_cast<_Compare_t>(_Compare_ord::greater)}; +inline constexpr partial_ordering partial_ordering::unordered{static_cast<_Compare_t>(_Compare_ncmp::unordered)}; // CLASS weak_ordering -class weak_ordering { -public: - _NODISCARD constexpr explicit weak_ordering(const _Compare_eq _Value_) noexcept - : _Value(static_cast<_Compare_t>(_Value_)) {} - _NODISCARD constexpr explicit weak_ordering(const _Compare_ord _Value_) noexcept - : _Value(static_cast<_Compare_t>(_Value_)) {} - +struct weak_ordering { static const weak_ordering less; static const weak_ordering equivalent; static const weak_ordering greater; constexpr operator partial_ordering() const noexcept { - return partial_ordering{static_cast<_Compare_ord>(_Value)}; + return {static_cast<_Compare_t>(_Value)}; } _NODISCARD friend constexpr bool operator==(const weak_ordering _Val, _Literal_zero) noexcept { @@ -169,36 +154,29 @@ public: } _NODISCARD friend constexpr weak_ordering operator<=>(_Literal_zero, const weak_ordering _Val) noexcept { - return weak_ordering{static_cast<_Compare_ord>(-_Val._Value)}; + return {static_cast<_Compare_t>(-_Val._Value)}; } -private: _Compare_t _Value; }; -inline constexpr weak_ordering weak_ordering::less(_Compare_ord::less); -inline constexpr weak_ordering weak_ordering::equivalent(_Compare_eq::equivalent); -inline constexpr weak_ordering weak_ordering::greater(_Compare_ord::greater); +inline constexpr weak_ordering weak_ordering::less{static_cast<_Compare_t>(_Compare_ord::less)}; +inline constexpr weak_ordering weak_ordering::equivalent{static_cast<_Compare_t>(_Compare_eq::equivalent)}; +inline constexpr weak_ordering weak_ordering::greater{static_cast<_Compare_t>(_Compare_ord::greater)}; // CLASS strong_ordering -class strong_ordering { -public: - _NODISCARD constexpr explicit strong_ordering(const _Compare_eq _Value_) noexcept - : _Value(static_cast<_Compare_t>(_Value_)) {} - _NODISCARD constexpr explicit strong_ordering(const _Compare_ord _Value_) noexcept - : _Value(static_cast<_Compare_t>(_Value_)) {} - +struct strong_ordering { static const strong_ordering less; static const strong_ordering equal; static const strong_ordering equivalent; static const strong_ordering greater; constexpr operator partial_ordering() const noexcept { - return partial_ordering{static_cast<_Compare_ord>(_Value)}; + return {static_cast<_Compare_t>(_Value)}; } constexpr operator weak_ordering() const noexcept { - return weak_ordering{static_cast<_Compare_ord>(_Value)}; + return {static_cast<_Compare_t>(_Value)}; } _NODISCARD friend constexpr bool operator==(const strong_ordering _Val, _Literal_zero) noexcept { @@ -244,17 +222,16 @@ public: } _NODISCARD friend constexpr strong_ordering operator<=>(_Literal_zero, const strong_ordering _Val) noexcept { - return strong_ordering{static_cast<_Compare_ord>(-_Val._Value)}; + return {static_cast<_Compare_t>(-_Val._Value)}; } -private: _Compare_t _Value; }; -inline constexpr strong_ordering strong_ordering::less(_Compare_ord::less); -inline constexpr strong_ordering strong_ordering::equal(_Compare_eq::equal); -inline constexpr strong_ordering strong_ordering::equivalent(_Compare_eq::equivalent); -inline constexpr strong_ordering strong_ordering::greater(_Compare_ord::greater); +inline constexpr strong_ordering strong_ordering::less{static_cast<_Compare_t>(_Compare_ord::less)}; +inline constexpr strong_ordering strong_ordering::equal{static_cast<_Compare_t>(_Compare_eq::equal)}; +inline constexpr strong_ordering strong_ordering::equivalent{static_cast<_Compare_t>(_Compare_eq::equivalent)}; +inline constexpr strong_ordering strong_ordering::greater{static_cast<_Compare_t>(_Compare_ord::greater)}; // FUNCTION is_eq _NODISCARD constexpr bool is_eq(const partial_ordering _Comp) noexcept { diff --git a/stl/inc/concepts b/stl/inc/concepts index 2cc8f904bad..4da6a23f497 100644 --- a/stl/inc/concepts +++ b/stl/inc/concepts @@ -79,7 +79,7 @@ concept integral = is_integral_v<_Ty>; // CONCEPT signed_integral template -concept signed_integral = integral<_Ty> && _Ty(-1) < _Ty(0); +concept signed_integral = integral<_Ty> && static_cast<_Ty>(-1) < static_cast<_Ty>(0); // CONCEPT unsigned_integral template diff --git a/stl/inc/deque b/stl/inc/deque index 08f1f6fb47d..00ffc768991 100644 --- a/stl/inc/deque +++ b/stl/inc/deque @@ -1292,6 +1292,10 @@ public: auto _Count = static_cast(_Last - _First); #endif // _ITERATOR_DEBUG_LEVEL == 2 + if (_Count == 0) { + return _First; + } + if (_Off < static_cast(end() - _Last)) { // closer to front _STD move_backward(begin(), _First, _Last); // copy over hole for (; 0 < _Count; --_Count) { diff --git a/stl/inc/execution b/stl/inc/execution index d632e4c4788..427ea5d0dac 100644 --- a/stl/inc/execution +++ b/stl/inc/execution @@ -29,11 +29,6 @@ _STL_DISABLE_CLANG_WARNINGS #undef new _EXTERN_C -// If on Windows XP, returns 1 (disabling parallelism); otherwise, returns the number of hardware threads available. -_NODISCARD unsigned int __stdcall __std_parallel_algorithms_hw_threads() noexcept; - -// Windows Vista thread pool interface; __std_parallel_algorithms_hw_threads must be called on the current -// thread before calling any of the below. #ifdef _M_CEE using __std_TP_WORK = void; using __std_TP_CALLBACK_INSTANCE = void; @@ -48,6 +43,8 @@ using __std_PTP_WORK = __std_TP_WORK*; using __std_PTP_CALLBACK_INSTANCE = __std_TP_CALLBACK_INSTANCE*; using __std_PTP_CALLBACK_ENVIRON = __std_TP_CALLBACK_ENVIRON*; +_NODISCARD unsigned int __stdcall __std_parallel_algorithms_hw_threads() noexcept; + using __std_PTP_WORK_CALLBACK = void(__stdcall*)( _Inout_ __std_PTP_CALLBACK_INSTANCE, _Inout_opt_ void*, _Inout_ __std_PTP_WORK); @@ -75,42 +72,71 @@ constexpr size_t _Still_active = static_cast(-1); // EXECUTION POLICIES namespace execution { - class sequenced_policy { // request for sequential execution with termination + class sequenced_policy { + // indicates support for only sequential execution, and requests termination on exceptions public: using _Standard_execution_policy = int; static constexpr bool _Parallelize = false; + static constexpr bool _Ivdep = false; }; inline constexpr sequenced_policy seq{/* unspecified */}; - class parallel_policy { // request for parallel execution with termination + class parallel_policy { + // indicates support by element access functions for parallel execution with parallel forward progress + // guarantees, and requests termination on exceptions public: using _Standard_execution_policy = int; static constexpr bool _Parallelize = true; + static constexpr bool _Ivdep = true; }; inline constexpr parallel_policy par{/* unspecified */}; class parallel_unsequenced_policy { - // request for parallel execution without thread identity with termination + // indicates support by element access functions for parallel execution with weakly parallel forward progress + // guarantees, and requests termination on exceptions + // // (at this time, equivalent to parallel_policy) public: using _Standard_execution_policy = int; static constexpr bool _Parallelize = true; + static constexpr bool _Ivdep = true; }; inline constexpr parallel_unsequenced_policy par_unseq{/* unspecified */}; + +#if _HAS_CXX20 + class unsequenced_policy { + // indicates support by element access functions for weakly parallel forward progress guarantees, and for + // executing interleaved on the same thread, and requests termination on exceptions + // + // (at this time, equivalent to sequenced_policy except for the for_each family) + public: + using _Standard_execution_policy = int; + static constexpr bool _Parallelize = false; + static constexpr bool _Ivdep = true; + }; + + inline constexpr unsequenced_policy unseq{/* unspecified */}; +#endif // _HAS_CXX20 + } // namespace execution +// All of the above are execution policies: template <> -struct is_execution_policy : true_type {}; // sequenced_policy is an execution policy +struct is_execution_policy : true_type {}; template <> -struct is_execution_policy : true_type {}; // parallel_policy is an execution policy +struct is_execution_policy : true_type {}; template <> -struct is_execution_policy : true_type { -}; // parallel_unsequenced_policy is an execution policy +struct is_execution_policy : true_type {}; + +#if _HAS_CXX20 +template <> +struct is_execution_policy : true_type {}; +#endif // _HAS_CXX20 // STRUCT _Parallelism_resources_exhausted struct _Parallelism_resources_exhausted : exception { @@ -1216,6 +1242,8 @@ void for_each(_ExPo&&, _FwdIt _First, _FwdIt _Last, _Fn _Func) noexcept /* termi } } + _For_each_ivdep(_UFirst, _ULast, _Pass_fn(_Func)); + } else if constexpr (remove_reference_t<_ExPo>::_Ivdep) { _For_each_ivdep(_UFirst, _ULast, _Pass_fn(_Func)); } else { for (; _UFirst != _ULast; ++_UFirst) { @@ -1258,6 +1286,8 @@ _FwdIt for_each_n(_ExPo&&, _FwdIt _First, const _Diff _Count_raw, _Fn _Func) noe _CATCH_END } + _Seek_wrapped(_First, _For_each_n_ivdep(_UFirst, _Count, _Pass_fn(_Func))); + } else if constexpr (remove_reference_t<_ExPo>::_Ivdep) { _Seek_wrapped(_First, _For_each_n_ivdep(_UFirst, _Count, _Pass_fn(_Func))); } else { for (; 0 < _Count; --_Count, (void) ++_UFirst) { @@ -2281,17 +2311,6 @@ _NODISCARD _FwdIt search_n(_ExPo&&, const _FwdIt _First, _FwdIt _Last, const _Di } // PARALLEL FUNCTION TEMPLATE transform -template -_FwdIt2 _Transform_ivdep(_FwdIt1 _First, const _FwdIt1 _Last, _FwdIt2 _Dest, _Fn _Func) { - // unary op transform with independent loop bodies -#pragma loop(ivdep) - for (; _First != _Last; ++_First, (void) ++_Dest) { - *_Dest = _Func(*_First); - } - - return _Dest; -} - template struct _Static_partitioned_unary_transform2 { using _Diff = _Common_diff_t<_FwdIt1, _FwdIt2>; @@ -2311,7 +2330,7 @@ struct _Static_partitioned_unary_transform2 { const auto _Key = _Team._Get_next_key(); if (_Key) { const auto _Source = _Source_basis._Get_chunk(_Key); - _Transform_ivdep(_Source._First, _Source._Last, _Dest_basis._Get_chunk(_Key)._First, _Func); + _STD transform(_Source._First, _Source._Last, _Dest_basis._Get_chunk(_Key)._First, _Func); return _Cancellation_status::_Running; } @@ -2349,12 +2368,12 @@ _FwdIt2 transform(_ExPo&&, const _FwdIt1 _First, const _FwdIt1 _Last, _FwdIt2 _D _CATCH_END } - _Seek_wrapped(_Dest, _Transform_ivdep(_UFirst, _ULast, _UDest, _Pass_fn(_Func))); + _Seek_wrapped(_Dest, _STD transform(_UFirst, _ULast, _UDest, _Pass_fn(_Func))); return _Dest; } else { _Seek_wrapped( - _Dest, _Transform_ivdep(_UFirst, _ULast, - _Get_unwrapped_n(_Dest, _Idl_distance<_FwdIt1>(_UFirst, _ULast)), _Pass_fn(_Func))); + _Dest, _STD transform(_UFirst, _ULast, _Get_unwrapped_n(_Dest, _Idl_distance<_FwdIt1>(_UFirst, _ULast)), + _Pass_fn(_Func))); return _Dest; } } else { @@ -2364,17 +2383,6 @@ _FwdIt2 transform(_ExPo&&, const _FwdIt1 _First, const _FwdIt1 _Last, _FwdIt2 _D } } -template -_FwdIt3 _Transform_ivdep(_FwdIt1 _First1, const _FwdIt1 _Last1, _FwdIt2 _First2, _FwdIt3 _Dest, _Fn _Func) { - // binary op transform with independent loop bodies -#pragma loop(ivdep) - for (; _First1 != _Last1; ++_First1, (void) ++_First2, ++_Dest) { - *_Dest = _Func(*_First1, *_First2); - } - - return _Dest; -} - template struct _Static_partitioned_binary_transform2 { using _Diff = _Common_diff_t<_FwdIt1, _FwdIt2, _FwdIt3>; @@ -2396,7 +2404,7 @@ struct _Static_partitioned_binary_transform2 { const auto _Key = _Team._Get_next_key(); if (_Key) { const auto _Source1 = _Source1_basis._Get_chunk(_Key); - _Transform_ivdep(_Source1._First, _Source1._Last, _Source2_basis._Get_chunk(_Key)._First, + _STD transform(_Source1._First, _Source1._Last, _Source2_basis._Get_chunk(_Key)._First, _Dest_basis._Get_chunk(_Key)._First, _Func); return _Cancellation_status::_Running; } @@ -2442,11 +2450,11 @@ _FwdIt3 transform(_ExPo&&, const _FwdIt1 _First1, const _FwdIt1 _Last1, const _F _CATCH_END } - _Seek_wrapped(_Dest, _Transform_ivdep(_UFirst1, _ULast1, _UFirst2, _UDest, _Pass_fn(_Func))); + _Seek_wrapped(_Dest, _STD transform(_UFirst1, _ULast1, _UFirst2, _UDest, _Pass_fn(_Func))); return _Dest; } else { const auto _Count = _Idl_distance<_FwdIt1>(_UFirst1, _ULast1); - _Seek_wrapped(_Dest, _Transform_ivdep(_UFirst1, _ULast1, _Get_unwrapped_n(_First2, _Count), + _Seek_wrapped(_Dest, _STD transform(_UFirst1, _ULast1, _Get_unwrapped_n(_First2, _Count), _Get_unwrapped_n(_Dest, _Count), _Pass_fn(_Func))); return _Dest; } @@ -3590,20 +3598,6 @@ _FwdIt partition(_ExPo&&, _FwdIt _First, const _FwdIt _Last, _Pr _Pred) noexcept } // PARALLEL FUNCTION TEMPLATE set_intersection -template -struct _Storage_for { - // uninitialized space to store a _Ty - alignas(_Ty) unsigned char _Storage[sizeof(_Ty)]; - - _Storage_for() = default; - _Storage_for(const _Storage_for&) = delete; - _Storage_for& operator=(const _Storage_for&) = delete; - - _Ty& _Ref() { - return reinterpret_cast<_Ty&>(_Storage); - } -}; - inline constexpr unsigned char _Local_available = 1; inline constexpr unsigned char _Sum_available = 2; diff --git a/stl/inc/experimental/coroutine b/stl/inc/experimental/coroutine index 5c01edb2903..a4068e07191 100644 --- a/stl/inc/experimental/coroutine +++ b/stl/inc/experimental/coroutine @@ -32,10 +32,10 @@ support Clang. You can define _SILENCE_CLANG_COROUTINE_MESSAGE to silence this m unsupported. #endif // defined(__clang__) && !defined(_SILENCE_CLANG_COROUTINE_MESSAGE) -#if defined(_MSC_VER) && defined(__cpp_impl_coroutine) +#ifdef __cpp_impl_coroutine #error The and headers are only supported \ with /await and implement pre-C++20 coroutine support. Use for standard C++20 coroutines. -#endif // defined(_MSC_VER) && defined(__cpp_impl_coroutine) +#endif // __cpp_impl_coroutine // intrinsics used in implementation of coroutine_handle extern "C" size_t _coro_resume(void*); diff --git a/stl/inc/experimental/resumable b/stl/inc/experimental/resumable index 353538d8a0a..7f308dd3ecf 100644 --- a/stl/inc/experimental/resumable +++ b/stl/inc/experimental/resumable @@ -30,7 +30,7 @@ _STD_BEGIN namespace experimental { -#if !defined(__EDG__) && _MSC_VER >= 1928 +#ifndef __EDG__ // STRUCT noop_coroutine_promise struct noop_coroutine_promise {}; @@ -75,7 +75,7 @@ namespace experimental { // Returns a handle to a coroutine that has no observable effects when resumed or destroyed. return noop_coroutine_handle{}; } -#endif // !defined(__EDG__) && _MSC_VER >= 1928 +#endif // ^^^ !defined(__EDG__) ^^^ } // namespace experimental _STD_END diff --git a/stl/inc/functional b/stl/inc/functional index 42f68a52603..05b969b5ef2 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -638,7 +638,7 @@ _NODISCARD const_mem_fun1_ref_t<_Result, _Ty, _Arg> mem_fun_ref(_Result (_Ty::*_ // FUNCTION TEMPLATE mem_fn template -class _Mem_fn : public _Weak_types<_Memptr>::type { +class _Mem_fn : public _Weak_types<_Memptr> { private: _Memptr _Pm; @@ -1449,7 +1449,7 @@ template struct _Binder_result_type { // provide result_type (sometimes) using _Decayed = decay_t<_Fx>; - using _All_weak_types = typename _Weak_types<_Decayed>::type; + using _All_weak_types = _Weak_types<_Decayed>; using type = conditional_t, _Weak_result_type<_All_weak_types>, _Forced_result_type<_Ret>>; diff --git a/stl/inc/limits b/stl/inc/limits index 67a18dd7173..01bb53bab0b 100644 --- a/stl/inc/limits +++ b/stl/inc/limits @@ -1041,15 +1041,12 @@ _NODISCARD constexpr int _Countr_zero_fallback(const _Ty _Val) noexcept { } #if defined(_M_IX86) || defined(_M_X64) -// TRANSITION, VS 2019 16.8 Preview 1, intrin0.h will declare _tzcnt* extern "C" { extern int __isa_available; #ifdef __clang__ #define _TZCNT_U32 __builtin_ia32_tzcnt_u32 #define _TZCNT_U64 __builtin_ia32_tzcnt_u64 #else // ^^^ __clang__ / !__clang__ vvv -__MACHINEX86_X64(unsigned int _tzcnt_u32(unsigned int)) -__MACHINEX64(unsigned __int64 _tzcnt_u64(unsigned __int64)); #define _TZCNT_U32 _tzcnt_u32 #define _TZCNT_U64 _tzcnt_u64 #endif // __clang__ @@ -1061,8 +1058,8 @@ _NODISCARD int _Checked_x86_x64_countr_zero(const _Ty _Val) noexcept { constexpr _Ty _Max = (numeric_limits<_Ty>::max)(); #ifndef __AVX2__ - const bool _Have_tzcnt = __isa_available >= __ISA_AVAILABLE_AVX2; - if (!_Have_tzcnt && _Val == 0) { + const bool _Definitely_have_tzcnt = __isa_available >= __ISA_AVAILABLE_AVX2; + if (!_Definitely_have_tzcnt && _Val == 0) { return _Digits; } #endif // __AVX2__ diff --git a/stl/inc/memory b/stl/inc/memory index 5ee7a127acc..a273a674b99 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -794,6 +794,40 @@ protected: } } + template + void _Weakly_convert_lvalue_avoiding_expired_conversions(const _Ptr_base<_Ty2>& _Other) noexcept { + // implement weak_ptr's copy converting ctor + if (_Other._Rep) { + _Rep = _Other._Rep; // always share ownership + _Rep->_Incwref(); + + if (_Rep->_Incref_nz()) { + _Ptr = _Other._Ptr; // keep resource alive during conversion, handling virtual inheritance + _Rep->_Decref(); + } else { + _STL_INTERNAL_CHECK(!_Ptr); + } + } else { + _STL_INTERNAL_CHECK(!_Ptr && !_Rep); + } + } + + template + void _Weakly_convert_rvalue_avoiding_expired_conversions(_Ptr_base<_Ty2>&& _Other) noexcept { + // implement weak_ptr's move converting ctor + _Rep = _Other._Rep; // always transfer ownership + _Other._Rep = nullptr; + + if (_Rep && _Rep->_Incref_nz()) { + _Ptr = _Other._Ptr; // keep resource alive during conversion, handling virtual inheritance + _Rep->_Decref(); + } else { + _STL_INTERNAL_CHECK(!_Ptr); + } + + _Other._Ptr = nullptr; + } + void _Incwref() const noexcept { if (_Rep) { _Rep->_Incwref(); @@ -2255,30 +2289,29 @@ _NODISCARD enable_if_t, shared_ptr<_Ty>> allocate_shared template class weak_ptr : public _Ptr_base<_Ty> { // class for pointer to reference counted resource public: - constexpr weak_ptr() noexcept {} // construct empty weak_ptr object + constexpr weak_ptr() noexcept {} - weak_ptr(const weak_ptr& _Other) noexcept { // construct weak_ptr object for resource pointed to by _Other - this->_Weakly_construct_from(_Other); + weak_ptr(const weak_ptr& _Other) noexcept { + this->_Weakly_construct_from(_Other); // same type, no conversion } template ::value, int> = 0> - weak_ptr(const shared_ptr<_Ty2>& _Other) noexcept { // construct weak_ptr object for resource owned by _Other - this->_Weakly_construct_from(_Other); + weak_ptr(const shared_ptr<_Ty2>& _Other) noexcept { + this->_Weakly_construct_from(_Other); // shared_ptr keeps resource alive during conversion } template ::value, int> = 0> - weak_ptr(const weak_ptr<_Ty2>& _Other) noexcept { // construct weak_ptr object for resource pointed to by _Other - this->_Weakly_construct_from(_Other.lock()); + weak_ptr(const weak_ptr<_Ty2>& _Other) noexcept { + this->_Weakly_convert_lvalue_avoiding_expired_conversions(_Other); } - weak_ptr(weak_ptr&& _Other) noexcept { // move construct from _Other + weak_ptr(weak_ptr&& _Other) noexcept { this->_Move_construct_from(_STD move(_Other)); } template ::value, int> = 0> - weak_ptr(weak_ptr<_Ty2>&& _Other) noexcept { // move construct from _Other - this->_Weakly_construct_from(_Other.lock()); - _Other.reset(); + weak_ptr(weak_ptr<_Ty2>&& _Other) noexcept { + this->_Weakly_convert_rvalue_avoiding_expired_conversions(_STD move(_Other)); } ~weak_ptr() noexcept { @@ -3067,6 +3100,15 @@ _CXX20_DEPRECATE_OLD_SHARED_PTR_ATOMIC_SUPPORT bool atomic_compare_exchange_stro template class alignas(2 * sizeof(void*)) _Atomic_ptr_base { // overalignment is to allow potential future use of cmpxchg16b + + static_assert(alignof(_Ref_count_base) >= (1 << 2), "Two bits don't fit as low bits"); + + static constexpr uintptr_t _Lock_mask = 3; + static constexpr uintptr_t _Not_locked = 0; + static constexpr uintptr_t _Locked_notify_not_needed = 1; + static constexpr uintptr_t _Locked_notify_needed = 2; + static constexpr uintptr_t _Ptr_value_mask = ~_Lock_mask; + protected: constexpr _Atomic_ptr_base() noexcept = default; @@ -3074,23 +3116,65 @@ protected: : _Ptr(_Px), _Repptr(reinterpret_cast(_Ref)) {} _NODISCARD _Ref_count_base* _Lock_and_load() const noexcept { - constexpr uintptr_t _Low_bit = 1; - uintptr_t _Rep = _Repptr.load(memory_order::relaxed); + uintptr_t _Rep = _Repptr.load(memory_order_relaxed); for (;;) { - _Rep &= ~_Low_bit; - if (_Repptr.compare_exchange_weak(_Rep, _Rep | _Low_bit)) { - return reinterpret_cast<_Ref_count_base*>(_Rep); + switch (_Rep & _Lock_mask) { + case _Not_locked: // Can try to lock now + if (_Repptr.compare_exchange_weak(_Rep, _Rep | _Locked_notify_not_needed)) { + return reinterpret_cast<_Ref_count_base*>(_Rep); + } + _YIELD_PROCESSOR(); + break; + + case _Locked_notify_not_needed: // Try to set "notify needed" and wait + if (!_Repptr.compare_exchange_weak(_Rep, (_Rep & _Ptr_value_mask) | _Locked_notify_needed)) { + // Failed to put notify needed flag on, try again + _YIELD_PROCESSOR(); + break; + } + _Rep = (_Rep & _Ptr_value_mask) | _Locked_notify_needed; + [[fallthrough]]; + + case _Locked_notify_needed: // "Notify needed" is already set, just wait + _Repptr.wait(_Rep, memory_order_relaxed); + _Rep = _Repptr.load(memory_order_relaxed); + break; + + default: // Unrecognized bit pattern + _CSTD abort(); } - - _YIELD_PROCESSOR(); } } void _Store_and_unlock(_Ref_count_base* const _Value) const noexcept { - _Repptr.store(reinterpret_cast(_Value)); + uintptr_t _Rep = _Repptr.exchange(reinterpret_cast(_Value)); + if ((_Rep & _Lock_mask) == _Locked_notify_needed) { + // As we don't count waiters, every waiter is notified, and then some may re-request notification + _Repptr.notify_all(); + } + } + + void _Wait(_Ty* _Old, memory_order) const noexcept { + for (;;) { + auto _Rep = _Lock_and_load(); + bool _Equal = _Ptr.load(memory_order_relaxed) == _Old; + _Store_and_unlock(_Rep); + if (!_Equal) { + break; + } + __std_atomic_wait_direct(&_Ptr, &_Old, sizeof(_Old), _Atomic_wait_no_timeout); + } } - _Ty* _Ptr = nullptr; + void notify_one() noexcept { + _Ptr.notify_one(); + } + + void notify_all() noexcept { + _Ptr.notify_all(); + } + + atomic<_Ty*> _Ptr{nullptr}; mutable atomic _Repptr{0}; }; @@ -3108,19 +3192,21 @@ public: return false; } - void store(shared_ptr<_Ty> _Value, const memory_order _Order = memory_order::seq_cst) noexcept { + void store(shared_ptr<_Ty> _Value, const memory_order _Order = memory_order_seq_cst) noexcept { _Check_store_memory_order(_Order); const auto _Rep = this->_Lock_and_load(); - _STD swap(this->_Ptr, _Value._Ptr); + _Ty* const _Tmp = _Value._Ptr; + _Value._Ptr = this->_Ptr.load(memory_order_relaxed); + this->_Ptr.store(_Tmp, memory_order_relaxed); this->_Store_and_unlock(_Value._Rep); _Value._Rep = _Rep; } - _NODISCARD shared_ptr<_Ty> load(const memory_order _Order = memory_order::seq_cst) const noexcept { + _NODISCARD shared_ptr<_Ty> load(const memory_order _Order = memory_order_seq_cst) const noexcept { _Check_load_memory_order(_Order); shared_ptr<_Ty> _Result; const auto _Rep = this->_Lock_and_load(); - _Result._Ptr = this->_Ptr; + _Result._Ptr = this->_Ptr.load(memory_order_relaxed); _Result._Rep = _Rep; _Result._Incref(); this->_Store_and_unlock(_Rep); @@ -3131,12 +3217,12 @@ public: return load(); } - shared_ptr<_Ty> exchange(shared_ptr<_Ty> _Value, const memory_order _Order = memory_order::seq_cst) noexcept { + shared_ptr<_Ty> exchange(shared_ptr<_Ty> _Value, const memory_order _Order = memory_order_seq_cst) noexcept { _Check_memory_order(_Order); shared_ptr<_Ty> _Result; _Result._Rep = this->_Lock_and_load(); - _Result._Ptr = this->_Ptr; - this->_Ptr = _Value._Ptr; + _Result._Ptr = this->_Ptr.load(memory_order_relaxed); + this->_Ptr.store(_Value._Ptr, memory_order_relaxed); this->_Store_and_unlock(_Value._Rep); _Value._Ptr = nullptr; // ownership of _Value ref has been given to this, silence decrement _Value._Rep = nullptr; @@ -3154,22 +3240,24 @@ public: } bool compare_exchange_weak(shared_ptr<_Ty>& _Expected, shared_ptr<_Ty> _Desired, - const memory_order _Order = memory_order::seq_cst) noexcept { + const memory_order _Order = memory_order_seq_cst) noexcept { return compare_exchange_strong(_Expected, _STD move(_Desired), _Order); } bool compare_exchange_strong(shared_ptr<_Ty>& _Expected, shared_ptr<_Ty> _Desired, - const memory_order _Order = memory_order::seq_cst) noexcept { + const memory_order _Order = memory_order_seq_cst) noexcept { _Check_memory_order(_Order); auto _Rep = this->_Lock_and_load(); - if (this->_Ptr == _Expected._Ptr && _Rep == _Expected._Rep) { - _STD swap(this->_Ptr, _Desired._Ptr); + if (this->_Ptr.load(memory_order_relaxed) == _Expected._Ptr && _Rep == _Expected._Rep) { + _Ty* const _Tmp = _Desired._Ptr; + _Desired._Ptr = this->_Ptr.load(memory_order_relaxed); + this->_Ptr.store(_Tmp, memory_order_relaxed); _STD swap(_Rep, _Desired._Rep); this->_Store_and_unlock(_Rep); return true; } _Ref_count_base* _Expected_rep = _Expected._Rep; - _Expected._Ptr = this->_Ptr; + _Expected._Ptr = this->_Ptr.load(memory_order_relaxed); _Expected._Rep = _Rep; _Expected._Incref(); this->_Store_and_unlock(_Rep); @@ -3179,6 +3267,13 @@ public: return false; } + void wait(shared_ptr<_Ty> _Old, memory_order _Order = memory_order_seq_cst) const noexcept { + this->_Wait(_Old._Ptr, _Order); + } + + using _Base::notify_all; + using _Base::notify_one; + constexpr atomic() noexcept = default; atomic(const shared_ptr<_Ty> _Value) noexcept : _Base(_Value._Ptr, _Value._Rep) { @@ -3193,7 +3288,7 @@ public: } ~atomic() { - const auto _Rep = reinterpret_cast<_Ref_count_base*>(this->_Repptr.load(memory_order::relaxed)); + const auto _Rep = reinterpret_cast<_Ref_count_base*>(this->_Repptr.load(memory_order_relaxed)); if (_Rep) { _Rep->_Decref(); } @@ -3214,19 +3309,21 @@ public: return false; } - void store(weak_ptr<_Ty> _Value, const memory_order _Order = memory_order::seq_cst) noexcept { + void store(weak_ptr<_Ty> _Value, const memory_order _Order = memory_order_seq_cst) noexcept { _Check_store_memory_order(_Order); const auto _Rep = this->_Lock_and_load(); - _STD swap(this->_Ptr, _Value._Ptr); + _Ty* const _Tmp = _Value._Ptr; + _Value._Ptr = this->_Ptr.load(memory_order_relaxed); + this->_Ptr.store(_Tmp, memory_order_relaxed); this->_Store_and_unlock(_Value._Rep); _Value._Rep = _Rep; } - _NODISCARD weak_ptr<_Ty> load(const memory_order _Order = memory_order::seq_cst) const noexcept { + _NODISCARD weak_ptr<_Ty> load(const memory_order _Order = memory_order_seq_cst) const noexcept { _Check_load_memory_order(_Order); weak_ptr<_Ty> _Result; const auto _Rep = this->_Lock_and_load(); - _Result._Ptr = this->_Ptr; + _Result._Ptr = this->_Ptr.load(memory_order_relaxed); _Result._Rep = _Rep; _Result._Incwref(); this->_Store_and_unlock(_Rep); @@ -3237,12 +3334,12 @@ public: return load(); } - weak_ptr<_Ty> exchange(weak_ptr<_Ty> _Value, const memory_order _Order = memory_order::seq_cst) noexcept { + weak_ptr<_Ty> exchange(weak_ptr<_Ty> _Value, const memory_order _Order = memory_order_seq_cst) noexcept { _Check_memory_order(_Order); weak_ptr<_Ty> _Result; _Result._Rep = this->_Lock_and_load(); - _Result._Ptr = this->_Ptr; - this->_Ptr = _Value._Ptr; + _Result._Ptr = this->_Ptr.load(memory_order_relaxed); + this->_Ptr.store(_Value._Ptr, memory_order_relaxed); this->_Store_and_unlock(_Value._Rep); _Value._Ptr = nullptr; // ownership of _Value ref has been given to this, silence decrement _Value._Rep = nullptr; @@ -3260,22 +3357,24 @@ public: } bool compare_exchange_weak( - weak_ptr<_Ty>& _Expected, weak_ptr<_Ty> _Desired, const memory_order _Order = memory_order::seq_cst) noexcept { + weak_ptr<_Ty>& _Expected, weak_ptr<_Ty> _Desired, const memory_order _Order = memory_order_seq_cst) noexcept { return compare_exchange_strong(_Expected, _STD move(_Desired), _Order); } bool compare_exchange_strong( - weak_ptr<_Ty>& _Expected, weak_ptr<_Ty> _Desired, const memory_order _Order = memory_order::seq_cst) noexcept { + weak_ptr<_Ty>& _Expected, weak_ptr<_Ty> _Desired, const memory_order _Order = memory_order_seq_cst) noexcept { _Check_memory_order(_Order); auto _Rep = this->_Lock_and_load(); - if (this->_Ptr == _Expected._Ptr && _Rep == _Expected._Rep) { - _STD swap(this->_Ptr, _Desired._Ptr); + if (this->_Ptr.load(memory_order_relaxed) == _Expected._Ptr && _Rep == _Expected._Rep) { + _Ty* const _Tmp = _Desired._Ptr; + _Desired._Ptr = this->_Ptr.load(memory_order_relaxed); + this->_Ptr.store(_Tmp, memory_order_relaxed); _STD swap(_Rep, _Desired._Rep); this->_Store_and_unlock(_Rep); return true; } const auto _Expected_rep = _Expected._Rep; - _Expected._Ptr = this->_Ptr; + _Expected._Ptr = this->_Ptr.load(memory_order_relaxed); _Expected._Rep = _Rep; _Expected._Incwref(); this->_Store_and_unlock(_Rep); @@ -3285,6 +3384,13 @@ public: return false; } + void wait(weak_ptr<_Ty> _Old, memory_order _Order = memory_order_seq_cst) const noexcept { + this->_Wait(_Old._Ptr, _Order); + } + + using _Base::notify_all; + using _Base::notify_one; + constexpr atomic() noexcept = default; atomic(const weak_ptr<_Ty> _Value) noexcept : _Base(_Value._Ptr, _Value._Rep) { @@ -3299,7 +3405,7 @@ public: } ~atomic() { - const auto _Rep = reinterpret_cast<_Ref_count_base*>(this->_Repptr.load(memory_order::relaxed)); + const auto _Rep = reinterpret_cast<_Ref_count_base*>(this->_Repptr.load(memory_order_relaxed)); if (_Rep) { _Rep->_Decwref(); } diff --git a/stl/inc/numeric b/stl/inc/numeric index 134385db5e4..c6f78d947e7 100644 --- a/stl/inc/numeric +++ b/stl/inc/numeric @@ -536,21 +536,16 @@ _CONSTEXPR20 void iota(_FwdIt _First, _FwdIt _Last, _Ty _Val) { #if _HAS_CXX17 // FUNCTION TEMPLATE _Abs_u -template -_NODISCARD constexpr auto _Abs_u(const _Arithmetic _Val) noexcept { +template +_NODISCARD constexpr auto _Abs_u(const _Integral _Val) noexcept { // computes absolute value of _Val (converting to an unsigned integer type if necessary to avoid overflow // representing the negation of the minimum value) - if constexpr (is_floating_point_v<_Arithmetic>) { - // TRANSITION, P0553: this mishandles NaNs - if (_Val < 0) { - return -_Val; - } + static_assert(is_integral_v<_Integral>); - return _Val; - } else if constexpr (is_signed_v<_Arithmetic>) { - using _Unsigned = make_unsigned_t<_Arithmetic>; + if constexpr (is_signed_v<_Integral>) { + using _Unsigned = make_unsigned_t<_Integral>; if (_Val < 0) { - // note static_cast to _Unsigned such that _Arithmetic == short returns unsigned short rather than int + // note static_cast to _Unsigned such that _Integral == short returns unsigned short rather than int return static_cast<_Unsigned>(_Unsigned{0} - static_cast<_Unsigned>(_Val)); } @@ -619,9 +614,24 @@ _NODISCARD constexpr common_type_t<_Mt, _Nt> lcm(const _Mt _Mx, const _Nt _Nx) n template && !is_same_v, bool>, int> = 0> _NODISCARD constexpr _Ty midpoint(const _Ty _Val1, const _Ty _Val2) noexcept { if constexpr (is_floating_point_v<_Ty>) { + if (_STD is_constant_evaluated()) { + if (_Is_nan(_Val1)) { + return _Val1; + } + + if (_Is_nan(_Val2)) { + return _Val2; + } + } else { + if (_Is_nan(_Val1) || _Is_nan(_Val2)) { + // raise FE_INVALID if at least one of _Val1 and _Val2 is signaling NaN + return _Val1 + _Val2; + } + } + constexpr _Ty _High_limit = (numeric_limits<_Ty>::max)() / 2; - const auto _Val1_a = _Abs_u(_Val1); - const auto _Val2_a = _Abs_u(_Val2); + const auto _Val1_a = _Float_abs(_Val1); + const auto _Val2_a = _Float_abs(_Val2); if (_Val1_a <= _High_limit && _Val2_a <= _High_limit) { // _Val1 and _Val2 are small enough that _Val1 + _Val2 won't overflow @@ -637,22 +647,12 @@ _NODISCARD constexpr _Ty midpoint(const _Ty _Val1, const _Ty _Val2) noexcept { return (_Val1 + _Val2) / 2; } - // TRANSITION, P0553: the next two branches handle NaNs but don't produce correct behavior under /fp:fast or - // -fassociative-math - if (_Val1 != _Val1) { - return _Val1; - } - - if (_Val2 != _Val2) { - return _Val2; - } - // Here at least one of {_Val1, _Val2} has large magnitude. // Therefore, if one of the values is too small to divide by 2 exactly, the small magnitude is much less than // one ULP of the result, so we can add it directly without the potentially inexact division by 2. // In the default rounding mode this less than one ULP difference will always be rounded away, so under - // /fp:precise or /fp:fast we could avoid these tests if we had some means of detecting it in the caller. + // /fp:fast we could avoid these tests if we had some means of detecting it in the caller. constexpr _Ty _Low_limit = (numeric_limits<_Ty>::min)() * 2; if (_Val1_a < _Low_limit) { return _Val1 + _Val2 / 2; diff --git a/stl/inc/random b/stl/inc/random index 4427cd637a5..f3748cb2fb6 100644 --- a/stl/inc/random +++ b/stl/inc/random @@ -15,10 +15,6 @@ #include #include -#ifdef __cpp_lib_concepts -#include -#endif // __cpp_lib_concepts - #pragma pack(push, _CRT_PACKING) #pragma warning(push, _STL_WARNING_LEVEL) #pragma warning(disable : _STL_DISABLED_WARNINGS) @@ -52,25 +48,6 @@ _STD_BEGIN "unsigned short, unsigned int, unsigned long, or unsigned long long"); \ _RNG_PROHIBIT_CHAR(_CheckedType) - -#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)(); -}; -// clang-format on -#endif // __cpp_lib_concepts - // ALIAS TEMPLATE _Enable_if_seed_seq_t template using _Enable_if_seed_seq_t = enable_if_t< @@ -4507,7 +4484,7 @@ private: result_type _Eval(_Engine& _Eng, const param_type& _Par0) const { double _Px = _NRAND(_Eng, double); const auto _First = _Par0._Pcdf.begin(); - const auto _Position = _STD lower_bound(_First, _Par0._Pcdf.end(), _Px); + const auto _Position = _STD lower_bound(_First, _Prev_iter(_Par0._Pcdf.end()), _Px); return static_cast(_Position - _First); } diff --git a/stl/inc/system_error b/stl/inc/system_error index 4121c5766d9..c500401a377 100644 --- a/stl/inc/system_error +++ b/stl/inc/system_error @@ -513,8 +513,6 @@ public: } }; -// -// TRANSITION, VSO-1043450 // _Immortalize_memcpy_image is used to provide a nonstandard guarantee. // Specifically, we want the error category objects returned from things like std::system_category() to always // be available, even during DLL unload (otherwise, would be a huge regression vs. legacy error codes). @@ -557,7 +555,35 @@ _NODISCARD const _Ty& _Immortalize_memcpy_image() noexcept { static constexpr _Ty _Static; return _Static; } -#else // choose immortalize strategy +#elif defined(__clang__) +template +_NODISCARD const _Ty& _Immortalize_memcpy_image() noexcept { + [[clang::require_constant_initialization]] static _Ty _Static; + return _Static; +} +#elif !defined(_M_CEE) // TRANSITION, VSO-1153256 +template +struct _Constexpr_immortalize_impl { + union { + _Ty _Storage; + }; + + constexpr _Constexpr_immortalize_impl() noexcept : _Storage{} {} + + _Constexpr_immortalize_impl(const _Constexpr_immortalize_impl&) = delete; + _Constexpr_immortalize_impl& operator=(const _Constexpr_immortalize_impl&) = delete; + + [[msvc::noop_dtor]] ~_Constexpr_immortalize_impl() { + // do nothing, allowing _Ty to be used during shutdown + } +}; + +template +_NODISCARD const _Ty& _Immortalize_memcpy_image() noexcept { + static _Constexpr_immortalize_impl<_Ty> _Static; + return _Static._Storage; +} +#else // ^^^ no workaround / workaround vvv template _NODISCARD const _Ty& _Immortalize_memcpy_image() noexcept { // return reference to a memcpy'd default-initialized _Ty diff --git a/stl/inc/tuple b/stl/inc/tuple index 8cbc90d9e4d..ee6f40fb1e2 100644 --- a/stl/inc/tuple +++ b/stl/inc/tuple @@ -115,7 +115,7 @@ _INLINE_VAR constexpr bool _Tuple_nothrow_assignable_v = _Tuple_nothrow_assignable_v0 == sizeof...(_Srcs), _Dest, _Srcs...>; // STRUCT TEMPLATE _Tuple_convert_copy_val -// Constrain tuple's converting copy constructor (LWG-2549) +// Constrain tuple's copy converting constructor (LWG-2549) template struct _Tuple_convert_copy_val : true_type {}; @@ -125,7 +125,7 @@ struct _Tuple_convert_copy_val, _Uty> is_convertible&, _This>>> {}; // STRUCT TEMPLATE _Tuple_convert_move_val -// Constrain tuple's converting move constructor (LWG-2549) +// Constrain tuple's move converting constructor (LWG-2549) template struct _Tuple_convert_move_val : true_type {}; diff --git a/stl/inc/type_traits b/stl/inc/type_traits index e17f1b7381c..477f3a47e12 100644 --- a/stl/inc/type_traits +++ b/stl/inc/type_traits @@ -72,84 +72,6 @@ struct negation : bool_constant(_Trait::value)> {}; // The ne template _INLINE_VAR constexpr bool negation_v = negation<_Trait>::value; -// STRUCT TEMPLATE _Arg_types -template -struct _Arg_types {}; // provide argument_type, etc. (sometimes) - -template -struct _Arg_types<_Ty1> { - _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty1 _ARGUMENT_TYPE_NAME; -}; - -template -struct _Arg_types<_Ty1, _Ty2> { - _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty1 _FIRST_ARGUMENT_TYPE_NAME; - _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty2 _SECOND_ARGUMENT_TYPE_NAME; -}; - -// STRUCT TEMPLATE is_function -template -struct _Is_function { // determine whether _Ty is a function - using _Bool_type = false_type; // NB: members are user-visible via _Weak_types -}; - -#define _IS_FUNCTION(CALL_OPT, CV_OPT, REF_OPT, NOEXCEPT_OPT) \ - template \ - struct _Is_function<_Ret CALL_OPT(_Types...) CV_OPT REF_OPT NOEXCEPT_OPT> : _Arg_types<_Types...> { \ - using _Bool_type = true_type; \ - _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ret _RESULT_TYPE_NAME; \ - }; - -_NON_MEMBER_CALL_CV_REF_NOEXCEPT(_IS_FUNCTION) -#undef _IS_FUNCTION - -#define _IS_FUNCTION_ELLIPSIS(CV_REF_NOEXCEPT_OPT) \ - template \ - struct _Is_function<_Ret(_Types..., ...) CV_REF_NOEXCEPT_OPT> { /* no calling conventions for ellipsis */ \ - using _Bool_type = true_type; \ - _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ret _RESULT_TYPE_NAME; \ - }; - -_CLASS_DEFINE_CV_REF_NOEXCEPT(_IS_FUNCTION_ELLIPSIS) -#undef _IS_FUNCTION_ELLIPSIS - -template -struct is_function : _Is_function<_Ty>::_Bool_type {}; // determine whether _Ty is a function - -template -_INLINE_VAR constexpr bool is_function_v = _Is_function<_Ty>::_Bool_type::value; - -template -struct _Is_memfunptr { // base class for member function pointer predicates - using _Bool_type = false_type; // NB: members are user-visible via _Weak_types -}; - -#define _IS_MEMFUNPTR(CALL_OPT, CV_OPT, REF_OPT, NOEXCEPT_OPT) \ - template \ - struct _Is_memfunptr<_Ret (CALL_OPT _Arg0::*)(_Types...) CV_OPT REF_OPT NOEXCEPT_OPT> \ - : _Arg_types { \ - using _Bool_type = true_type; \ - _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ret _RESULT_TYPE_NAME; \ - using _Class_type = _Arg0; \ - using _Guide_type = enable_if, _Ret(_Types...)>; \ - }; - -_MEMBER_CALL_CV_REF_NOEXCEPT(_IS_MEMFUNPTR) -#undef _IS_MEMFUNPTR - -#define _IS_MEMFUNPTR_ELLIPSIS(CV_REF_NOEXCEPT_OPT) \ - template \ - struct _Is_memfunptr<_Ret (_Arg0::*)(_Types..., ...) \ - CV_REF_NOEXCEPT_OPT> { /* no calling conventions for ellipsis */ \ - using _Bool_type = true_type; \ - _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ret _RESULT_TYPE_NAME; \ - using _Class_type = _Arg0; \ - using _Guide_type = enable_if; \ - }; - -_CLASS_DEFINE_CV_REF_NOEXCEPT(_IS_MEMFUNPTR_ELLIPSIS) -#undef _IS_MEMFUNPTR_ELLIPSIS - // STRUCT TEMPLATE is_void template _INLINE_VAR constexpr bool is_void_v = is_same_v, void>; @@ -379,33 +301,6 @@ _INLINE_VAR constexpr bool is_reference_v<_Ty&&> = true; template struct is_reference : bool_constant> {}; -// STRUCT TEMPLATE is_member_object_pointer -template ::_Bool_type::value> -struct _Is_member_object_pointer { // determine whether _Ty is a pointer to member object - static constexpr bool value = false; -}; - -template -struct _Is_member_object_pointer<_Ty1 _Ty2::*, false> { - static constexpr bool value = true; - using _Class_type = _Ty2; -}; - -template -_INLINE_VAR constexpr bool is_member_object_pointer_v = // determine whether _Ty is a pointer to member object - _Is_member_object_pointer>::value; - -template -struct is_member_object_pointer : bool_constant> {}; - -// STRUCT TEMPLATE is_member_function_pointer -template -_INLINE_VAR constexpr bool is_member_function_pointer_v = // determine whether _Ty is a pointer to member function - _Is_memfunptr>::_Bool_type::value; - -template -struct is_member_function_pointer : bool_constant> {}; - // STRUCT TEMPLATE is_pointer template _INLINE_VAR constexpr bool is_pointer_v = false; // determine whether _Ty is a pointer @@ -454,13 +349,6 @@ _INLINE_VAR constexpr bool is_fundamental_v = is_arithmetic_v<_Ty> || is_void_v< template struct is_fundamental : bool_constant> {}; // determine whether _Ty is a fundamental type -// STRUCT TEMPLATE is_object -template -_INLINE_VAR constexpr bool is_object_v = !is_function_v<_Ty> && !is_reference_v<_Ty> && !is_void_v<_Ty>; - -template -struct is_object : bool_constant> {}; // determine whether _Ty is an object type - // STRUCT TEMPLATE is_convertible template struct is_convertible : bool_constant<__is_convertible_to(_From, _To)> { @@ -484,20 +372,63 @@ struct is_compound : bool_constant> {}; // determine whet template _INLINE_VAR constexpr bool is_compound_v = !is_fundamental_v<_Ty>; -// STRUCT TEMPLATE is_member_pointer -template -_INLINE_VAR constexpr bool is_member_pointer_v = is_member_object_pointer_v<_Ty> || is_member_function_pointer_v<_Ty>; +// STRUCT TEMPLATE _Arg_types +template +struct _Arg_types {}; // provide argument_type, etc. when sizeof...(_Types) is 1 or 2 + +template +struct _Arg_types<_Ty1> { + _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty1 _ARGUMENT_TYPE_NAME; +}; + +template +struct _Arg_types<_Ty1, _Ty2> { + _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty1 _FIRST_ARGUMENT_TYPE_NAME; + _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty2 _SECOND_ARGUMENT_TYPE_NAME; +}; +// STRUCT TEMPLATE is_member_function_pointer template -struct is_member_pointer : bool_constant> {}; // determine whether _Ty is a pointer to member +struct _Is_memfunptr { // base class for member function pointer predicates + using _Bool_type = false_type; // NB: members are user-visible via _Weak_types +}; -// STRUCT TEMPLATE is_scalar +#define _IS_MEMFUNPTR(CALL_OPT, CV_OPT, REF_OPT, NOEXCEPT_OPT) \ + template \ + struct _Is_memfunptr<_Ret (CALL_OPT _Arg0::*)(_Types...) CV_OPT REF_OPT NOEXCEPT_OPT> \ + : _Arg_types { \ + using _Bool_type = true_type; \ + _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ret _RESULT_TYPE_NAME; \ + using _Class_type = _Arg0; \ + using _Guide_type = enable_if, _Ret(_Types...)>; \ + }; + +_MEMBER_CALL_CV_REF_NOEXCEPT(_IS_MEMFUNPTR) +#undef _IS_MEMFUNPTR + +#define _IS_MEMFUNPTR_ELLIPSIS(CV_REF_NOEXCEPT_OPT) \ + template \ + struct _Is_memfunptr<_Ret (_Arg0::*)(_Types..., ...) \ + CV_REF_NOEXCEPT_OPT> { /* no calling conventions for ellipsis */ \ + using _Bool_type = true_type; \ + _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ret _RESULT_TYPE_NAME; \ + using _Class_type = _Arg0; \ + using _Guide_type = enable_if; \ + }; + +_CLASS_DEFINE_CV_REF_NOEXCEPT(_IS_MEMFUNPTR_ELLIPSIS) +#undef _IS_MEMFUNPTR_ELLIPSIS + +#ifdef __clang__ template -_INLINE_VAR constexpr bool is_scalar_v = // determine whether _Ty is a scalar type - is_arithmetic_v<_Ty> || is_enum_v<_Ty> || is_pointer_v<_Ty> || is_member_pointer_v<_Ty> || is_null_pointer_v<_Ty>; +_INLINE_VAR constexpr bool is_member_function_pointer_v = __is_member_function_pointer(_Ty); +#else // ^^^ Clang ^^^ / vvv Other vvv +template +_INLINE_VAR constexpr bool is_member_function_pointer_v = _Is_memfunptr>::_Bool_type::value; +#endif // ^^^ Other ^^^ template -struct is_scalar : bool_constant> {}; +struct is_member_function_pointer : bool_constant> {}; // STRUCT TEMPLATE is_const template @@ -519,6 +450,65 @@ _INLINE_VAR constexpr bool is_volatile_v = true; template struct is_volatile : bool_constant> {}; +// STRUCT TEMPLATE is_function +template +_INLINE_VAR constexpr bool is_function_v = // only function types and reference types can't be const qualified + !is_const_v && !is_reference_v<_Ty>; + +template +struct is_function : bool_constant> {}; + +// STRUCT TEMPLATE is_object +template +_INLINE_VAR constexpr bool is_object_v = // only function types and reference types can't be const qualified + is_const_v && !is_void_v<_Ty>; + +template +struct is_object : bool_constant> {}; + +// STRUCT TEMPLATE is_member_object_pointer +template +struct _Is_member_object_pointer { + static constexpr bool value = false; +}; + +template +struct _Is_member_object_pointer<_Ty1 _Ty2::*> { + static constexpr bool value = !is_function_v<_Ty1>; + using _Class_type = _Ty2; +}; + +#ifdef __clang__ +template +_INLINE_VAR constexpr bool is_member_object_pointer_v = __is_member_object_pointer(_Ty); +#else // ^^^ Clang / Other vvv +template +_INLINE_VAR constexpr bool is_member_object_pointer_v = _Is_member_object_pointer>::value; +#endif // ^^^ Other ^^^ + +template +struct is_member_object_pointer : bool_constant> {}; + +// STRUCT TEMPLATE is_member_pointer +#ifdef __clang__ +template +_INLINE_VAR constexpr bool is_member_pointer_v = __is_member_pointer(_Ty); +#else // ^^^ Clang / Other vvv +template +_INLINE_VAR constexpr bool is_member_pointer_v = is_member_object_pointer_v<_Ty> || is_member_function_pointer_v<_Ty>; +#endif // ^^^ Other ^^^ + +template +struct is_member_pointer : bool_constant> {}; // determine whether _Ty is a pointer to member + +// STRUCT TEMPLATE is_scalar +template +_INLINE_VAR constexpr bool is_scalar_v = // determine whether _Ty is a scalar type + is_arithmetic_v<_Ty> || is_enum_v<_Ty> || is_pointer_v<_Ty> || is_member_pointer_v<_Ty> || is_null_pointer_v<_Ty>; + +template +struct is_scalar : bool_constant> {}; + // STRUCT TEMPLATE is_pod template struct _CXX20_DEPRECATE_IS_POD is_pod : bool_constant<__is_pod(_Ty)> {}; // determine whether _Ty is a POD type @@ -905,7 +895,7 @@ template > struct _Sign_base { // determine whether integral type _Ty is signed or unsigned using _Uty = remove_cv_t<_Ty>; - static constexpr bool _Signed = _Uty(-1) < _Uty(0); + static constexpr bool _Signed = static_cast<_Uty>(-1) < static_cast<_Uty>(0); static constexpr bool _Unsigned = !_Signed; }; @@ -1810,7 +1800,28 @@ inline constexpr bool is_nothrow_invocable_r_v = _Select_invoke_traits<_Callable, _Args...>::template _Is_nothrow_invocable_r<_Rx>::value; #endif // _HAS_CXX17 -// STRUCT TEMPLATE _Weak_types +// ALIAS TEMPLATE _Weak_types +template +struct _Function_args {}; // determine whether _Ty is a function + +#define _FUNCTION_ARGS(CALL_OPT, CV_OPT, REF_OPT, NOEXCEPT_OPT) \ + template \ + struct _Function_args<_Ret CALL_OPT(_Types...) CV_OPT REF_OPT NOEXCEPT_OPT> : _Arg_types<_Types...> { \ + _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ret _RESULT_TYPE_NAME; \ + }; + +_NON_MEMBER_CALL_CV_REF_NOEXCEPT(_FUNCTION_ARGS) +#undef _FUNCTION_ARGS + +#define _FUNCTION_ARGS_ELLIPSIS(CV_REF_NOEXCEPT_OPT) \ + template \ + struct _Function_args<_Ret(_Types..., ...) CV_REF_NOEXCEPT_OPT> { /* no calling conventions for ellipsis */ \ + _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ret _RESULT_TYPE_NAME; \ + }; + +_CLASS_DEFINE_CV_REF_NOEXCEPT(_FUNCTION_ARGS_ELLIPSIS) +#undef _FUNCTION_ARGS_ELLIPSIS + template struct _Weak_result_type {}; // default definition @@ -1826,8 +1837,8 @@ struct _Weak_argument_type : _Weak_result_type<_Ty> {}; // default definition _STL_DISABLE_DEPRECATED_WARNING template -struct _Weak_argument_type<_Ty, void_t> - : _Weak_result_type<_Ty> { // defined if _Ty::argument_type exists +struct _Weak_argument_type<_Ty, void_t> : _Weak_result_type<_Ty> { + // defined if _Ty::argument_type exists _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef typename _Ty::argument_type _ARGUMENT_TYPE_NAME; }; _STL_RESTORE_DEPRECATED_WARNING @@ -1846,12 +1857,8 @@ struct _Weak_binary_args<_Ty, void_t -struct _Weak_types { // provide nested types (sometimes) - using _Is_f_or_pf = _Is_function>; - using _Is_pmf = _Is_memfunptr>; - using type = conditional_t>, _Is_f_or_pf, - conditional_t, _Is_pmf, _Weak_binary_args<_Ty>>>; -}; +using _Weak_types = conditional_t>, _Function_args>, + conditional_t, _Is_memfunptr>, _Weak_binary_args<_Ty>>>; // CLASS TEMPLATE reference_wrapper template @@ -1868,7 +1875,7 @@ struct _Refwrap_has_ctor_from<_Ty, _Uty, void_t( template class reference_wrapper #if !_HAS_CXX20 - : public _Weak_types<_Ty>::type + : public _Weak_types<_Ty> #endif // !_HAS_CXX20 { public: diff --git a/stl/inc/valarray b/stl/inc/valarray index d17080150c7..4c50bff4795 100644 --- a/stl/inc/valarray +++ b/stl/inc/valarray @@ -938,9 +938,16 @@ public: slice_array() = delete; - slice_array(const slice_array&); // not defined + slice_array(const slice_array&) = default; - slice_array& operator=(const slice_array&); // not defined + const slice_array& operator=(const slice_array& _Right) const { + size_t _Dst_off = _Start; + size_t _Src_off = _Right._Start; + for (size_t _Idx = 0; _Idx < _Len; ++_Idx, _Dst_off += _Stride, _Src_off += _Right._Stride) { + _Myptr[_Dst_off] = _Right._Myptr[_Src_off]; + } + return *this; + } private: friend valarray<_Ty>; @@ -992,7 +999,7 @@ public: return _Ans; } - size_t _Totlen() const { + _NODISCARD size_t _Totlen() const { if (_Len.size() == 0) { return 0; } @@ -1073,15 +1080,23 @@ public: _GSLOP(>>= _Right[_Idx]); } - _Ty& _Data(size_t _Idx) const { + _NODISCARD _Ty& _Data(size_t _Idx) const { return _Myptr[_Idx]; } gslice_array() = delete; - gslice_array(const gslice_array&); // not defined + gslice_array(const gslice_array&) = default; - gslice_array& operator=(const gslice_array&); // not defined + const gslice_array& operator=(const gslice_array& _Right) const { + _Sizarray _Dst_indexarray(size_t{0}, _Nslice()); + _Sizarray _Src_indexarray(size_t{0}, _Right._Nslice()); + const size_t _Size = _Totlen(); + for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { + _Myptr[_Off(_Dst_indexarray)] = _Right._Myptr[_Right._Off(_Src_indexarray)]; + } + return *this; + } private: friend valarray<_Ty>; @@ -1155,15 +1170,32 @@ public: _MOP(>>= _Right[_Idx]); } - _Ty& _Data(size_t _Idx) const { + _NODISCARD _Ty& _Data(size_t _Idx) const { return _Myptr[_Idx]; } - bool _Mask(size_t _Idx) const { + _NODISCARD bool _Mask(size_t _Idx) const { return _Mybool[_Idx]; } - size_t _Totlen() const { + _NODISCARD size_t _Start_off() const { + size_t _Off = 0; + const size_t _Size = _Mybool.size(); + while (_Off < _Size && !_Mybool[_Off]) { + ++_Off; + } + return _Off; + } + + _NODISCARD size_t _Next_off(size_t _Off) const { + const size_t _Size = _Mybool.size(); + do { + ++_Off; + } while (_Off < _Size && !_Mybool[_Off]); + return _Off; + } + + _NODISCARD size_t _Totlen() const { size_t _Count = 0; for (size_t _Idx = 0; _Idx < _Mybool.size(); ++_Idx) { if (_Mybool[_Idx]) { @@ -1176,9 +1208,17 @@ public: mask_array() = delete; - mask_array(const mask_array&); // not defined + mask_array(const mask_array&) = default; - mask_array& operator=(const mask_array&); // not defined + const mask_array& operator=(const mask_array& _Right) const { + const size_t _Size = _Mybool.size(); + size_t _Dst_off = _Start_off(); + size_t _Src_off = _Right._Start_off(); + for (; _Dst_off < _Size; _Src_off = _Right._Next_off(_Src_off), _Dst_off = _Next_off(_Dst_off)) { + _Myptr[_Dst_off] = _Right._Myptr[_Src_off]; + } + return *this; + } private: friend valarray<_Ty>; @@ -1250,23 +1290,29 @@ public: _IOP(>>= _Right[_Idx]); } - _Ty& _Data(size_t _Idx) const { + _NODISCARD _Ty& _Data(size_t _Idx) const { return _Myptr[_Idx]; } - size_t _Indir(size_t _Idx) const { + _NODISCARD size_t _Indir(size_t _Idx) const { return _Myindarr[_Idx]; } - size_t _Totlen() const { + _NODISCARD size_t _Totlen() const { return _Myindarr.size(); } indirect_array() = delete; - indirect_array(const indirect_array&); // not defined + indirect_array(const indirect_array&) = default; - indirect_array& operator=(const indirect_array&); // not defined + const indirect_array& operator=(const indirect_array& _Right) const { + const size_t _Size = _Totlen(); + for (size_t _Idx = 0; _Idx < _Size; ++_Idx) { + _Myptr[_Indir(_Idx)] = _Right._Myptr[_Right._Indir(_Idx)]; + } + return *this; + } private: friend valarray<_Ty>; diff --git a/stl/inc/vector b/stl/inc/vector index 4f784ca7eb2..f6a32273b24 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -2983,6 +2983,65 @@ namespace pmr { using vector = _STD vector<_Ty, polymorphic_allocator<_Ty>>; } // namespace pmr #endif // _HAS_CXX17 + +#if _HAS_IF_CONSTEXPR +// VARIABLE TEMPLATE _Is_vb_iterator +template +_INLINE_VAR constexpr bool _Is_vb_iterator<_Vb_iterator<_Alloc>, _RequiresMutable> = true; + +template +_INLINE_VAR constexpr bool _Is_vb_iterator<_Vb_const_iterator<_Alloc>, false> = true; + +template +_CONSTEXPR20 void _Fill_vbool(_FwdIt _First, _FwdIt _Last, const _Ty& _Val) { + // Set [_First, _Last) to _Val + if (_First == _Last) { + return; + } + + _Vbase* _VbFirst = const_cast<_Vbase*>(_First._Myptr); + _Vbase* const _VbLast = const_cast<_Vbase*>(_Last._Myptr); + + const auto _FirstSourceMask = static_cast<_Vbase>(-1) << _First._Myoff; + const auto _FirstDestMask = ~_FirstSourceMask; + const auto _FillVal = static_cast<_Vbase>(_Val ? -1 : 0); + + if (_VbFirst == _VbLast) { + // We already excluded _First == _Last, so here _Last._Myoff > 0 and the shift is safe + const auto _LastSourceMask = static_cast<_Vbase>(-1) >> (_VBITS - _Last._Myoff); + const auto _LastDestMask = ~_LastSourceMask; + const auto _SourceMask = _FirstSourceMask & _LastSourceMask; + const auto _DestMask = _FirstDestMask | _LastDestMask; + *_VbFirst = (*_VbFirst & _DestMask) | (_FillVal & _SourceMask); + return; + } + + *_VbFirst = (*_VbFirst & _FirstDestMask) | (_FillVal & _FirstSourceMask); + ++_VbFirst; + +#ifdef __cpp_lib_is_constant_evaluated + if (_STD is_constant_evaluated()) { + for (; _VbFirst != _VbLast; ++_VbFirst) { + *_VbFirst = _FillVal; + } + } else +#endif // __cpp_lib_is_constant_evaluated + { + const auto _VbFirst_ch = reinterpret_cast(_VbFirst); + const auto _VbLast_ch = reinterpret_cast(_VbLast); + const auto _Count_ch = static_cast(_VbLast_ch - _VbFirst_ch); + const auto _ValChar = static_cast(_Val ? -1 : 0); + _CSTD memset(_VbFirst, _ValChar, _Count_ch); + _VbFirst = _VbLast; + } + + if (_Last._Myoff != 0) { + const auto _LastSourceMask = static_cast<_Vbase>(-1) >> (_VBITS - _Last._Myoff); + const auto _LastDestMask = ~_LastSourceMask; + *_VbFirst = (*_VbFirst & _LastDestMask) | (_FillVal & _LastSourceMask); + } +} +#endif // _HAS_IF_CONSTEXPR _STD_END #pragma pop_macro("new") diff --git a/stl/inc/xatomic_wait.h b/stl/inc/xatomic_wait.h new file mode 100644 index 00000000000..266aae0c9f8 --- /dev/null +++ b/stl/inc/xatomic_wait.h @@ -0,0 +1,72 @@ +// xatomic_wait.h internal header + +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once +#ifndef _XATOMIC_WAIT_H +#define _XATOMIC_WAIT_H +#include +#if _STL_COMPILER_PREPROCESSOR + +#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 + +_INLINE_VAR constexpr unsigned long long _Atomic_wait_no_deadline = 0xFFFF'FFFF'FFFF'FFFF; +_INLINE_VAR constexpr unsigned long _Atomic_wait_no_timeout = 0xFFFF'FFFF; // Pass as partial timeout + +_EXTERN_C +enum class __std_atomic_api_level : unsigned long { + __not_set, + __detecting, + __has_srwlock, + __has_wait_on_address, +}; + +// This function allows testing the atomic wait support while always using the APIs for a platform with fewer +// capabilities; it attempts to lock the APIs used to the level `_Requested_api_level`, and returns the actual API level +// in use. Once the API level has been set by calling this function (or detected by a call to one of the atomic wait +// functions), it can no longer be changed. +__std_atomic_api_level __stdcall __std_atomic_set_api_level(__std_atomic_api_level _Requested_api_level) noexcept; + +// Support for atomic waits. +// The "direct" functions are used when the underlying infrastructure can use WaitOnAddress directly; that is, _Size is +// 1, 2, 4, or 8. The contract is the same as the WaitOnAddress function from the Windows SDK. If WaitOnAddress is not +// available on the current platform, falls back to a similar solution based on SRWLOCK and CONDITION_VARIABLE. +int __stdcall __std_atomic_wait_direct( + const void* _Storage, void* _Comparand, size_t _Size, unsigned long _Remaining_timeout) noexcept; +void __stdcall __std_atomic_notify_one_direct(const void* _Storage) noexcept; +void __stdcall __std_atomic_notify_all_direct(const void* _Storage) noexcept; + +// The "indirect" functions are used when the size is not 1, 2, 4, or 8; these notionally wait on another value which is +// of one of those sizes whose value changes upon notify, hence "indirect". (As of 2020-07-24, this always uses the +// fallback SRWLOCK and CONDITION_VARIABLE implementation but that is not contractual.) +using _Atomic_wait_indirect_equal_callback_t = bool(__stdcall*)( + const void* _Storage, void* _Comparand, size_t _Size, void* _Param) noexcept; + +int __stdcall __std_atomic_wait_indirect(const void* _Storage, void* _Comparand, size_t _Size, void* _Param, + _Atomic_wait_indirect_equal_callback_t _Are_equal, unsigned long _Remaining_timeout) noexcept; +void __stdcall __std_atomic_notify_one_indirect(const void* _Storage) noexcept; +void __stdcall __std_atomic_notify_all_indirect(const void* _Storage) noexcept; + +// These functions convert a duration into a time point in order to tolerate spurious wakes in atomic wait, and then +// convert back from the time point to individual wait attempts (which are limited by DWORD milliseconds to a length of +// ~49 days) +unsigned long long __stdcall __std_atomic_wait_get_deadline(unsigned long long _Timeout) noexcept; +unsigned long __stdcall __std_atomic_wait_get_remaining_timeout(unsigned long long _Deadline) noexcept; + +_END_EXTERN_C + +#pragma pop_macro("new") +_STL_RESTORE_CLANG_WARNINGS +#pragma warning(pop) +#pragma pack(pop) +#endif // _STL_COMPILER_PREPROCESSOR +#endif // _XATOMIC_WAIT_H diff --git a/stl/inc/xstring b/stl/inc/xstring index eba056a939e..787ba828f27 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -336,7 +336,7 @@ template <> struct char_traits : _WChar_traits {}; #endif // _NATIVE_WCHAR_T_DEFINED -#if defined(__cpp_char8_t) && defined(_MSC_VER) && !defined(__EDG__) && !defined(__clang__) +#if defined(__cpp_char8_t) && !defined(__EDG__) && !defined(__clang__) #define _HAS_U8_INTRINSICS 1 #else // ^^^ Use intrinsics for char8_t / don't use said intrinsics vvv #define _HAS_U8_INTRINSICS 0 diff --git a/stl/inc/xutility b/stl/inc/xutility index f80ed07fa86..9c82bb27c55 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -27,6 +27,12 @@ _STL_DISABLE_CLANG_WARNINGS #define _USE_STD_VECTOR_ALGORITHMS 0 #endif +#ifdef __CUDACC__ +#define _CONSTEXPR_BIT_CAST inline +#else // ^^^ workaround ^^^ / vvv no workaround vvv +#define _CONSTEXPR_BIT_CAST constexpr +#endif // ^^^ no workaround ^^^ + #if _USE_STD_VECTOR_ALGORITHMS _EXTERN_C // The "noalias" attribute tells the compiler optimizer that pointers going into these hand-vectorized algorithms @@ -52,17 +58,15 @@ template , is_trivially_copyable<_To>, is_trivially_copyable<_From>>, int> = 0> +_NODISCARD _CONSTEXPR_BIT_CAST _To _Bit_cast(const _From& _Val) noexcept { #ifdef __CUDACC__ -_NODISCARD _To _Bit_cast(const _From& _Val) noexcept { _To _To_obj; // assumes default-init _CSTD memcpy(_STD addressof(_To_obj), _STD addressof(_Val), sizeof(_To)); return _To_obj; -} #else // ^^^ workaround ^^^ / vvv no workaround vvv -_NODISCARD constexpr _To _Bit_cast(const _From& _Val) noexcept { return __builtin_bit_cast(_To, _Val); -} #endif // ^^^ no workaround ^^^ +} // STRUCT TEMPLATE _Get_first_parameter template @@ -190,16 +194,22 @@ template struct _Ref_fn { // pass function object by value as a reference template constexpr decltype(auto) operator()(_Args&&... _Vals) { // forward function call operator - return _Fn(_STD forward<_Args>(_Vals)...); +#if _HAS_IF_CONSTEXPR + if constexpr (is_member_pointer_v<_Fx>) { + return _STD invoke(_Fn, _STD forward<_Args>(_Vals)...); + } else +#endif // _HAS_IF_CONSTEXPR + { + return _Fn(_STD forward<_Args>(_Vals)...); + } } _Fx& _Fn; }; template -_INLINE_VAR constexpr bool - _Pass_functor_by_value_v = sizeof(_Fn) <= sizeof(void*) - && conjunction_v, is_trivially_destructible<_Fn>>; +_INLINE_VAR constexpr bool _Pass_functor_by_value_v = conjunction_v, + is_trivially_copy_constructible<_Fn>, is_trivially_destructible<_Fn>>; template , int> = 0> // TRANSITION, if constexpr constexpr _Fn _Pass_fn(_Fn _Val) { // pass functor by value @@ -741,20 +751,34 @@ concept indirectly_writable = requires(_It&& __i, _Ty&& __t) { const_cast&&>(*static_cast<_It&&>(__i)) = static_cast<_Ty&&>(__t); }; +// CONCEPT _Integer_like +// clang-format off +template +concept _Integer_like = _Is_nonbool_integral<_Ty>; // per the proposed resolution of LWG-3467 + +// CONCEPT _Signed_integer_like +template +concept _Signed_integer_like = _Integer_like<_Ty> && static_cast<_Ty>(-1) < static_cast<_Ty>(0); +// clang-format on + +// ALIAS TEMPLATE _Make_unsigned_like_t +template +using _Make_unsigned_like_t = make_unsigned_t<_Ty>; + +// ALIAS TEMPLATE _Make_signed_like_t +template +using _Make_signed_like_t = make_signed_t<_Ty>; // per the proposed resolution of LWG-3403 + // CONCEPT weakly_incrementable // clang-format off template concept weakly_incrementable = default_initializable<_Ty> && movable<_Ty> && requires(_Ty __i) { typename iter_difference_t<_Ty>; - requires signed_integral>; + requires _Signed_integer_like>; { ++__i } -> same_as<_Ty&>; __i++; }; -// ALIAS TEMPLATE _Make_unsigned_like_t -template -using _Make_unsigned_like_t = make_unsigned_t<_Ty>; - // CONCEPT incrementable template concept incrementable = regular<_Ty> && weakly_incrementable<_Ty> && requires(_Ty __t) { @@ -1028,10 +1052,10 @@ namespace ranges { // clang-format on template - _NODISCARD constexpr iter_value_t> _Iter_exchange_move( - _Xty&& _XVal, _Yty&& _YVal) noexcept(noexcept(iter_value_t>(iter_move(_XVal)))) { - iter_value_t> _Tmp(iter_move(_XVal)); - *_XVal = iter_move(_YVal); + _NODISCARD constexpr iter_value_t> _Iter_exchange_move(_Xty&& _XVal, + _Yty&& _YVal) noexcept(noexcept(iter_value_t>(_RANGES iter_move(_XVal)))) { + iter_value_t> _Tmp(_RANGES iter_move(_XVal)); + *_XVal = _RANGES iter_move(_YVal); return _Tmp; } @@ -1047,7 +1071,7 @@ namespace ranges { return {_St::_Swap, noexcept(_RANGES swap(*_STD declval<_Ty1>(), *_STD declval<_Ty2>()))}; } else if constexpr (_Symmetric_indirectly_movable_storable<_Ty1, _Ty2>) { constexpr auto _Nothrow = noexcept( - *_STD declval<_Ty1>() = _Iter_exchange_move(_STD declval<_Ty1>(), _STD declval<_Ty2>())); + *_STD declval<_Ty1>() = _Iter_exchange_move(_STD declval<_Ty2>(), _STD declval<_Ty1>())); return {_St::_Exchange, _Nothrow}; } else { return {_St::_None}; @@ -1068,7 +1092,7 @@ namespace ranges { _RANGES swap(*static_cast<_Ty1&&>(_Val1), *static_cast<_Ty2&&>(_Val2)); } else if constexpr (_Choice<_Ty1, _Ty2>._Strategy == _St::_Exchange) { *static_cast<_Ty1&&>(_Val1) = - _Iter_exchange_move(static_cast<_Ty1&&>(_Val1), static_cast<_Ty2&&>(_Val2)); + _Iter_exchange_move(static_cast<_Ty2&&>(_Val2), static_cast<_Ty1&&>(_Val1)); } else { static_assert(_Always_false<_Ty1>, "should be unreachable"); } @@ -1106,8 +1130,16 @@ concept permutable = forward_iterator<_It> && indirectly_movable_storable<_It, _It> && indirectly_swappable<_It, _It>; -// CONCEPT sortable +// CONCEPT mergeable namespace ranges { struct less; } +template +concept mergeable = input_iterator<_It1> && input_iterator<_It2> + && weakly_incrementable<_Out> + && indirectly_copyable<_It1, _Out> + && indirectly_copyable<_It2, _Out> + && indirect_strict_weak_order<_Pr, projected<_It1, _Pj1>, projected<_It2, _Pj2>>; + +// CONCEPT sortable template concept sortable = permutable<_It> && indirect_strict_weak_order<_Pr, projected<_It, _Proj>>; // clang-format on @@ -1135,19 +1167,19 @@ struct _Iterator_traits_base<_Iter, typename _Iter::pointer, typename _Iter::reference>> { // defined if _Iter::* types exist using iterator_category = typename _Iter::iterator_category; - using value_type = typename _Iter::value_type; - using difference_type = typename _Iter::difference_type; - using pointer = typename _Iter::pointer; - using reference = typename _Iter::reference; + using value_type = typename _Iter::value_type; + using difference_type = typename _Iter::difference_type; + using pointer = typename _Iter::pointer; + using reference = typename _Iter::reference; }; template > struct _Iterator_traits_pointer_base { // iterator properties for pointers to object using iterator_category = random_access_iterator_tag; - using value_type = remove_cv_t<_Ty>; - using difference_type = ptrdiff_t; - using pointer = _Ty*; - using reference = _Ty&; + using value_type = remove_cv_t<_Ty>; + using difference_type = ptrdiff_t; + using pointer = _Ty*; + using reference = _Ty&; }; template @@ -1407,8 +1439,8 @@ _NODISCARD constexpr decltype(auto) _Get_unwrapped_n(_Iter&& _It, const _Diff _O template && is_integral_v<_Diff>, int> = 0> _NODISCARD constexpr decltype(auto) _Get_unwrapped_n(_Iter&& _It, const _Diff _Off) { // ask an iterator to assert that the iterator moved _Off positions is valid, and unwrap - using _IDiff = _Iter_diff_t<_Remove_cvref_t<_Iter>>; - using _CDiff = common_type_t<_Diff, _IDiff>; + using _IDiff = _Iter_diff_t<_Remove_cvref_t<_Iter>>; + using _CDiff = common_type_t<_Diff, _IDiff>; const auto _COff = static_cast<_CDiff>(_Off); _STL_ASSERT(_COff <= static_cast<_CDiff>(_Max_possible_v<_IDiff>) @@ -1717,7 +1749,7 @@ _CONSTEXPR17 void _Advance1(_InIt& _Where, _Diff _Off, input_iterator_tag) { // increment iterator by offset, input iterators _STL_ASSERT(_Off >= 0, "negative advance of non-bidirectional iterator"); - decltype(auto) _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off); + decltype(auto) _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off); constexpr bool _Need_rewrap = !is_reference_v; for (; 0 < _Off; --_Off) { @@ -1732,7 +1764,7 @@ _CONSTEXPR17 void _Advance1(_InIt& _Where, _Diff _Off, input_iterator_tag) { template _CONSTEXPR17 void _Advance1(_BidIt& _Where, _Diff _Off, bidirectional_iterator_tag) { // increment iterator by offset, bidirectional iterators - decltype(auto) _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off); + decltype(auto) _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off); constexpr bool _Need_rewrap = !is_reference_v; for (; 0 < _Off; --_Off) { @@ -1785,8 +1817,8 @@ template _CONSTEXPR17 _Iter_diff_t<_InIt> _Distance1(_InIt _First, _InIt _Last, input_iterator_tag) { // return distance between iterators; input _Adl_verify_range(_First, _Last); - auto _UFirst = _Get_unwrapped(_First); - const auto _ULast = _Get_unwrapped(_Last); + auto _UFirst = _Get_unwrapped(_First); + const auto _ULast = _Get_unwrapped(_Last); _Iter_diff_t<_InIt> _Off = 0; for (; _UFirst != _ULast; ++_UFirst) { ++_Off; @@ -2831,12 +2863,12 @@ namespace ranges { // clang-format off template concept _Has_member = !disable_sized_range<_UnCV> && requires(_Ty __t) { - { _Fake_decay_copy(__t.size()) } -> integral; + { _Fake_decay_copy(__t.size()) } -> _Integer_like; }; template concept _Has_ADL = _Has_class_or_enum_type<_Ty> && !disable_sized_range<_UnCV> && requires(_Ty __t) { - { _Fake_decay_copy(size(__t)) } -> integral; + { _Fake_decay_copy(size(__t)) } -> _Integer_like; }; template @@ -3121,7 +3153,7 @@ namespace ranges { ~_Not_quite_object() = default; }; - // CLASS ranges::_Advance_fn + // VARIABLE ranges::advance class _Advance_fn : private _Not_quite_object { public: using _Not_quite_object::_Not_quite_object; @@ -3211,10 +3243,9 @@ namespace ranges { } }; - // VARIABLE ranges::advance inline constexpr _Advance_fn advance{_Not_quite_object::_Construct_tag{}}; - // CLASS ranges::_Distance_fn + // VARIABLE ranges::distance class _Distance_fn : private _Not_quite_object { public: using _Not_quite_object::_Not_quite_object; @@ -3225,31 +3256,54 @@ namespace ranges { return _Last - _First; } else { _Adl_verify_range(_First, _Last); - auto _UFirst = _Get_unwrapped(static_cast<_It&&>(_First)); - const auto _ULast = _Get_unwrapped(static_cast<_Se&&>(_Last)); - iter_difference_t<_It> _Count = 0; - for (; _UFirst != _ULast; ++_UFirst) { - ++_Count; - } - - return _Count; + return _Distance_unchecked(_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last))); } } template - _NODISCARD constexpr range_difference_t<_Rng> operator()(_Rng&& _Val) const { + _NODISCARD constexpr range_difference_t<_Rng> operator()(_Rng&& _Range) const { if constexpr (sized_range<_Rng>) { - return static_cast>(_RANGES size(_Val)); + return static_cast>(_RANGES size(_Range)); } else { - return (*this)(_RANGES begin(_Val), _RANGES end(_Val)); + return _Distance_unchecked(_Ubegin(_Range), _Uend(_Range)); + } + } + + private: + template + _NODISCARD static constexpr iter_difference_t<_It> _Distance_unchecked(_It _First, const _Se _Last) { + _STL_INTERNAL_STATIC_ASSERT(input_or_output_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + + iter_difference_t<_It> _Count = 0; + for (; _First != _Last; ++_First) { + ++_Count; } + + return _Count; } }; - // VARIABLE ranges::distance inline constexpr _Distance_fn distance{_Not_quite_object::_Construct_tag{}}; - // CLASS ranges::_Next_fn + // VARIABLE ranges::ssize + class _Ssize_fn { // Per the proposed resolution of LWG-3403 + public: + // clang-format off + template + _NODISCARD constexpr auto operator()(_Rng&& _Range) const requires requires { _RANGES size(_Range); } { + using _Sty = _Make_signed_like_t; + using _Ty = common_type_t, ptrdiff_t, _Sty>, _Sty>; + return static_cast<_Ty>(_RANGES size(_Range)); + } + // clang-format on + }; + + inline namespace _Cpos { + inline constexpr _Ssize_fn ssize; + } + + // VARIABLE ranges::next class _Next_fn : private _Not_quite_object { public: using _Not_quite_object::_Not_quite_object; @@ -3279,10 +3333,9 @@ namespace ranges { } }; - // VARIABLE ranges::next inline constexpr _Next_fn next{_Not_quite_object::_Construct_tag{}}; - // CLASS ranges::_Prev_fn + // VARIABLE ranges::prev class _Prev_fn : private _Not_quite_object { public: using _Not_quite_object::_Not_quite_object; @@ -3308,7 +3361,6 @@ namespace ranges { } }; - // VARIABLE ranges::prev inline constexpr _Prev_fn prev{_Not_quite_object::_Construct_tag{}}; // FUNCTION TEMPLATE _Find_last_iterator @@ -3902,7 +3954,7 @@ public: _CONSTEXPR17 move_iterator() = default; _CONSTEXPR17 explicit move_iterator(_Iter _Right) noexcept(is_nothrow_move_constructible_v<_Iter>) // strengthened - : current(_STD move(_Right)) {} + : _Current(_STD move(_Right)) {} // clang-format off template @@ -3912,7 +3964,7 @@ public: #endif // __cpp_lib_concepts _CONSTEXPR17 move_iterator(const move_iterator<_Other>& _Right) noexcept( is_nothrow_constructible_v<_Iter, const _Other&>) // strengthened - : current(_Right.base()) {} + : _Current(_Right.base()) {} template #ifdef __cpp_lib_concepts @@ -3922,38 +3974,38 @@ public: #endif // __cpp_lib_concepts _CONSTEXPR17 move_iterator& operator=(const move_iterator<_Other>& _Right) noexcept( is_nothrow_assignable_v<_Iter&, const _Other&>) /* strengthened */ { - current = _Right.base(); + _Current = _Right.base(); return *this; } // clang-format on #ifdef __cpp_lib_concepts _NODISCARD constexpr const iterator_type& base() const& { // Per LWG-3391 - return current; + return _Current; } _NODISCARD constexpr iterator_type base() && { - return _STD move(current); + return _STD move(_Current); } #else // ^^^ __cpp_lib_concepts / !__cpp_lib_concepts vvv _NODISCARD _CONSTEXPR17 iterator_type base() const { - return current; + return _Current; } #endif // __cpp_lib_concepts _NODISCARD _CONSTEXPR17 reference operator*() const { #ifdef __cpp_lib_concepts - return _RANGES iter_move(current); + return _RANGES iter_move(_Current); #else // ^^^ __cpp_lib_concepts / !__cpp_lib_concepts vvv - return static_cast(*current); + return static_cast(*_Current); #endif // __cpp_lib_concepts } _NODISCARD _CXX20_DEPRECATE_MOVE_ITERATOR_ARROW _CONSTEXPR17 pointer operator->() const { - return current; + return _Current; } _CONSTEXPR17 move_iterator& operator++() { - ++current; + ++_Current; return *this; } @@ -3962,102 +4014,102 @@ public: if constexpr (forward_iterator<_Iter>) { #endif // __cpp_lib_concepts move_iterator _Tmp = *this; - ++current; + ++_Current; return _Tmp; #ifdef __cpp_lib_concepts } else { - ++current; + ++_Current; } #endif // __cpp_lib_concepts } _CONSTEXPR17 move_iterator& operator--() { - --current; + --_Current; return *this; } _CONSTEXPR17 move_iterator operator--(int) { move_iterator _Tmp = *this; - --current; + --_Current; return _Tmp; } template _NODISCARD auto operator==(_Default_sentinel _Sentinel) const noexcept -> decltype(_STD declval() == _Sentinel) { - return current == _Sentinel; + return _Current == _Sentinel; } template _NODISCARD auto operator!=(_Default_sentinel _Sentinel) const noexcept -> decltype(_STD declval() != _Sentinel) { - return current != _Sentinel; + return _Current != _Sentinel; } _NODISCARD _CONSTEXPR17 move_iterator operator+(const difference_type _Off) const { - return move_iterator(current + _Off); + return move_iterator(_Current + _Off); } _CONSTEXPR17 move_iterator& operator+=(const difference_type _Off) { - current += _Off; + _Current += _Off; return *this; } _NODISCARD _CONSTEXPR17 move_iterator operator-(const difference_type _Off) const { - return move_iterator(current - _Off); + return move_iterator(_Current - _Off); } _CONSTEXPR17 move_iterator& operator-=(const difference_type _Off) { - current -= _Off; + _Current -= _Off; return *this; } _NODISCARD _CONSTEXPR17 reference operator[](const difference_type _Off) const { #ifdef __cpp_lib_concepts - return _RANGES iter_move(current + _Off); + return _RANGES iter_move(_Current + _Off); #else // ^^^ __cpp_lib_concepts / !__cpp_lib_concepts vvv - return _STD move(current[_Off]); + return _STD move(_Current[_Off]); #endif // __cpp_lib_concepts } #ifdef __cpp_lib_concepts template _Sent> _NODISCARD friend constexpr bool operator==(const move_iterator& _Left, const move_sentinel<_Sent>& _Right) { - return _Left.current == _Right._Get_last(); + return _Left._Current == _Right._Get_last(); } template _Sent> _NODISCARD friend constexpr difference_type operator-( const move_sentinel<_Sent>& _Left, const move_iterator& _Right) { - return _Left._Get_last() - _Right.current; + return _Left._Get_last() - _Right._Current; } template _Sent> _NODISCARD friend constexpr difference_type operator-( const move_iterator& _Left, const move_sentinel<_Sent>& _Right) { - return _Left.current - _Right._Get_last(); + return _Left._Current - _Right._Get_last(); } _NODISCARD friend constexpr reference iter_move(const move_iterator& _It) noexcept( - noexcept(_RANGES iter_move(_It.current))) { - return _RANGES iter_move(_It.current); + noexcept(_RANGES iter_move(_It._Current))) { + return _RANGES iter_move(_It._Current); } template _Iter2> friend constexpr void iter_swap(const move_iterator& _Left, const move_iterator<_Iter2>& _Right) noexcept( - noexcept(_RANGES iter_swap(_Left.current, _Right.base()))) { - _RANGES iter_swap(_Left.current, _Right.base()); + noexcept(_RANGES iter_swap(_Left._Current, _Right.base()))) { + _RANGES iter_swap(_Left._Current, _Right.base()); } #endif // __cpp_lib_concepts template , int> = 0> friend constexpr void _Verify_range(const move_iterator& _First, const move_iterator<_Iter2>& _Last) { - _Verify_range(_First.current, _Last.base()); + _Verify_range(_First._Current, _Last.base()); } #ifdef __cpp_lib_concepts template _Sent, enable_if_t<_Range_verifiable_v<_Iter, _Sent>, int> = 0> friend constexpr void _Verify_range(const move_iterator& _First, const move_sentinel<_Sent>& _Last) { - _Verify_range(_First.current, _Last._Get_last()); + _Verify_range(_First._Current, _Last._Get_last()); } #endif // __cpp_lib_concepts @@ -4065,31 +4117,31 @@ public: template , int> = 0> constexpr void _Verify_offset(const difference_type _Off) const { - current._Verify_offset(_Off); + _Current._Verify_offset(_Off); } template , int> = 0> _NODISCARD constexpr move_iterator<_Unwrapped_t> _Unwrapped() const& { - return static_cast>>(current._Unwrapped()); + return static_cast>>(_Current._Unwrapped()); } template , int> = 0> _NODISCARD constexpr move_iterator<_Unwrapped_t<_Iter2>> _Unwrapped() && { - return static_cast>>(_STD move(current)._Unwrapped()); + return static_cast>>(_STD move(_Current)._Unwrapped()); } static constexpr bool _Unwrap_when_unverified = _Do_unwrap_when_unverified_v; template , int> = 0> constexpr void _Seek_to(const move_iterator<_Src>& _It) { - current._Seek_to(_It.base()); + _Current._Seek_to(_It.base()); } template , int> = 0> constexpr void _Seek_to(move_iterator<_Src>&& _It) { - current._Seek_to(_STD move(_It).base()); + _Current._Seek_to(_STD move(_It).base()); } -protected: - iterator_type current{}; +private: + iterator_type _Current{}; }; template @@ -4284,6 +4336,10 @@ _OutIt _Copy_memmove(move_iterator<_InIt> _First, move_iterator<_InIt> _Last, _O } #if _HAS_IF_CONSTEXPR +// VARIABLE TEMPLATE _Is_vb_iterator +template +_INLINE_VAR constexpr bool _Is_vb_iterator = false; + template _CONSTEXPR20 _OutIt _Copy_unchecked(_InIt _First, _InIt _Last, _OutIt _Dest) { // copy [_First, _Last) to [_Dest, ...) @@ -4417,7 +4473,7 @@ _OutIt copy_n(_InIt _First, _Diff _Count_raw, _OutIt _Dest) { // copy [_First, _ const _Algorithm_int_t<_Diff> _Count = _Count_raw; if (0 < _Count) { auto _UFirst = _Get_unwrapped_n(_First, _Count); - auto _UDest = _Get_unwrapped_n(_Dest, _Count); + auto _UDest = _Get_unwrapped_n(_Dest, _Count); _Seek_wrapped( _Dest, _Copy_n_unchecked4(_UFirst, _Count, _UDest, bool_constant<_Ptr_copy_cat::_Trivially_copyable>{})); @@ -4504,9 +4560,9 @@ _BidIt2 _Copy_backward_unchecked(_BidIt1 _First, _BidIt1 _Last, _BidIt2 _Dest, t template _BidIt2 copy_backward(_BidIt1 _First, _BidIt1 _Last, _BidIt2 _Dest) { // copy [_First, _Last) backwards to [..., _Dest) _Adl_verify_range(_First, _Last); - auto _UFirst = _Get_unwrapped(_First); + auto _UFirst = _Get_unwrapped(_First); const auto _ULast = _Get_unwrapped(_Last); - auto _UDest = _Get_unwrapped_n(_Dest, -_Idl_distance<_BidIt1>(_UFirst, _ULast)); + auto _UDest = _Get_unwrapped_n(_Dest, -_Idl_distance<_BidIt1>(_UFirst, _ULast)); _Seek_wrapped(_Dest, _Copy_backward_unchecked(_UFirst, _ULast, _UDest, bool_constant<_Ptr_copy_cat::_Trivially_copyable>{})); return _Dest; @@ -4699,20 +4755,24 @@ template _CONSTEXPR20 void fill(const _FwdIt _First, const _FwdIt _Last, const _Ty& _Val) { // copy _Val through [_First, _Last) _Adl_verify_range(_First, _Last); - auto _UFirst = _Get_unwrapped(_First); - const auto _ULast = _Get_unwrapped(_Last); - if constexpr (_Fill_memset_is_safe) { + if constexpr (_Is_vb_iterator<_FwdIt, true>) { + _Fill_vbool(_First, _Last, _Val); + } else { + auto _UFirst = _Get_unwrapped(_First); + const auto _ULast = _Get_unwrapped(_Last); + if constexpr (_Fill_memset_is_safe) { #ifdef __cpp_lib_is_constant_evaluated - if (!_STD is_constant_evaluated()) + if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated - { - _CSTD memset(_UFirst, static_cast(_Val), static_cast(_ULast - _UFirst)); - return; + { + _CSTD memset(_UFirst, static_cast(_Val), static_cast(_ULast - _UFirst)); + return; + } } - } - for (; _UFirst != _ULast; ++_UFirst) { - *_UFirst = _Val; + for (; _UFirst != _ULast; ++_UFirst) { + *_UFirst = _Val; + } } } #else // ^^^ _HAS_IF_CONSTEXPR // !_HAS_IF_CONSTEXPR vvv @@ -4755,26 +4815,31 @@ _CONSTEXPR20 _OutIt fill_n(_OutIt _Dest, const _Diff _Count_raw, const _Ty& _Val // copy _Val _Count times through [_Dest, ...) _Algorithm_int_t<_Diff> _Count = _Count_raw; if (0 < _Count) { - auto _UDest = _Get_unwrapped_n(_Dest, _Count); - if constexpr (_Fill_memset_is_safe) { + if constexpr (_Is_vb_iterator<_OutIt, true>) { + const auto _Last = _Dest + static_cast(_Count); + _Fill_vbool(_Dest, _Last, _Val); + return _Last; + } else { + auto _UDest = _Get_unwrapped_n(_Dest, _Count); + if constexpr (_Fill_memset_is_safe) { #ifdef __cpp_lib_is_constant_evaluated - if (!_STD is_constant_evaluated()) + if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated - { - _CSTD memset(_UDest, static_cast(_Val), static_cast(_Count)); - _UDest += _Count; - _Seek_wrapped(_Dest, _UDest); - return _Dest; + { + _CSTD memset(_UDest, static_cast(_Val), static_cast(_Count)); + _UDest += _Count; + _Seek_wrapped(_Dest, _UDest); + return _Dest; + } } - } - for (; 0 < _Count; --_Count, (void) ++_UDest) { - *_UDest = _Val; - } + for (; 0 < _Count; --_Count, (void) ++_UDest) { + *_UDest = _Val; + } - _Seek_wrapped(_Dest, _UDest); + _Seek_wrapped(_Dest, _UDest); + } } - return _Dest; } @@ -4955,7 +5020,7 @@ bool _Equal_unchecked(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _F // compare [_First1, _Last1) to [_First2, ...), memcmp optimization const auto _First1_ch = reinterpret_cast(_First1); const auto _First2_ch = reinterpret_cast(_First2); - const auto _Count = static_cast(reinterpret_cast(_Last1) - _First1_ch); + const auto _Count = static_cast(reinterpret_cast(_Last1) - _First1_ch); return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; } @@ -4964,7 +5029,7 @@ _NODISCARD bool equal(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _F // compare [_First1, _Last1) to [_First2, ...) _Adl_verify_range(_First1, _Last1); const auto _UFirst1 = _Get_unwrapped(_First1); - const auto _ULast1 = _Get_unwrapped(_Last1); + const auto _ULast1 = _Get_unwrapped(_Last1); const auto _UFirst2 = _Get_unwrapped_n(_First2, _Idl_distance<_InIt1>(_UFirst1, _ULast1)); return _Equal_unchecked(_UFirst1, _ULast1, _UFirst2, _Pass_fn(_Pred)); } @@ -5481,116 +5546,42 @@ _NODISCARD _CONSTEXPR20 bool _Check_match_counts( } // FUNCTION TEMPLATE reverse -#if _HAS_IF_CONSTEXPR template _CONSTEXPR20 void reverse(const _BidIt _First, const _BidIt _Last) { // reverse elements in [_First, _Last) _Adl_verify_range(_First, _Last); auto _UFirst = _Get_unwrapped(_First); auto _ULast = _Get_unwrapped(_Last); -#if _USE_STD_VECTOR_ALGORITHMS +#if _HAS_IF_CONSTEXPR && _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); - if constexpr (_Allow_vectorization && sizeof(_Elem) == 1) { +#pragma warning(suppress : 6326) // Potential comparison of a constant with another constant + if constexpr (_Allow_vectorization && _Nx <= 8 && (_Nx & (_Nx - 1)) == 0) { #ifdef __cpp_lib_is_constant_evaluated if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - __std_reverse_trivially_swappable_1(_UFirst, _ULast); - return; - } - } else if constexpr (_Allow_vectorization && sizeof(_Elem) == 2) { -#ifdef __cpp_lib_is_constant_evaluated - if (!_STD is_constant_evaluated()) -#endif // __cpp_lib_is_constant_evaluated - { - __std_reverse_trivially_swappable_2(_UFirst, _ULast); - return; - } - } else if constexpr (_Allow_vectorization && sizeof(_Elem) == 4) { -#ifdef __cpp_lib_is_constant_evaluated - if (!_STD is_constant_evaluated()) -#endif // __cpp_lib_is_constant_evaluated - { - __std_reverse_trivially_swappable_4(_UFirst, _ULast); - return; - } - } else if constexpr (_Allow_vectorization && sizeof(_Elem) == 8) { -#ifdef __cpp_lib_is_constant_evaluated - if (!_STD is_constant_evaluated()) -#endif // __cpp_lib_is_constant_evaluated - { - __std_reverse_trivially_swappable_8(_UFirst, _ULast); + if constexpr (_Nx == 1) { + __std_reverse_trivially_swappable_1(_UFirst, _ULast); + } else if constexpr (_Nx == 2) { + __std_reverse_trivially_swappable_2(_UFirst, _ULast); + } else if constexpr (_Nx == 4) { + __std_reverse_trivially_swappable_4(_UFirst, _ULast); + } else { + __std_reverse_trivially_swappable_8(_UFirst, _ULast); + } + return; } } -#endif // _USE_STD_VECTOR_ALGORITHMS +#endif // _HAS_IF_CONSTEXPR && _USE_STD_VECTOR_ALGORITHMS for (; _UFirst != _ULast && _UFirst != --_ULast; ++_UFirst) { _STD iter_swap(_UFirst, _ULast); } } -#else // ^^^ _HAS_IF_CONSTEXPR / !_HAS_IF_CONSTEXPR vvv -template -void _Reverse_unchecked1(_BidIt _First, _BidIt _Last, integral_constant) { - // reverse elements in [_First, _Last), general bidirectional iterators - for (; _First != _Last && _First != --_Last; ++_First) { - _STD iter_swap(_First, _Last); - } -} - -#if _USE_STD_VECTOR_ALGORITHMS -template -void _Reverse_unchecked1(const _BidIt _First, const _BidIt _Last, integral_constant) { - // reverse elements in [_First, _Last), pointers to trivially swappable of size 1 - __std_reverse_trivially_swappable_1(_First, _Last); -} - -template -void _Reverse_unchecked1(const _BidIt _First, const _BidIt _Last, integral_constant) { - // reverse elements in [_First, _Last), pointers to trivially swappable of size 2 - __std_reverse_trivially_swappable_2(_First, _Last); -} - -template -void _Reverse_unchecked1(const _BidIt _First, const _BidIt _Last, integral_constant) { - // reverse elements in [_First, _Last), pointers to trivially swappable of size 4 - __std_reverse_trivially_swappable_4(_First, _Last); -} - -template -void _Reverse_unchecked1(const _BidIt _First, const _BidIt _Last, integral_constant) { - // reverse elements in [_First, _Last), pointers to trivially swappable of size 8 - __std_reverse_trivially_swappable_8(_First, _Last); -} -#endif // _USE_STD_VECTOR_ALGORITHMS - -template -void _Reverse_unchecked(const _BidIt _First, const _BidIt _Last) { - // reverse elements in [_First, _Last), choose optimization -#if _USE_STD_VECTOR_ALGORITHMS - using _Elem = remove_pointer_t<_BidIt>; - constexpr size_t _Opt = - is_pointer_v<_BidIt> // - && _Is_trivially_swappable_v<_Elem> // - && !is_volatile_v<_Elem> // - && (sizeof(_Elem) == 1 || sizeof(_Elem) == 2 || sizeof(_Elem) == 4 || sizeof(_Elem) == 8) - ? sizeof(_Elem) - : 0; -#else // ^^^ vectorize / no vectorize vvv - constexpr size_t _Opt = 0; -#endif // _USE_STD_VECTOR_ALGORITHMS - _Reverse_unchecked1(_First, _Last, integral_constant{}); -} - -template -void reverse(const _BidIt _First, const _BidIt _Last) { - // reverse elements in [_First, _Last) - _Adl_verify_range(_First, _Last); - _Reverse_unchecked(_Get_unwrapped(_First), _Get_unwrapped(_Last)); -} -#endif // _HAS_IF_CONSTEXPR #if _HAS_CXX17 template = 0> @@ -5694,19 +5685,19 @@ _FwdIt _Rotate_unchecked1(_FwdIt _First, _FwdIt _Mid, _FwdIt _Last, forward_iter template _BidIt _Rotate_unchecked1(_BidIt _First, _BidIt _Mid, _BidIt _Last, bidirectional_iterator_tag) { // rotate [_First, _Last) left by distance(_First, _Mid) positions, bidirectional iterators - _Reverse_unchecked(_First, _Mid); - _Reverse_unchecked(_Mid, _Last); + _STD reverse(_First, _Mid); + _STD reverse(_Mid, _Last); auto _Tmp = _Reverse_until_sentinel_unchecked(_First, _Mid, _Last); - _Reverse_unchecked(_Tmp.first, _Tmp.second); + _STD reverse(_Tmp.first, _Tmp.second); return _Mid != _Tmp.first ? _Tmp.first : _Tmp.second; } template _RanIt _Rotate_unchecked1(_RanIt _First, _RanIt _Mid, _RanIt _Last, random_access_iterator_tag) { // rotate [_First, _Last) left by distance(_First, _Mid) positions, random-access iterators - _Reverse_unchecked(_First, _Mid); - _Reverse_unchecked(_Mid, _Last); - _Reverse_unchecked(_First, _Last); + _STD reverse(_First, _Mid); + _STD reverse(_Mid, _Last); + _STD reverse(_First, _Last); return _First + (_Last - _Mid); } @@ -5927,7 +5918,53 @@ struct _CXX17_DEPRECATE_ITERATOR_BASE_CLASS iterator { // base type for iterator using pointer = _Pointer; using reference = _Reference; }; + +// STRUCT TEMPLATE _Float_traits +template +struct _Float_traits { + static_assert(is_floating_point_v<_Ty>, "_Float_traits is invalid"); + + // traits for double and long double: + using type = unsigned long long; + + static constexpr type _Magnitude_mask = 0x7fff'ffff'ffff'ffffULL; + static constexpr type _Exponent_mask = 0x7ff0'0000'0000'0000ULL; +}; + +template <> +struct _Float_traits { + using type = unsigned int; + + static constexpr type _Magnitude_mask = 0x7fff'ffffU; + static constexpr type _Exponent_mask = 0x7f80'0000U; +}; + +// FUNCTION TEMPLATE _Float_abs_bits +template , int> = 0> +_NODISCARD _CONSTEXPR_BIT_CAST auto _Float_abs_bits(const _Ty& _Xx) { + const auto _Bits = _Bit_cast::type>(_Xx); + return _Bits & _Float_traits<_Ty>::_Magnitude_mask; +} + +// FUNCTION TEMPLATE _Float_abs +template , int> = 0> +_NODISCARD _CONSTEXPR_BIT_CAST _Ty _Float_abs(const _Ty _Xx) { // constexpr floating point abs() + return _Bit_cast<_Ty>(_Float_abs_bits(_Xx)); +} + +// FUNCTION TEMPLATE _Is_nan +template , int> = 0> +_NODISCARD _CONSTEXPR_BIT_CAST bool _Is_nan(const _Ty _Xx) { // constexpr isnan() + return _Float_abs_bits(_Xx) > _Float_traits<_Ty>::_Exponent_mask; +} + +// FUNCTION TEMPLATE _Is_finite +template , int> = 0> +_NODISCARD _CONSTEXPR_BIT_CAST bool _Is_finite(const _Ty _Xx) { // constexpr isfinite() + return _Float_abs_bits(_Xx) < _Float_traits<_Ty>::_Exponent_mask; +} _STD_END +#undef _CONSTEXPR_BIT_CAST #pragma pop_macro("new") _STL_RESTORE_CLANG_WARNINGS #pragma warning(pop) diff --git a/stl/inc/yvals.h b/stl/inc/yvals.h index 1a70aa17ea4..ea0b53f4571 100644 --- a/stl/inc/yvals.h +++ b/stl/inc/yvals.h @@ -306,6 +306,22 @@ _STL_DISABLE_CLANG_WARNINGS #define _LOCK_DEBUG 3 #define _LOCK_AT_THREAD_EXIT 4 +#ifndef _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE +#if _STL_WIN32_WINNT >= _STL_WIN32_WINNT_WIN8 +#define _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE 1 +#else // ^^^ _STL_WIN32_WINNT >= _STL_WIN32_WINNT_WIN8 // _STL_WIN32_WINNT < _STL_WIN32_WINNT_WIN8 vvv +#define _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE 0 +#endif // ^^^ _STL_WIN32_WINNT < _STL_WIN32_WINNT_WIN8 ^^^ +#endif // _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE + +#ifndef _ALLOW_ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE_MISMATCH +#if _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE +#pragma detect_mismatch("_ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE", "1") +#else +#pragma detect_mismatch("_ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE", "0") +#endif +#endif // !_ALLOW_ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE_MISMATCH + #ifdef __cplusplus _STD_BEGIN enum _Uninitialized { // tag for suppressing initialization diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 61292ad030b..3239d8eb259 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -164,7 +164,6 @@ // (partially implemented) // P0769R2 shift_left(), shift_right() // P0811R3 midpoint(), lerp() -// (partially implemented, lerp() not yet constexpr) // P0879R0 constexpr For Swapping Functions // P0887R1 type_identity // P0896R4 Ranges @@ -174,6 +173,7 @@ // (partially implemented, missing noop coroutines) // P0919R3 Heterogeneous Lookup For Unordered Containers // P0966R1 string::reserve() Should Not Shrink +// P1001R2 execution::unseq // P1006R1 constexpr For pointer_traits::pointer_to() // P1023R0 constexpr For std::array Comparisons // P1024R3 Enhancing span Usability @@ -204,6 +204,7 @@ // P1871R1 disable_sized_sentinel_for // P1872R0 span Should Have size_type, Not index_type // P1878R1 Constraining Readable Types +// P1907R2 ranges::ssize // P1956R1 has_single_bit(), bit_ceil(), bit_floor(), bit_width() // P1959R0 Removing weak_equality And strong_equality // P1964R2 Replacing boolean With boolean-testable @@ -225,6 +226,10 @@ // C++ allows an implementation to implement parallel algorithms as calls to the serial algorithms. // This implementation parallelizes several common algorithm calls, but not all. // +// std::execution::unseq has no direct analogue for any optimizer we target as of 2020-07-29, +// though we will map it to #pragma loop(ivdep) for the for_each algorithms only as these are the only algorithms where +// the library does not need to introduce inter-loop-body dependencies to accomplish the algorithm's goals. +// // The following algorithms are parallelized. // * adjacent_difference // * adjacent_find @@ -485,7 +490,7 @@ #define _CPPLIB_VER 650 #define _MSVC_STL_VERSION 142 -#define _MSVC_STL_UPDATE 202007L +#define _MSVC_STL_UPDATE 202008L #ifndef _ALLOW_COMPILER_AND_STL_VERSION_MISMATCH #ifdef __EDG__ @@ -495,8 +500,8 @@ #error STL1000: Unexpected compiler version, expected Clang 10.0.0 or newer. #endif // ^^^ old Clang ^^^ #elif defined(_MSC_VER) -#if _MSC_VER < 1927 // Coarse-grained, not inspecting _MSC_FULL_VER -#error STL1001: Unexpected compiler version, expected MSVC 19.27 or newer. +#if _MSC_VER < 1928 // Coarse-grained, not inspecting _MSC_FULL_VER +#error STL1001: Unexpected compiler version, expected MSVC 19.28 or newer. #endif // ^^^ old MSVC ^^^ #else // vvv other compilers vvv // not attempting to detect other compilers @@ -1091,10 +1096,7 @@ #if _HAS_STD_BYTE #define __cpp_lib_byte 201603L #endif // _HAS_STD_BYTE -#define __cpp_lib_clamp 201603L -#ifndef _M_CEE -#define __cpp_lib_execution 201603L -#endif // _M_CEE +#define __cpp_lib_clamp 201603L #define __cpp_lib_filesystem 201703L #define __cpp_lib_gcd_lcm 201606L #define __cpp_lib_hardware_interference_size 201703L @@ -1136,6 +1138,7 @@ #define __cpp_lib_atomic_float 201711L #define __cpp_lib_atomic_lock_free_type_aliases 201907L #define __cpp_lib_atomic_shared_ptr 201711L +#define __cpp_lib_atomic_wait 201907L #define __cpp_lib_bind_front 201907L #define __cpp_lib_bit_cast 201806L #define __cpp_lib_bitops 201907L @@ -1145,9 +1148,9 @@ #define __cpp_lib_char8_t 201907L #endif // __cpp_char8_t -#if defined(__cpp_concepts) && __cpp_concepts > 201507L +#ifndef __EDG__ // TRANSITION, EDG concepts support #define __cpp_lib_concepts 201907L -#endif // defined(__cpp_concepts) && __cpp_concepts > 201507L +#endif // __EDG__ #define __cpp_lib_constexpr_algorithms 201806L #define __cpp_lib_constexpr_complex 201711L @@ -1159,7 +1162,7 @@ #define __cpp_lib_constexpr_tuple 201811L #define __cpp_lib_constexpr_utility 201811L -#ifdef __cpp_impl_coroutine // TRANSITION, VS 2019 16.8 Preview 1 +#ifdef __cpp_impl_coroutine // TRANSITION, VS 2019 16.8 Preview 3 #define __cpp_lib_coroutine 197000L #endif // __cpp_impl_coroutine @@ -1169,6 +1172,7 @@ #define __cpp_lib_generic_unordered_lookup 201811L #define __cpp_lib_int_pow2 202002L #define __cpp_lib_integer_comparison_functions 202002L +#define __cpp_lib_interpolate 201902L #define __cpp_lib_is_constant_evaluated 201811L #define __cpp_lib_is_nothrow_convertible 201806L #define __cpp_lib_list_remove_return_type 201806L @@ -1184,6 +1188,14 @@ #define __cpp_lib_unwrap_ref 201811L #endif // _HAS_CXX20 +#ifndef _M_CEE +#if _HAS_CXX20 +#define __cpp_lib_execution 201902L // P1001R2 execution::unseq +#elif _HAS_CXX17 +#define __cpp_lib_execution 201603L // P0024R2 Parallel Algorithms +#endif // language mode +#endif // _M_CEE + #if _HAS_CXX20 #define __cpp_lib_array_constexpr 201811L // P1032R1 Miscellaneous constexpr #elif _HAS_CXX17 // ^^^ _HAS_CXX20 / _HAS_CXX17 vvv @@ -1250,5 +1262,20 @@ compiler option, or define _ALLOW_RTCc_IN_STL to acknowledge that you have recei #error In yvals_core.h, defined(MRTDLL) implies defined(_M_CEE_PURE); !defined(_M_CEE_PURE) implies !defined(MRTDLL) #endif // defined(MRTDLL) && !defined(_M_CEE_PURE) +#define _STL_WIN32_WINNT_WINXP 0x0501 // _WIN32_WINNT_WINXP from sdkddkver.h +#define _STL_WIN32_WINNT_VISTA 0x0600 // _WIN32_WINNT_VISTA from sdkddkver.h +#define _STL_WIN32_WINNT_WIN8 0x0602 // _WIN32_WINNT_WIN8 from sdkddkver.h + +// Note that the STL DLL builds will set this to XP for ABI compatibility with VS2015 which supported XP. +#ifndef _STL_WIN32_WINNT +#if defined(_M_ARM) || defined(_M_ARM64) || defined(_ONECORE) || defined(_CRT_APP) +// The first ARM or OneCore or App Windows was Windows 8 +#define _STL_WIN32_WINNT _STL_WIN32_WINNT_WIN8 +#else // ^^^ default to Win8 // default to Vista vvv +// The earliest Windows supported by this implementation is Windows Vista +#define _STL_WIN32_WINNT _STL_WIN32_WINNT_VISTA +#endif // ^^^ !defined(_M_ARM) && !defined(_M_ARM64) && !defined(_ONECORE) && !defined(_CRT_APP) ^^^ +#endif // _STL_WIN32_WINNT + #endif // _STL_COMPILER_PREPROCESSOR #endif // _YVALS_CORE_H_ diff --git a/stl/msbuild/stl_atomic_wait/dirs.proj b/stl/msbuild/stl_atomic_wait/dirs.proj new file mode 100644 index 00000000000..7d349d37ffe --- /dev/null +++ b/stl/msbuild/stl_atomic_wait/dirs.proj @@ -0,0 +1,15 @@ + + + + + + + + + + + + diff --git a/stl/msbuild/stl_atomic_wait/md/dirs.proj b/stl/msbuild/stl_atomic_wait/md/dirs.proj new file mode 100644 index 00000000000..8376c0a1913 --- /dev/null +++ b/stl/msbuild/stl_atomic_wait/md/dirs.proj @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/stl/msbuild/stl_atomic_wait/md/msvcp_atomic_wait_app/msvcp_atomic_wait.nativeproj b/stl/msbuild/stl_atomic_wait/md/msvcp_atomic_wait_app/msvcp_atomic_wait.nativeproj new file mode 100644 index 00000000000..e22fbab9c1b --- /dev/null +++ b/stl/msbuild/stl_atomic_wait/md/msvcp_atomic_wait_app/msvcp_atomic_wait.nativeproj @@ -0,0 +1,15 @@ + + + + + + md + app + + + + + diff --git a/stl/msbuild/stl_atomic_wait/md/msvcp_atomic_wait_kernel32/msvcp_atomic_wait.nativeproj b/stl/msbuild/stl_atomic_wait/md/msvcp_atomic_wait_kernel32/msvcp_atomic_wait.nativeproj new file mode 100644 index 00000000000..e009997783c --- /dev/null +++ b/stl/msbuild/stl_atomic_wait/md/msvcp_atomic_wait_kernel32/msvcp_atomic_wait.nativeproj @@ -0,0 +1,15 @@ + + + + + + md + kernel32 + + + + + diff --git a/stl/msbuild/stl_atomic_wait/md/msvcp_atomic_wait_netfx/msvcp_atomic_wait.nativeproj b/stl/msbuild/stl_atomic_wait/md/msvcp_atomic_wait_netfx/msvcp_atomic_wait.nativeproj new file mode 100644 index 00000000000..5b3f631fc00 --- /dev/null +++ b/stl/msbuild/stl_atomic_wait/md/msvcp_atomic_wait_netfx/msvcp_atomic_wait.nativeproj @@ -0,0 +1,15 @@ + + + + + + md + netfx + + + + + diff --git a/stl/msbuild/stl_atomic_wait/md/msvcp_atomic_wait_onecore/msvcp_atomic_wait.nativeproj b/stl/msbuild/stl_atomic_wait/md/msvcp_atomic_wait_onecore/msvcp_atomic_wait.nativeproj new file mode 100644 index 00000000000..774d8aa1b5a --- /dev/null +++ b/stl/msbuild/stl_atomic_wait/md/msvcp_atomic_wait_onecore/msvcp_atomic_wait.nativeproj @@ -0,0 +1,15 @@ + + + + + + md + onecore + + + + + diff --git a/stl/msbuild/stl_atomic_wait/msvcp_atomic_wait.settings.targets b/stl/msbuild/stl_atomic_wait/msvcp_atomic_wait.settings.targets new file mode 100644 index 00000000000..9590f000309 --- /dev/null +++ b/stl/msbuild/stl_atomic_wait/msvcp_atomic_wait.settings.targets @@ -0,0 +1,90 @@ + + + + + + p_atomic_wait + + true + true + true + + DYNLINK + + + + + + _clr + + _app + _clr$(NetFxVerX) + + msvcp$(VCToolsProdVerSuffix)$(BuildSuffix)_atomic_wait$(MsvcpFlavorSuffix) + msvcprt$(BuildSuffix)_atomic_wait$(ClrLibSuffix) + $(LibOutputFileName).lib + + _VCRT_ALLOW_INTERNALS;$(ClDefines) + $(ClDefines);_CRT_APP + + false + true + $(IntermediateOutputDirectory) + $(CrtBuildDir)\msvcprt_atomic_wait$(BuildSuffix).$(MsvcpFlavor).import_only.lib + $(LibOutputFileName).$(MsvcpFlavor) + $(IntermediateOutputDirectory)\$(DllDefName).def + + true + $(OutputPath)\$(OutputName)$(_PDB_VER_NAME_)$(DllPdbFlavorSuffix) + + -debugtype:cv,fixup $(LinkAdditionalOptions) + -opt:ref,icf=3 $(LinkAdditionalOptions) + -opt:ref,noicf $(LinkAdditionalOptions) + -nodefaultlib:libcpmt$(BuildSuffix).lib $(LinkAdditionalOptions) + -nodefaultlib:$(LibOutputFile) $(LinkAdditionalOptions) + + true + true + + + + + LIBRARYNAME=$(OutputName.ToUpper()) + + + $(IntermediateOutputDirectory) + $(IntermediateOutputDirectory) + $(DllDefName) + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/stl/msbuild/stl_atomic_wait/msvcprt_atomic_wait.rc b/stl/msbuild/stl_atomic_wait/msvcprt_atomic_wait.rc new file mode 100644 index 00000000000..b43a7e1238a --- /dev/null +++ b/stl/msbuild/stl_atomic_wait/msvcprt_atomic_wait.rc @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +////////////////////////////////////////////////////////////////////////////////////// +// +// msvcprt_atomic_wait.rc : Defines the version resource for the C++ Runtime Library "_atomic_wait" DLL +// +////////////////////////////////////////////////////////////////////////////////////// + +#include "winver.h" // extract from windows header +#include "verstamp.h" + +#define MKARGSTR2(X) #X +#define MKARGSTR(X) MKARGSTR2(X) + +#define VER_FILETYPE VFT_DLL +#define VER_FILESUBTYPE VFT_UNKNOWN + +#define VER_FILEDESCRIPTION_STR "Microsoft\256 C Runtime Library _atomic_wait\0" +#define VER_INTERNALNAME_STR MKARGSTR(SXS_TARGET) +#define VER_ORIGINALFILENAME_STR MKARGSTR(SXS_TARGET) + +#include 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 new file mode 100644 index 00000000000..0de6759e9cd --- /dev/null +++ b/stl/msbuild/stl_atomic_wait/stl_atomic_wait.files.settings.targets @@ -0,0 +1,15 @@ + + + + + + nativecpp + + + diff --git a/stl/msbuild/stl_atomic_wait/xmd/dirs.proj b/stl/msbuild/stl_atomic_wait/xmd/dirs.proj new file mode 100644 index 00000000000..c6bf75b3668 --- /dev/null +++ b/stl/msbuild/stl_atomic_wait/xmd/dirs.proj @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + diff --git a/stl/msbuild/stl_atomic_wait/xmd/msvcp_atomic_wait_app/msvcp_atomic_wait.nativeproj b/stl/msbuild/stl_atomic_wait/xmd/msvcp_atomic_wait_app/msvcp_atomic_wait.nativeproj new file mode 100644 index 00000000000..6d99c3ab360 --- /dev/null +++ b/stl/msbuild/stl_atomic_wait/xmd/msvcp_atomic_wait_app/msvcp_atomic_wait.nativeproj @@ -0,0 +1,15 @@ + + + + + + xmd + app + + + + + diff --git a/stl/msbuild/stl_atomic_wait/xmd/msvcp_atomic_wait_kernel32/msvcp_atomic_wait.nativeproj b/stl/msbuild/stl_atomic_wait/xmd/msvcp_atomic_wait_kernel32/msvcp_atomic_wait.nativeproj new file mode 100644 index 00000000000..9fe52b880d3 --- /dev/null +++ b/stl/msbuild/stl_atomic_wait/xmd/msvcp_atomic_wait_kernel32/msvcp_atomic_wait.nativeproj @@ -0,0 +1,15 @@ + + + + + + xmd + kernel32 + + + + + diff --git a/stl/msbuild/stl_atomic_wait/xmd/msvcp_atomic_wait_netfx/msvcp_atomic_wait.nativeproj b/stl/msbuild/stl_atomic_wait/xmd/msvcp_atomic_wait_netfx/msvcp_atomic_wait.nativeproj new file mode 100644 index 00000000000..e0629c9e503 --- /dev/null +++ b/stl/msbuild/stl_atomic_wait/xmd/msvcp_atomic_wait_netfx/msvcp_atomic_wait.nativeproj @@ -0,0 +1,15 @@ + + + + + + xmd + netfx + + + + + diff --git a/stl/msbuild/stl_atomic_wait/xmd/msvcp_atomic_wait_onecore/msvcp_atomic_wait.nativeproj b/stl/msbuild/stl_atomic_wait/xmd/msvcp_atomic_wait_onecore/msvcp_atomic_wait.nativeproj new file mode 100644 index 00000000000..54f964b787a --- /dev/null +++ b/stl/msbuild/stl_atomic_wait/xmd/msvcp_atomic_wait_onecore/msvcp_atomic_wait.nativeproj @@ -0,0 +1,15 @@ + + + + + + xmd + onecore + + + + + diff --git a/stl/msbuild/stl_base/stl.files.settings.targets b/stl/msbuild/stl_base/stl.files.settings.targets index 4c371170a5f..5fd1b263103 100644 --- a/stl/msbuild/stl_base/stl.files.settings.targets +++ b/stl/msbuild/stl_base/stl.files.settings.targets @@ -12,8 +12,10 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception nativecpp @@ -170,7 +172,6 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception $(CrtRoot)\github\stl\src\filesystem.cpp; $(CrtRoot)\github\stl\src\locale0_implib.cpp; $(CrtRoot)\github\stl\src\nothrow.cpp; - $(CrtRoot)\github\stl\src\parallel_algorithms.cpp; $(CrtRoot)\github\stl\src\sharedmutex.cpp; $(CrtRoot)\github\stl\src\syserror_import_lib.cpp; $(CrtRoot)\github\stl\src\vector_algorithms.cpp; diff --git a/stl/msbuild/stl_post/msvcp_post.settings.targets b/stl/msbuild/stl_post/msvcp_post.settings.targets index 6d09f6244f5..3271bca0e9f 100644 --- a/stl/msbuild/stl_post/msvcp_post.settings.targets +++ b/stl/msbuild/stl_post/msvcp_post.settings.targets @@ -50,10 +50,13 @@ SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception Include="$(CrtRoot)\github\stl\msbuild\stl_2\$(CrtBuildModel)\msvcp_2_$(MsvcpFlavor)\msvcp_2.nativeproj"/> + + diff --git a/stl/src/atomic_wait.cpp b/stl/src/atomic_wait.cpp new file mode 100644 index 00000000000..d16f53f2197 --- /dev/null +++ b/stl/src/atomic_wait.cpp @@ -0,0 +1,334 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// implement atomic wait / notify_one / notify_all + +// clang-format off + +#include +#include +#include +#include +#include + +// clang-format on + +namespace { + + constexpr size_t _Wait_table_size_power = 8; + constexpr size_t _Wait_table_size = 1 << _Wait_table_size_power; + constexpr size_t _Wait_table_index_mask = _Wait_table_size - 1; + + struct _Wait_context { + const void* _Storage; // Pointer to wait on + _Wait_context* _Next; + _Wait_context* _Prev; + CONDITION_VARIABLE _Condition; + }; + + struct _Guarded_wait_context : _Wait_context { + _Guarded_wait_context(const void* _Storage_, _Wait_context* const _Head) noexcept + : _Wait_context{_Storage_, _Head, _Head->_Prev, CONDITION_VARIABLE_INIT} { + _Prev->_Next = this; + _Next->_Prev = this; + } + + ~_Guarded_wait_context() { + const auto _Next_local = _Next; + const auto _Prev_local = _Prev; + _Next->_Prev = _Prev_local; + _Prev->_Next = _Next_local; + } + + _Guarded_wait_context(const _Guarded_wait_context&) = delete; + _Guarded_wait_context& operator=(const _Guarded_wait_context&) = delete; + }; + + class _SrwLock_guard { + public: + explicit _SrwLock_guard(SRWLOCK& _Locked_) noexcept : _Locked(&_Locked_) { + AcquireSRWLockExclusive(_Locked); + } + + ~_SrwLock_guard() { + ReleaseSRWLockExclusive(_Locked); + } + + _SrwLock_guard(const _SrwLock_guard&) = delete; + _SrwLock_guard& operator=(const _SrwLock_guard&) = delete; + + private: + SRWLOCK* _Locked; + }; + + +#pragma warning(push) +#pragma warning(disable : 4324) // structure was padded due to alignment specifier + struct alignas(_STD hardware_destructive_interference_size) _Wait_table_entry { + SRWLOCK _Lock = SRWLOCK_INIT; + _Wait_context _Wait_list_head = {nullptr, &_Wait_list_head, &_Wait_list_head, CONDITION_VARIABLE_INIT}; + + constexpr _Wait_table_entry() noexcept = default; + }; +#pragma warning(pop) + + [[nodiscard]] _Wait_table_entry& _Atomic_wait_table_entry(const void* const _Storage) noexcept { + static _Wait_table_entry wait_table[_Wait_table_size]; + auto index = reinterpret_cast<_STD uintptr_t>(_Storage); + index ^= index >> (_Wait_table_size_power * 2); + index ^= index >> _Wait_table_size_power; + return wait_table[index & _Wait_table_index_mask]; + } + + void _Assume_timeout() noexcept { +#ifdef _DEBUG + if (GetLastError() != ERROR_TIMEOUT) { + _CSTD abort(); + } +#endif // _DEBUG + } + +#if _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE + +#define __crtWaitOnAddress WaitOnAddress +#define __crtWakeByAddressSingle WakeByAddressSingle +#define __crtWakeByAddressAll WakeByAddressAll + +#else // ^^^ _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE / !_ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE vvv + + + struct _Wait_functions_table { + _STD atomic _Pfn_WaitOnAddress{nullptr}; + _STD atomic _Pfn_WakeByAddressSingle{nullptr}; + _STD atomic _Pfn_WakeByAddressAll{nullptr}; + _STD atomic<__std_atomic_api_level> _Api_level{__std_atomic_api_level::__not_set}; + }; + + _Wait_functions_table _Wait_functions; + + void _Force_wait_functions_srwlock_only() noexcept { + auto _Local = _Wait_functions._Api_level.load(_STD memory_order_acquire); + if (_Local <= __std_atomic_api_level::__detecting) { + while (!_Wait_functions._Api_level.compare_exchange_weak(_Local, __std_atomic_api_level::__has_srwlock)) { + if (_Local > __std_atomic_api_level::__detecting) { + return; + } + } + } + } + + [[nodiscard]] __std_atomic_api_level _Init_wait_functions(__std_atomic_api_level _Level) { + while (!_Wait_functions._Api_level.compare_exchange_weak(_Level, __std_atomic_api_level::__detecting)) { + if (_Level > __std_atomic_api_level::__detecting) { + return _Level; + } + } + + _Level = __std_atomic_api_level::__has_srwlock; + + const HMODULE _Sync_module = GetModuleHandleW(L"api-ms-win-core-synch-l1-2-0.dll"); + if (_Sync_module != nullptr) { + const auto _Wait_on_address = + reinterpret_cast(GetProcAddress(_Sync_module, "WaitOnAddress")); + const auto _Wake_by_address_single = + reinterpret_cast(GetProcAddress(_Sync_module, "WakeByAddressSingle")); + const auto _Wake_by_address_all = + reinterpret_cast(GetProcAddress(_Sync_module, "WakeByAddressAll")); + + if (_Wait_on_address != nullptr && _Wake_by_address_single != nullptr && _Wake_by_address_all != nullptr) { + _Wait_functions._Pfn_WaitOnAddress.store(_Wait_on_address, _STD memory_order_relaxed); + _Wait_functions._Pfn_WakeByAddressSingle.store(_Wake_by_address_single, _STD memory_order_relaxed); + _Wait_functions._Pfn_WakeByAddressAll.store(_Wake_by_address_all, _STD memory_order_relaxed); + _Level = __std_atomic_api_level::__has_wait_on_address; + } + } + + // for __has_srwlock, relaxed would have been enough, not distinguishing for consistency + _Wait_functions._Api_level.store(_Level, _STD memory_order_release); + return _Level; + } + + [[nodiscard]] __std_atomic_api_level _Acquire_wait_functions() noexcept { + auto _Level = _Wait_functions._Api_level.load(_STD memory_order_acquire); + if (_Level <= __std_atomic_api_level::__detecting) { + _Level = _Init_wait_functions(_Level); + } + + return _Level; + } + + [[nodiscard]] BOOL __crtWaitOnAddress( + volatile VOID* Address, PVOID CompareAddress, SIZE_T AddressSize, DWORD dwMilliseconds) { + const auto _Wait_on_address = _Wait_functions._Pfn_WaitOnAddress.load(_STD memory_order_relaxed); + return _Wait_on_address(Address, CompareAddress, AddressSize, dwMilliseconds); + } + + VOID __crtWakeByAddressSingle(PVOID Address) { + const auto _Wake_by_address_single = _Wait_functions._Pfn_WakeByAddressSingle.load(_STD memory_order_relaxed); + _Wake_by_address_single(Address); + } + + VOID __crtWakeByAddressAll(PVOID Address) { + const auto _Wake_by_address_all = _Wait_functions._Pfn_WakeByAddressAll.load(_STD memory_order_relaxed); + _Wake_by_address_all(Address); + } + + bool __stdcall _Atomic_wait_are_equal_direct_fallback( + const void* _Storage, void* _Comparand, size_t _Size, void*) noexcept { + switch (_Size) { + case 1: + return __iso_volatile_load8(static_cast(_Storage)) == *static_cast(_Comparand); + case 2: + return __iso_volatile_load16(static_cast(_Storage)) == *static_cast(_Comparand); + case 4: + return __iso_volatile_load32(static_cast(_Storage)) == *static_cast(_Comparand); + case 8: + return __iso_volatile_load64(static_cast(_Storage)) + == *static_cast(_Comparand); + default: + _CSTD abort(); + } + } +#endif // _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE +} // unnamed namespace + + +_EXTERN_C +int __stdcall __std_atomic_wait_direct(const void* const _Storage, void* const _Comparand, const size_t _Size, + const unsigned long _Remaining_timeout) noexcept { +#if _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE == 0 + if (_Acquire_wait_functions() < __std_atomic_api_level::__has_wait_on_address) { + return __std_atomic_wait_indirect( + _Storage, _Comparand, _Size, nullptr, &_Atomic_wait_are_equal_direct_fallback, _Remaining_timeout); + } +#endif // _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE == 0 + + const auto _Result = __crtWaitOnAddress( + const_cast(_Storage), const_cast(_Comparand), _Size, _Remaining_timeout); + + if (!_Result) { + _Assume_timeout(); + } + return _Result; +} + +void __stdcall __std_atomic_notify_one_direct(const void* const _Storage) noexcept { +#if _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE == 0 + if (_Acquire_wait_functions() < __std_atomic_api_level::__has_wait_on_address) { + __std_atomic_notify_one_indirect(_Storage); + return; + } +#endif // _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE = 0 + + __crtWakeByAddressSingle(const_cast(_Storage)); +} + +void __stdcall __std_atomic_notify_all_direct(const void* const _Storage) noexcept { +#if _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE == 0 + if (_Acquire_wait_functions() < __std_atomic_api_level::__has_wait_on_address) { + __std_atomic_notify_all_indirect(_Storage); + return; + } +#endif // _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE == 0 + + __crtWakeByAddressAll(const_cast(_Storage)); +} + +void __stdcall __std_atomic_notify_one_indirect(const void* const _Storage) noexcept { + auto& _Entry = _Atomic_wait_table_entry(_Storage); + _SrwLock_guard _Guard(_Entry._Lock); + _Wait_context* _Context = _Entry._Wait_list_head._Next; + for (; _Context != &_Entry._Wait_list_head; _Context = _Context->_Next) { + if (_Context->_Storage == _Storage) { + // Can't move wake outside SRWLOCKed section: SRWLOCK also protects the _Context itself + WakeAllConditionVariable(&_Context->_Condition); + break; + } + } +} + +void __stdcall __std_atomic_notify_all_indirect(const void* const _Storage) noexcept { + auto& _Entry = _Atomic_wait_table_entry(_Storage); + _SrwLock_guard _Guard(_Entry._Lock); + _Wait_context* _Context = _Entry._Wait_list_head._Next; + for (; _Context != &_Entry._Wait_list_head; _Context = _Context->_Next) { + if (_Context->_Storage == _Storage) { + // Can't move wake outside SRWLOCKed section: SRWLOCK also protects the _Context itself + WakeAllConditionVariable(&_Context->_Condition); + } + } +} + +int __stdcall __std_atomic_wait_indirect(const void* _Storage, void* _Comparand, size_t _Size, void* _Param, + _Atomic_wait_indirect_equal_callback_t _Are_equal, unsigned long _Remaining_timeout) noexcept { + auto& _Entry = _Atomic_wait_table_entry(_Storage); + + _SrwLock_guard _Guard(_Entry._Lock); + _Guarded_wait_context _Context{_Storage, &_Entry._Wait_list_head}; + for (;;) { + if (!_Are_equal(_Storage, _Comparand, _Size, _Param)) { // note: under lock to prevent lost wakes + return TRUE; + } + + if (!SleepConditionVariableSRW(&_Context._Condition, &_Entry._Lock, _Remaining_timeout, 0)) { + _Assume_timeout(); + return FALSE; + } + + if (_Remaining_timeout != _Atomic_wait_no_timeout) { + // spurious wake to recheck the clock + return TRUE; + } + } +} + +unsigned long long __stdcall __std_atomic_wait_get_deadline(const unsigned long long _Timeout) noexcept { + if (_Timeout == _Atomic_wait_no_deadline) { + return _Atomic_wait_no_deadline; + } else { + return GetTickCount64() + _Timeout; + } +} + +unsigned long __stdcall __std_atomic_wait_get_remaining_timeout(unsigned long long _Deadline) noexcept { + static_assert(_Atomic_wait_no_timeout == INFINITE, + "_Atomic_wait_no_timeout is passed directly to underlying API, so should match it"); + + if (_Deadline == _Atomic_wait_no_deadline) { + return INFINITE; + } + + const unsigned long long _Current_time = GetTickCount64(); + if (_Current_time >= _Deadline) { + return 0; + } + + unsigned long long _Remaining = _Deadline - _Current_time; + constexpr unsigned long _Ten_days = 864'000'000; + if (_Remaining > _Ten_days) { + return _Ten_days; + } + return static_cast(_Remaining); +} + +__std_atomic_api_level __stdcall __std_atomic_set_api_level(__std_atomic_api_level _Requested_api_level) noexcept { +#if _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE + (void) _Requested_api_level; + return __std_atomic_api_level::__has_wait_on_address; +#else // ^^^ _ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE // !_ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE vvv + switch (_Requested_api_level) { + case __std_atomic_api_level::__not_set: + case __std_atomic_api_level::__detecting: + _CSTD abort(); + case __std_atomic_api_level::__has_srwlock: + _Force_wait_functions_srwlock_only(); + break; + case __std_atomic_api_level::__has_wait_on_address: + default: // future compat: new header using an old DLL will get the highest requested level supported + break; + } + + return _Acquire_wait_functions(); +#endif // !_ATOMIC_WAIT_ON_ADDRESS_STATICALLY_AVAILABLE +} +_END_EXTERN_C diff --git a/stl/src/excptptr.cpp b/stl/src/excptptr.cpp index 821697998c6..517c63c65b0 100644 --- a/stl/src/excptptr.cpp +++ b/stl/src/excptptr.cpp @@ -37,13 +37,37 @@ extern "C" _CRTIMP2 void* __cdecl __AdjustPointer(void*, const PMD&); // defined using namespace std; namespace { -#ifdef _M_CEE_PURE +#if defined(_M_CEE_PURE) template _Ty& _Immortalize() { // return a reference to an object that will live forever /* MAGIC */ static _Immortalizer_impl<_Ty> _Static; return reinterpret_cast<_Ty&>(_Static._Storage); } -#else // ^^^ _M_CEE_PURE ^^^ // vvv !_M_CEE_PURE vvv +#elif !defined(_M_CEE) // _M_CEE test is TRANSITION, VSO-1153256 + template + struct _Constexpr_excptptr_immortalize_impl { + union { + _Ty _Storage; + }; + + constexpr _Constexpr_excptptr_immortalize_impl() noexcept : _Storage{} {} + + _Constexpr_excptptr_immortalize_impl(const _Constexpr_excptptr_immortalize_impl&) = delete; + _Constexpr_excptptr_immortalize_impl& operator=(const _Constexpr_excptptr_immortalize_impl&) = delete; + + [[msvc::noop_dtor]] ~_Constexpr_excptptr_immortalize_impl() { + // do nothing, allowing _Ty to be used during shutdown + } + }; + + template + _Constexpr_excptptr_immortalize_impl<_Ty> _Immortalize_impl; + + template + _NODISCARD _Ty& _Immortalize() noexcept { + return _Immortalize_impl<_Ty>._Storage; + } +#else // choose immortalize strategy template int __stdcall _Immortalize_impl(void*, void* _Storage_ptr, void**) noexcept { // adapt True Placement New to _Execute_once diff --git a/stl/src/filesys.cpp b/stl/src/filesys.cpp index 7a734059a48..3218e6e5e6d 100644 --- a/stl/src/filesys.cpp +++ b/stl/src/filesys.cpp @@ -264,9 +264,9 @@ _FS_DLL unsigned long long __CLRCALL_PURE_OR_CDECL _File_size(const wchar_t* _Fn // 1600 is excluded, 1700/1800 are not leap years // 1 partial century with 17 leap years: // 1900 is not a leap year -// 1904 is leap year #1 -// 1908 is leap year #2 -// 1968 is leap year #17 +// 1904 is leap year number 1 +// 1908 is leap year number 2 +// 1968 is leap year number 17 constexpr uint64_t _Win_ticks_per_second = 10000000ULL; diff --git a/stl/src/filesystem.cpp b/stl/src/filesystem.cpp index 0a1d3d5a1e8..063f5f9c7a6 100644 --- a/stl/src/filesystem.cpp +++ b/stl/src/filesystem.cpp @@ -18,7 +18,6 @@ #include #include - #include // We have several switches that do not have case statements for every possible enum value. diff --git a/stl/src/msvcp_atomic_wait.src b/stl/src/msvcp_atomic_wait.src new file mode 100644 index 00000000000..ec335cc161c --- /dev/null +++ b/stl/src/msvcp_atomic_wait.src @@ -0,0 +1,25 @@ +; Copyright (c) Microsoft Corporation. +; SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +; atomic wait satellite DLL definition + +LIBRARY LIBRARYNAME + +EXPORTS + __std_atomic_wait_get_deadline + __std_atomic_wait_get_remaining_timeout + __std_atomic_notify_all_direct + __std_atomic_notify_all_indirect + __std_atomic_notify_one_direct + __std_atomic_notify_one_indirect + __std_atomic_set_api_level + __std_atomic_wait_direct + __std_atomic_wait_indirect + __std_bulk_submit_threadpool_work + __std_close_threadpool_work + __std_create_threadpool_work + __std_execution_wait_on_uchar + __std_execution_wake_by_address_all + __std_parallel_algorithms_hw_threads + __std_submit_threadpool_work + __std_wait_for_threadpool_work_callbacks diff --git a/stl/src/parallel_algorithms.cpp b/stl/src/parallel_algorithms.cpp index c59577b7d53..47c2bc4f552 100644 --- a/stl/src/parallel_algorithms.cpp +++ b/stl/src/parallel_algorithms.cpp @@ -3,297 +3,66 @@ // support for -#include #include -#include +#include +#include -// This must be as small as possible, because its contents are -// injected into the msvcprt.lib and msvcprtd.lib import libraries. -// Do not include or define anything else here. -// In particular, basic_string must not be included here. - -#if _STL_WIN32_WINNT >= _WIN32_WINNT_WIN8 -#pragma comment(lib, "synchronization") // for WaitOnAddress family -#endif // _STL_WIN32_WINNT >= _WIN32_WINNT_WIN8 - -#if _STL_WIN32_WINNT < _WIN32_WINNT_WIN8 namespace { - struct _Parallel_init_info { - unsigned int _Hw_threads; -#if _STL_WIN32_WINNT < _WIN32_WINNT_VISTA - decltype(CreateThreadpoolWork)* _Pfn_CreateThreadpoolWork; - decltype(SubmitThreadpoolWork)* _Pfn_SubmitThreadpoolWork; - decltype(CloseThreadpoolWork)* _Pfn_CloseThreadpoolWork; - decltype(WaitForThreadpoolWorkCallbacks)* _Pfn_WaitForThreadpoolWorkCallbacks; - decltype(AcquireSRWLockExclusive)* _Pfn_AcquireSRWLockExclusive; // nullptr if _Pfn_WaitOnAddress is non-nullptr - decltype(ReleaseSRWLockExclusive)* _Pfn_ReleaseSRWLockExclusive; // ditto - decltype(SleepConditionVariableSRW)* _Pfn_SleepConditionVariableSRW; // ditto - decltype(WakeAllConditionVariable)* _Pfn_WakeAllConditionVariable; // ditto -#endif // _STL_WIN32_WINNT < _WIN32_WINNT_VISTA - decltype(WaitOnAddress)* _Pfn_WaitOnAddress; - decltype(WakeByAddressAll)* _Pfn_WakeByAddressAll; - }; - - _Parallel_init_info _Parallel_info; - - struct _Wait_semaphore { - SRWLOCK _Mtx; - CONDITION_VARIABLE _Cv; - }; - - constexpr int _Wait_table_size = 256; // one 4k page - constexpr int _Wait_table_max_index = _Wait_table_size - 1; - _Wait_semaphore _Wait_table[_Wait_table_size]{}; - size_t _Choose_wait_entry(const volatile void* _Target) noexcept { - auto _Num = reinterpret_cast(_Target); -#ifdef _WIN64 - _Num = (_Num & ((1ull << 32) - 1ull)) ^ (_Num >> 32); // down to 32 bits -#endif // _WIN64 - _Num = (_Num & ((1u << 16) - 1u)) ^ (_Num >> 16); // to 16 bits - _Num = (_Num & ((1u << 8) - 1u)) ^ (_Num >> 8); // to 8 bits - static_assert(_Wait_table_max_index == (1 << 8) - 1, "Bad wait table size assumption"); - return _Num; - } - unsigned char _Atomic_load_uchar(const volatile unsigned char* _Ptr) noexcept { // atomic load of unsigned char, copied from except ARM and ARM64 bits unsigned char _Value; -#if defined(_M_IX86) || defined(_M_X64) - _Value = *_Ptr; - _ReadWriteBarrier(); -#else // architecture, no ARM support as this is guarded by _STL_WIN32_WINNT < _WIN32_WINNT_WIN8 -#error Unsupported architecture -#endif // architecture - return _Value; - } - - unsigned int _Atomic_load_uint(const volatile unsigned int* _Ptr) noexcept { - // atomic load of unsigned int, copied from except ARM and ARM64 bits - unsigned int _Value; -#if defined(_M_IX86) || defined(_M_X64) - _Value = *_Ptr; +#if defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM) || defined(_M_ARM64) + _Value = __iso_volatile_load8(reinterpret_cast(_Ptr)); _ReadWriteBarrier(); -#else // architecture, ditto no ARM support +#else #error Unsupported architecture -#endif // architecture +#endif return _Value; } - - void _Atomic_store_uint(volatile unsigned int* _Tgt, unsigned int _Value) { - // atomic store of unsigned int, copied from -#if defined(_M_IX86) || defined(_M_X64) - _InterlockedExchange(reinterpret_cast(_Tgt), static_cast(_Value)); -#else // architecture, ditto no ARM support -#error Unsupported architecture -#endif // architecture - } - - bool _Initialize_parallel_init_info() { // try to fill in _Parallel_info -#if !(defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM) || defined(_M_ARM64)) -#error Check hardware assumption: Assumes that write races of identical values to pointer-sized variables are benign -#endif // !(defined(_M_IX86) || defined(_M_X64) || defined(_M_ARM) || defined(_M_ARM64)) - - HMODULE _Kernel32 = GetModuleHandleW(L"kernel32.dll"); -#if _STL_WIN32_WINNT < _WIN32_WINNT_VISTA - _Parallel_info._Pfn_CreateThreadpoolWork = - reinterpret_cast(GetProcAddress(_Kernel32, "CreateThreadpoolWork")); - _Parallel_info._Pfn_SubmitThreadpoolWork = - reinterpret_cast(GetProcAddress(_Kernel32, "SubmitThreadpoolWork")); - _Parallel_info._Pfn_CloseThreadpoolWork = - reinterpret_cast(GetProcAddress(_Kernel32, "CloseThreadpoolWork")); - _Parallel_info._Pfn_WaitForThreadpoolWorkCallbacks = - reinterpret_cast( - GetProcAddress(_Kernel32, "WaitForThreadpoolWorkCallbacks")); - if (!_Parallel_info._Pfn_CreateThreadpoolWork || !_Parallel_info._Pfn_SubmitThreadpoolWork - || !_Parallel_info._Pfn_CloseThreadpoolWork || !_Parallel_info._Pfn_WaitForThreadpoolWorkCallbacks) { - // don't parallelize without the Windows Vista threadpool - return false; - } -#endif // _STL_WIN32_WINNT < _WIN32_WINNT_VISTA - - HMODULE _KernelBase = GetModuleHandleW(L"kernelbase.dll"); - if (_KernelBase) { - _Parallel_info._Pfn_WaitOnAddress = - reinterpret_cast(GetProcAddress(_KernelBase, "WaitOnAddress")); - _Parallel_info._Pfn_WakeByAddressAll = - reinterpret_cast(GetProcAddress(_KernelBase, "WakeByAddressAll")); - if ((_Parallel_info._Pfn_WaitOnAddress == nullptr) != (_Parallel_info._Pfn_WakeByAddressAll == nullptr)) { - // if we don't have both we can use neither - _Parallel_info._Pfn_WaitOnAddress = nullptr; - _Parallel_info._Pfn_WakeByAddressAll = nullptr; - } - } - -#if _STL_WIN32_WINNT < _WIN32_WINNT_VISTA - if (_Parallel_info._Pfn_WaitOnAddress) { // no need for SRWLOCK or CONDITION_VARIABLE if we have WaitOnAddress - return true; - } - - _Parallel_info._Pfn_AcquireSRWLockExclusive = - reinterpret_cast(GetProcAddress(_Kernel32, "AcquireSRWLockExclusive")); - _Parallel_info._Pfn_ReleaseSRWLockExclusive = - reinterpret_cast(GetProcAddress(_Kernel32, "ReleaseSRWLockExclusive")); - _Parallel_info._Pfn_SleepConditionVariableSRW = reinterpret_cast( - GetProcAddress(_Kernel32, "SleepConditionVariableSRW")); - _Parallel_info._Pfn_WakeAllConditionVariable = reinterpret_cast( - GetProcAddress(_Kernel32, "WakeAllConditionVariable")); - - if (!_Parallel_info._Pfn_AcquireSRWLockExclusive || !_Parallel_info._Pfn_ReleaseSRWLockExclusive - || !_Parallel_info._Pfn_SleepConditionVariableSRW || !_Parallel_info._Pfn_WakeAllConditionVariable) { - // no fallback for WaitOnAddress; shouldn't be possible as these - // APIs were added at the same time as the Windows Vista threadpool API - return false; - } -#endif // _STL_WIN32_WINNT < _WIN32_WINNT_VISTA - - return true; - } } // unnamed namespace -#endif // _STL_WIN32_WINNT < _WIN32_WINNT_WIN8 - -static DWORD _Get_number_of_processors() noexcept { - SYSTEM_INFO _Info; - GetNativeSystemInfo(&_Info); - return _Info.dwNumberOfProcessors; -} extern "C" { _NODISCARD unsigned int __stdcall __std_parallel_algorithms_hw_threads() noexcept { -#if _STL_WIN32_WINNT >= _WIN32_WINNT_WIN8 - return _Get_number_of_processors(); -#else // ^^^ _STL_WIN32_WINNT >= _WIN32_WINNT_WIN8 ^^^ / vvv _STL_WIN32_WINNT < _WIN32_WINNT_WIN8 vvv - // _Atomic_load_uint enforces memory ordering in _Initialize_parallel_init_info: - unsigned int _Result = _Atomic_load_uint(&_Parallel_info._Hw_threads); - if (_Result == 0) { - if (_Initialize_parallel_init_info()) { - _Result = _Get_number_of_processors(); - } else { - _Result = 1; - } - - // _Atomic_store_uint enforces memory ordering in _Initialize_parallel_init_info: - _Atomic_store_uint(&_Parallel_info._Hw_threads, _Result); + static int _Cached_hw_concurrency = -1; + int _Hw_concurrency = __iso_volatile_load32(&_Cached_hw_concurrency); + if (_Hw_concurrency == -1) { + _Hw_concurrency = static_cast(_STD thread::hardware_concurrency()); + __iso_volatile_store32(&_Cached_hw_concurrency, _Hw_concurrency); } - return _Result; -#endif // ^^^ _STL_WIN32_WINNT < _WIN32_WINNT_WIN8 ^^^ + return static_cast(_Hw_concurrency); } -// Relaxed reads of _Parallel_info below because __std_parallel_algorithms_hw_threads must be called -// before any of these on each thread. - _NODISCARD PTP_WORK __stdcall __std_create_threadpool_work( PTP_WORK_CALLBACK _Callback, void* _Context, PTP_CALLBACK_ENVIRON _Callback_environ) noexcept { -#if _STL_WIN32_WINNT >= _WIN32_WINNT_VISTA return CreateThreadpoolWork(_Callback, _Context, _Callback_environ); -#else // ^^^ _STL_WIN32_WINNT >= _WIN32_WINNT_VISTA ^^^ / vvv _STL_WIN32_WINNT < _WIN32_WINNT_VISTA vvv - return _Parallel_info._Pfn_CreateThreadpoolWork(_Callback, _Context, _Callback_environ); -#endif // ^^^ _STL_WIN32_WINNT < _WIN32_WINNT_VISTA ^^^ } void __stdcall __std_submit_threadpool_work(PTP_WORK _Work) noexcept { -#if _STL_WIN32_WINNT >= _WIN32_WINNT_VISTA SubmitThreadpoolWork(_Work); -#else // ^^^ _STL_WIN32_WINNT >= _WIN32_WINNT_VISTA ^^^ / vvv _STL_WIN32_WINNT < _WIN32_WINNT_VISTA vvv - _Parallel_info._Pfn_SubmitThreadpoolWork(_Work); -#endif // ^^^ _STL_WIN32_WINNT < _WIN32_WINNT_VISTA ^^^ } void __stdcall __std_bulk_submit_threadpool_work(PTP_WORK _Work, const size_t _Submissions) noexcept { -#if _STL_WIN32_WINNT >= _WIN32_WINNT_VISTA for (size_t _Idx = 0; _Idx < _Submissions; ++_Idx) { SubmitThreadpoolWork(_Work); } -#else // ^^^ _STL_WIN32_WINNT >= _WIN32_WINNT_VISTA ^^^ / vvv _STL_WIN32_WINNT < _WIN32_WINNT_VISTA vvv - const auto _Fn = _Parallel_info._Pfn_SubmitThreadpoolWork; - for (size_t _Idx = 0; _Idx < _Submissions; ++_Idx) { - _Fn(_Work); - } -#endif // ^^^ _STL_WIN32_WINNT < _WIN32_WINNT_VISTA ^^^ } void __stdcall __std_close_threadpool_work(PTP_WORK _Work) noexcept { -#if _STL_WIN32_WINNT >= _WIN32_WINNT_VISTA CloseThreadpoolWork(_Work); -#else // ^^^ _STL_WIN32_WINNT >= _WIN32_WINNT_VISTA ^^^ / vvv _STL_WIN32_WINNT < _WIN32_WINNT_VISTA vvv - _Parallel_info._Pfn_CloseThreadpoolWork(_Work); -#endif // ^^^ _STL_WIN32_WINNT < _WIN32_WINNT_VISTA ^^^ } void __stdcall __std_wait_for_threadpool_work_callbacks(PTP_WORK _Work, BOOL _Cancel) noexcept { -#if _STL_WIN32_WINNT >= _WIN32_WINNT_VISTA WaitForThreadpoolWorkCallbacks(_Work, _Cancel); -#else // ^^^ _STL_WIN32_WINNT >= _WIN32_WINNT_VISTA ^^^ / vvv _STL_WIN32_WINNT < _WIN32_WINNT_VISTA vvv - _Parallel_info._Pfn_WaitForThreadpoolWorkCallbacks(_Work, _Cancel); -#endif // ^^^ _STL_WIN32_WINNT < _WIN32_WINNT_VISTA ^^^ } void __stdcall __std_execution_wait_on_uchar(const volatile unsigned char* _Address, unsigned char _Compare) noexcept { -#if _STL_WIN32_WINNT >= _WIN32_WINNT_WIN8 - if (WaitOnAddress(const_cast(_Address), &_Compare, 1, INFINITE) == FALSE) { - // this API failing should only be possible with a timeout, and we asked for INFINITE - ::terminate(); - } -#else // ^^^ _STL_WIN32_WINNT >= _WIN32_WINNT_WIN8 ^^^ / vvv _STL_WIN32_WINNT < _WIN32_WINNT_WIN8 vvv - if (_Parallel_info._Pfn_WaitOnAddress) { - if (_Parallel_info._Pfn_WaitOnAddress(const_cast(_Address), &_Compare, 1, INFINITE) - == FALSE) { - ::terminate(); - } - - return; - } - - // fake WaitOnAddress via SRWLOCK and CONDITION_VARIABLE - for (int _Idx = 0; _Idx < 4096; ++_Idx) { // optimistic non-backoff spin - if (_Atomic_load_uchar(_Address) == _Compare) { - return; - } - } - - auto& _Wait_entry = _Wait_table[_Choose_wait_entry(_Address)]; -#if _STL_WIN32_WINNT < _WIN32_WINNT_VISTA - _Parallel_info._Pfn_AcquireSRWLockExclusive(&_Wait_entry._Mtx); - while (_Atomic_load_uchar(_Address) == _Compare) { - if (_Parallel_info._Pfn_SleepConditionVariableSRW(&_Wait_entry._Cv, &_Wait_entry._Mtx, INFINITE, 0) == 0) { - ::terminate(); - } - } - - _Parallel_info._Pfn_ReleaseSRWLockExclusive(&_Wait_entry._Mtx); -#else // ^^^ _STL_WIN32_WINNT < _WIN32_WINNT_VISTA ^^^ / vvv _STL_WIN32_WINNT >= _WIN32_WINNT_VISTA vvv - AcquireSRWLockExclusive(&_Wait_entry._Mtx); - while (_Atomic_load_uchar(_Address) == _Compare) { - if (SleepConditionVariableSRW(&_Wait_entry._Cv, &_Wait_entry._Mtx, INFINITE, 0) == 0) { - ::terminate(); - } - } - - ReleaseSRWLockExclusive(&_Wait_entry._Mtx); -#endif // ^^^ _STL_WIN32_WINNT >= _WIN32_WINNT_VISTA ^^^ -#endif // ^^^ _STL_WIN32_WINNT < _WIN32_WINNT_WIN8 ^^^ + __std_atomic_wait_direct(const_cast(_Address), &_Compare, 1, _Atomic_wait_no_timeout); } void __stdcall __std_execution_wake_by_address_all(const volatile void* _Address) noexcept { -#if _STL_WIN32_WINNT >= _WIN32_WINNT_WIN8 - WakeByAddressAll(const_cast(_Address)); -#else // ^^^ _STL_WIN32_WINNT >= _WIN32_WINNT_WIN8 ^^^ / vvv _STL_WIN32_WINNT < _WIN32_WINNT_WIN8 vvv - if (_Parallel_info._Pfn_WakeByAddressAll) { - _Parallel_info._Pfn_WakeByAddressAll(const_cast(_Address)); - } else { - auto& _Wait_entry = _Wait_table[_Choose_wait_entry(_Address)]; -#if _STL_WIN32_WINNT < _WIN32_WINNT_VISTA - _Parallel_info._Pfn_AcquireSRWLockExclusive(&_Wait_entry._Mtx); - _Parallel_info._Pfn_ReleaseSRWLockExclusive(&_Wait_entry._Mtx); - _Parallel_info._Pfn_WakeAllConditionVariable(&_Wait_entry._Cv); -#else // ^^^ _STL_WIN32_WINNT < _WIN32_WINNT_VISTA ^^^ / vvv _STL_WIN32_WINNT >= _WIN32_WINNT_VISTA vvv - AcquireSRWLockExclusive(&_Wait_entry._Mtx); - ReleaseSRWLockExclusive(&_Wait_entry._Mtx); - WakeAllConditionVariable(&_Wait_entry._Cv); -#endif // ^^^ _STL_WIN32_WINNT >= _WIN32_WINNT_VISTA ^^^ - } -#endif // ^^^ _STL_WIN32_WINNT < _WIN32_WINNT_WIN8 ^^^ + __std_atomic_notify_all_direct(const_cast(_Address)); } } // extern "C" diff --git a/stl/src/vector_algorithms.cpp b/stl/src/vector_algorithms.cpp index 2467b2b0626..4823e4fdd9d 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -24,20 +24,31 @@ static void _Reverse_tail(_BidIt _First, _BidIt _Last) noexcept { } } -static size_t _Byte_length(void* _First, void* _Last) noexcept { - return static_cast(_Last) - static_cast(_First); +template +static void _Reverse_copy_tail(_BidIt _First, _BidIt _Last, _OutIt _Dest) noexcept { + while (_First != _Last) { + *_Dest++ = *--_Last; + } +} + +static size_t _Byte_length(const void* _First, const void* _Last) noexcept { + return static_cast(_Last) - static_cast(_First); } static void _Advance_bytes(void*& _Target, ptrdiff_t _Offset) noexcept { _Target = static_cast(_Target) + _Offset; } +static void _Advance_bytes(const void*& _Target, ptrdiff_t _Offset) noexcept { + _Target = static_cast(_Target) + _Offset; +} + extern "C" { __declspec(noalias) void __cdecl __std_swap_ranges_trivially_swappable_noalias( void* _First1, void* _Last1, void* _First2) noexcept { constexpr size_t _Mask_32 = ~((static_cast(1) << 5) - 1); if (_Byte_length(_First1, _Last1) >= 32 && _bittest(&__isa_enabled, __ISA_AVAILABLE_AVX2)) { - void* _Stop_at = _First1; + const void* _Stop_at = _First1; _Advance_bytes(_Stop_at, _Byte_length(_First1, _Last1) & _Mask_32); do { const __m256i _Left = _mm256_loadu_si256(static_cast<__m256i*>(_First1)); @@ -55,7 +66,7 @@ __declspec(noalias) void __cdecl __std_swap_ranges_trivially_swappable_noalias( && _bittest(&__isa_enabled, __ISA_AVAILABLE_SSE2) #endif // _M_IX86 ) { - void* _Stop_at = _First1; + const void* _Stop_at = _First1; _Advance_bytes(_Stop_at, _Byte_length(_First1, _Last1) & _Mask_16); do { const __m128i _Left = _mm_loadu_si128(static_cast<__m128i*>(_First1)); @@ -70,7 +81,7 @@ __declspec(noalias) void __cdecl __std_swap_ranges_trivially_swappable_noalias( #if defined(_M_X64) // NOTE: UNALIGNED MEMORY ACCESSES constexpr size_t _Mask_8 = ~((static_cast(1) << 3) - 1); if (_Byte_length(_First1, _Last1) >= 8) { - void* _Stop_at = _First1; + const void* _Stop_at = _First1; _Advance_bytes(_Stop_at, _Byte_length(_First1, _Last1) & _Mask_8); do { const unsigned long long _Left = *static_cast(_First1); @@ -84,7 +95,7 @@ __declspec(noalias) void __cdecl __std_swap_ranges_trivially_swappable_noalias( #elif defined(_M_IX86) // NOTE: UNALIGNED MEMORY ACCESSES constexpr size_t _Mask_4 = ~((static_cast(1) << 2) - 1); if (_Byte_length(_First1, _Last1) >= 4) { - void* _Stop_at = _First1; + const void* _Stop_at = _First1; _Advance_bytes(_Stop_at, _Byte_length(_First1, _Last1) & _Mask_4); do { const unsigned long _Left = *static_cast(_First1); @@ -120,7 +131,7 @@ __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_1(void* _Firs 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, // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); - void* _Stop_at = _First; + const void* _Stop_at = _First; _Advance_bytes(_Stop_at, _Byte_length(_First, _Last) >> 6 << 5); do { _Advance_bytes(_Last, -32); @@ -138,7 +149,7 @@ __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_1(void* _Firs 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); - void* _Stop_at = _First; + const void* _Stop_at = _First; _Advance_bytes(_Stop_at, _Byte_length(_First, _Last) >> 5 << 4); do { _Advance_bytes(_Last, -16); @@ -160,7 +171,7 @@ __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_2(void* _Firs 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, // 1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); - void* _Stop_at = _First; + const void* _Stop_at = _First; _Advance_bytes(_Stop_at, _Byte_length(_First, _Last) >> 6 << 5); do { _Advance_bytes(_Last, -32); @@ -176,7 +187,7 @@ __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_2(void* _Firs 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); - void* _Stop_at = _First; + const void* _Stop_at = _First; _Advance_bytes(_Stop_at, _Byte_length(_First, _Last) >> 5 << 4); do { _Advance_bytes(_Last, -16); @@ -195,7 +206,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 (_Byte_length(_First, _Last) >= 64 && _bittest(&__isa_enabled, __ISA_AVAILABLE_AVX2)) { - void* _Stop_at = _First; + const void* _Stop_at = _First; _Advance_bytes(_Stop_at, _Byte_length(_First, _Last) >> 6 << 5); do { _Advance_bytes(_Last, -32); @@ -214,7 +225,7 @@ __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_4(void* _Firs && _bittest(&__isa_enabled, __ISA_AVAILABLE_SSE2) #endif // _M_IX86 ) { - void* _Stop_at = _First; + const void* _Stop_at = _First; _Advance_bytes(_Stop_at, _Byte_length(_First, _Last) >> 5 << 4); do { _Advance_bytes(_Last, -16); @@ -233,7 +244,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 (_Byte_length(_First, _Last) >= 64 && _bittest(&__isa_enabled, __ISA_AVAILABLE_AVX2)) { - void* _Stop_at = _First; + const void* _Stop_at = _First; _Advance_bytes(_Stop_at, _Byte_length(_First, _Last) >> 6 << 5); do { _Advance_bytes(_Last, -32); @@ -252,7 +263,7 @@ __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_8(void* _Firs && _bittest(&__isa_enabled, __ISA_AVAILABLE_SSE2) #endif // _M_IX86 ) { - void* _Stop_at = _First; + const void* _Stop_at = _First; _Advance_bytes(_Stop_at, _Byte_length(_First, _Last) >> 5 << 4); do { _Advance_bytes(_Last, -16); @@ -269,6 +280,143 @@ __declspec(noalias) void __cdecl __std_reverse_trivially_swappable_8(void* _Firs _Reverse_tail(static_cast(_First), static_cast(_Last)); } +__declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_1( + const void* _First, const void* _Last, void* _Dest) noexcept { + 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, // + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + const void* _Stop_at = _Dest; + _Advance_bytes(_Stop_at, _Byte_length(_First, _Last) >> 5 << 5); + do { + _Advance_bytes(_Last, -32); + const __m256i _Block = _mm256_permute4x64_epi64(_mm256_loadu_si256(static_cast(_Last)), 78); + const __m256i _Block_reversed = _mm256_shuffle_epi8(_Block, _Reverse_char_lanes_avx); + _mm256_storeu_si256(static_cast<__m256i*>(_Dest), _Block_reversed); + _Advance_bytes(_Dest, 32); + } while (_Dest != _Stop_at); + } + + 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); + const void* _Stop_at = _Dest; + _Advance_bytes(_Stop_at, _Byte_length(_First, _Last) >> 4 << 4); + do { + _Advance_bytes(_Last, -16); + const __m128i _Block = _mm_loadu_si128(static_cast(_Last)); + const __m128i _Block_reversed = _mm_shuffle_epi8(_Block, _Reverse_char_sse); // SSSE3 + _mm_storeu_si128(static_cast<__m128i*>(_Dest), _Block_reversed); + _Advance_bytes(_Dest, 16); + } while (_Dest != _Stop_at); + } + + _Reverse_copy_tail(static_cast(_First), static_cast(_Last), + static_cast(_Dest)); +} + +__declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_2( + const void* _First, const void* _Last, void* _Dest) noexcept { + 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, // + 1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14); + const void* _Stop_at = _Dest; + _Advance_bytes(_Stop_at, _Byte_length(_First, _Last) >> 5 << 5); + do { + _Advance_bytes(_Last, -32); + const __m256i _Block = _mm256_permute4x64_epi64(_mm256_loadu_si256(static_cast(_Last)), 78); + const __m256i _Block_reversed = _mm256_shuffle_epi8(_Block, _Reverse_short_lanes_avx); + _mm256_storeu_si256(static_cast<__m256i*>(_Dest), _Block_reversed); + _Advance_bytes(_Dest, 32); + } while (_Dest != _Stop_at); + } + + 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); + const void* _Stop_at = _Dest; + _Advance_bytes(_Stop_at, _Byte_length(_First, _Last) >> 4 << 4); + do { + _Advance_bytes(_Last, -16); + const __m128i _Block = _mm_loadu_si128(static_cast(_Last)); + const __m128i _Block_reversed = _mm_shuffle_epi8(_Block, _Reverse_short_sse); // SSSE3 + _mm_storeu_si128(static_cast<__m128i*>(_Dest), _Block_reversed); + _Advance_bytes(_Dest, 16); + } while (_Dest != _Stop_at); + } + + _Reverse_copy_tail(static_cast(_First), static_cast(_Last), + static_cast(_Dest)); +} + +__declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_4( + const void* _First, const void* _Last, void* _Dest) noexcept { + 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); + do { + _Advance_bytes(_Last, -32); + const __m256i _Block = _mm256_permute4x64_epi64(_mm256_loadu_si256(static_cast(_Last)), 78); + const __m256i _Block_reversed = _mm256_shuffle_epi32(_Block, 27); + _mm256_storeu_si256(static_cast<__m256i*>(_Dest), _Block_reversed); + _Advance_bytes(_Dest, 32); + } while (_Dest != _Stop_at); + } + + if (_Byte_length(_First, _Last) >= 16 +#ifdef _M_IX86 + && _bittest(&__isa_enabled, __ISA_AVAILABLE_SSE2) +#endif // _M_IX86 + ) { + const void* _Stop_at = _Dest; + _Advance_bytes(_Stop_at, _Byte_length(_First, _Last) >> 4 << 4); + do { + _Advance_bytes(_Last, -16); + const __m128i _Block = _mm_loadu_si128(static_cast(_Last)); + const __m128i _Block_reversed = _mm_shuffle_epi32(_Block, 27); + _mm_storeu_si128(static_cast<__m128i*>(_Dest), _Block_reversed); + _Advance_bytes(_Dest, 16); + } while (_Dest != _Stop_at); + } + + _Reverse_copy_tail(static_cast(_First), static_cast(_Last), + static_cast(_Dest)); +} + +__declspec(noalias) void __cdecl __std_reverse_copy_trivially_copyable_8( + const void* _First, const void* _Last, void* _Dest) noexcept { + 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); + do { + _Advance_bytes(_Last, -32); + const __m256i _Block = _mm256_loadu_si256(static_cast(_Last)); + const __m256i _Block_reversed = _mm256_permute4x64_epi64(_Block, 27); + _mm256_storeu_si256(static_cast<__m256i*>(_Dest), _Block_reversed); + _Advance_bytes(_Dest, 32); + } while (_Dest != _Stop_at); + } + + if (_Byte_length(_First, _Last) >= 16 +#ifdef _M_IX86 + && _bittest(&__isa_enabled, __ISA_AVAILABLE_SSE2) +#endif // _M_IX86 + ) { + const void* _Stop_at = _Dest; + _Advance_bytes(_Stop_at, _Byte_length(_First, _Last) >> 4 << 4); + do { + _Advance_bytes(_Last, -16); + const __m128i _Block = _mm_loadu_si128(static_cast(_Last)); + const __m128i _Block_reversed = _mm_shuffle_epi32(_Block, 78); + _mm_storeu_si128(static_cast<__m128i*>(_Dest), _Block_reversed); + _Advance_bytes(_Dest, 16); + } while (_Dest != _Stop_at); + } + + _Reverse_copy_tail(static_cast(_First), static_cast(_Last), + static_cast(_Dest)); +} + + } // extern "C" #endif // (defined(_M_IX86) || defined(_M_X64)) && !defined(_M_CEE_PURE) diff --git a/tests/libcxx/expected_results.txt b/tests/libcxx/expected_results.txt index b2753dc3709..25ee7b5d449 100644 --- a/tests/libcxx/expected_results.txt +++ b/tests/libcxx/expected_results.txt @@ -465,10 +465,6 @@ std/utilities/variant/variant.variant/variant.ctor/T.pass.cpp FAIL # C++20 P0768R1 "Library Support for the Spaceship (Comparison) Operator" std/language.support/support.limits/support.limits.general/compare.version.pass.cpp FAIL -# C++20 P0811R2 "midpoint(), lerp()" -std/language.support/support.limits/support.limits.general/numeric.version.pass.cpp FAIL -std/numerics/c.math/c.math.lerp/c.math.lerp.pass.cpp FAIL - # C++20 P0896R4 "" std/language.support/support.limits/support.limits.general/algorithm.version.pass.cpp FAIL std/language.support/support.limits/support.limits.general/functional.version.pass.cpp FAIL @@ -476,7 +472,6 @@ std/language.support/support.limits/support.limits.general/iterator.version.pass std/language.support/support.limits/support.limits.general/memory.version.pass.cpp FAIL # C++20 P1135R6 "The C++20 Synchronization Library" -std/atomics/atomics.types.operations/atomics.types.operations.wait/atomic_wait.pass.cpp FAIL std/thread/thread.barrier/arrive.pass.cpp FAIL std/thread/thread.barrier/arrive_and_drop.pass.cpp FAIL std/thread/thread.barrier/arrive_and_wait.pass.cpp FAIL @@ -535,6 +530,11 @@ std/thread/macro.pass.cpp:1 FAIL std/depr/depr.c.headers/tgmath_h.pass.cpp:1 FAIL +# *** CLANG FEATURES NOT YET IMPLEMENTED *** +# P0960R3 "Allow initializing aggregates from a parenthesized list of values" +std/utilities/tuple/tuple.tuple/tuple.creation/tuple_cat.pass.cpp SKIPPED # TRANSITION, VS 2019 16.8 p3 + + # *** CLANG ISSUES, NOT YET ANALYZED *** # Clang doesn't enable sized deallocation by default. Should we add -fsized-deallocation or do something else? std/language.support/support.dynamic/new.delete/new.delete.array/sized_delete_array_fsizeddeallocation.pass.cpp:1 FAIL @@ -565,10 +565,6 @@ std/re/re.traits/transform.pass.cpp FAIL # STL bug: Incorrect return types. std/numerics/complex.number/cmplx.over/pow.pass.cpp FAIL -# STL bug: Missing assignment operators. -std/numerics/numarray/template.mask.array/mask.array.assign/mask_array.pass.cpp FAIL -std/numerics/numarray/template.slice.array/slice.arr.assign/slice_array.pass.cpp FAIL - # STL bug: We allow fill() and swap() for array. std/containers/sequences/array/array.fill/fill.fail.cpp FAIL std/containers/sequences/array/array.swap/swap.fail.cpp FAIL @@ -779,6 +775,11 @@ std/iterators/predef.iterators/insert.iterators/insert.iterator/types.pass.cpp F std/numerics/complex.number/cmplx.over/conj.pass.cpp:0 FAIL std/numerics/complex.number/cmplx.over/proj.pass.cpp:0 FAIL +# Assertion failed: (std::lerp(T(2.3), T(2.3), inf) == T(2.3)) +# Asserts `(std::lerp(T(2.3), T(2.3), inf) == T(2.3))` and `std::isnan(std::lerp(T( 0), T( 0), inf))` +# They shouldn't behave differently. Both of them should probably return NaN. +std/numerics/c.math/c.math.lerp/c.math.lerp.pass.cpp FAIL + # *** LIKELY STL BUGS *** # Not yet analyzed, likely STL bugs. Assertions and other runtime failures. diff --git a/tests/libcxx/skipped_tests.txt b/tests/libcxx/skipped_tests.txt index 6f62c1c1b7d..116e6e92f08 100644 --- a/tests/libcxx/skipped_tests.txt +++ b/tests/libcxx/skipped_tests.txt @@ -465,10 +465,6 @@ utilities\variant\variant.variant\variant.ctor\T.pass.cpp # C++20 P0768R1 "Library Support for the Spaceship (Comparison) Operator" language.support\support.limits\support.limits.general\compare.version.pass.cpp -# C++20 P0811R2 "midpoint(), lerp()" -language.support\support.limits\support.limits.general\numeric.version.pass.cpp -numerics\c.math\c.math.lerp\c.math.lerp.pass.cpp - # C++20 P0896R4 "" language.support\support.limits\support.limits.general\algorithm.version.pass.cpp language.support\support.limits\support.limits.general\functional.version.pass.cpp @@ -476,7 +472,6 @@ language.support\support.limits\support.limits.general\iterator.version.pass.cpp language.support\support.limits\support.limits.general\memory.version.pass.cpp # C++20 P1135R6 "The C++20 Synchronization Library" -atomics\atomics.types.operations\atomics.types.operations.wait\atomic_wait.pass.cpp thread\thread.barrier\arrive.pass.cpp thread\thread.barrier\arrive_and_drop.pass.cpp thread\thread.barrier\arrive_and_wait.pass.cpp @@ -535,6 +530,11 @@ thread\macro.pass.cpp depr\depr.c.headers\tgmath_h.pass.cpp +# *** CLANG FEATURES NOT YET IMPLEMENTED *** +# P0960R3 "Allow initializing aggregates from a parenthesized list of values" +utilities\tuple\tuple.tuple\tuple.creation\tuple_cat.pass.cpp + + # *** CLANG ISSUES, NOT YET ANALYZED *** # Clang doesn't enable sized deallocation by default. Should we add -fsized-deallocation or do something else? language.support\support.dynamic\new.delete\new.delete.array\sized_delete_array_fsizeddeallocation.pass.cpp @@ -565,10 +565,6 @@ re\re.traits\transform.pass.cpp # STL bug: Incorrect return types. numerics\complex.number\cmplx.over\pow.pass.cpp -# STL bug: Missing assignment operators. -numerics\numarray\template.mask.array\mask.array.assign\mask_array.pass.cpp -numerics\numarray\template.slice.array\slice.arr.assign\slice_array.pass.cpp - # STL bug: We allow fill() and swap() for array. containers\sequences\array\array.fill\fill.fail.cpp containers\sequences\array\array.swap\swap.fail.cpp @@ -779,6 +775,11 @@ iterators\predef.iterators\insert.iterators\insert.iterator\types.pass.cpp numerics\complex.number\cmplx.over\conj.pass.cpp numerics\complex.number\cmplx.over\proj.pass.cpp +# Assertion failed: (std::lerp(T(2.3), T(2.3), inf) == T(2.3)) +# Asserts `(std::lerp(T(2.3), T(2.3), inf) == T(2.3))` and `std::isnan(std::lerp(T( 0), T( 0), inf))` +# They shouldn't behave differently. Both of them should probably return NaN. +numerics\c.math\c.math.lerp\c.math.lerp.pass.cpp + # *** LIKELY STL BUGS *** # Not yet analyzed, likely STL bugs. Assertions and other runtime failures. diff --git a/tests/std/include/floating_point_test_cases.hpp b/tests/std/include/floating_point_test_cases.hpp index 46b8172ead1..d463fb8f3f7 100644 --- a/tests/std/include/floating_point_test_cases.hpp +++ b/tests/std/include/floating_point_test_cases.hpp @@ -62,13 +62,13 @@ constexpr std::pair floating_point_test_cases_double[] = {"2.2250738585072004e-308", 0x000FFFFFFFFFFFFEULL}, {"2.2250738585072010e-308", 0x000FFFFFFFFFFFFFULL}, - // DevDiv#576315 "I/O library incorrect rounds floating point numbers on input" - // DevDiv#616647 "Visual C++ 11: iostream bug: incorrect input streaming of the smallest normal double and some + // DevDiv-576315 "I/O library incorrect rounds floating point numbers on input" + // DevDiv-616647 "Visual C++ 11: iostream bug: incorrect input streaming of the smallest normal double and some // denormals" - // DevDiv#730414 "iostreams is still misparsing floating-point" - // DevDiv#938627 "parsing float values using std::istream gives results inconsistent with sscanf() and with C++ + // DevDiv-730414 "iostreams is still misparsing floating-point" + // DevDiv-938627 "parsing float values using std::istream gives results inconsistent with sscanf() and with C++ // compiler" - // DevDiv#961116 "floating point string conversion accuracy" + // DevDiv-961116 "floating point string conversion accuracy" {"2.2250738585072014e-308", 0x0010000000000000ULL}, // DBL_MIN {"1.7976931348623158e+308", 0x7FEFFFFFFFFFFFFFULL}, // DBL_MAX {"4.26144921954407e-309", 0x00031076B2F00000ULL}, @@ -257,13 +257,13 @@ constexpr std::pair floating_point_test_cases_float[] = { {"1.1754940705625946e-38", 0x007FFFFEU}, {"1.1754942106924411e-38", 0x007FFFFFU}, - // DevDiv#576315 "I/O library incorrect rounds floating point numbers on input" - // DevDiv#616647 "Visual C++ 11: iostream bug: incorrect input streaming of the smallest normal double and some + // DevDiv-576315 "I/O library incorrect rounds floating point numbers on input" + // DevDiv-616647 "Visual C++ 11: iostream bug: incorrect input streaming of the smallest normal double and some // denormals" - // DevDiv#730414 "iostreams is still misparsing floating-point" - // DevDiv#938627 "parsing float values using std::istream gives results inconsistent with sscanf() and with C++ + // DevDiv-730414 "iostreams is still misparsing floating-point" + // DevDiv-938627 "parsing float values using std::istream gives results inconsistent with sscanf() and with C++ // compiler" - // DevDiv#961116 "floating point string conversion accuracy" + // DevDiv-961116 "floating point string conversion accuracy" {"1.175494351e-38", 0x00800000U}, // FLT_MIN {"3.402823466e+38", 0x7F7FFFFFU}, // FLT_MAX {"179.9999999999999855", 0x43340000U}, diff --git a/tests/std/include/instantiate_algorithms.hpp b/tests/std/include/instantiate_algorithms.hpp index 47d81e69976..5d19929cb36 100644 --- a/tests/std/include/instantiate_algorithms.hpp +++ b/tests/std/include/instantiate_algorithms.hpp @@ -415,6 +415,9 @@ namespace std_testing { test_exec_fwd1_fwd2(std::execution::seq, fwd1, fwd2); test_exec_fwd1_fwd2(std::execution::par, fwd1, fwd2); test_exec_fwd1_fwd2(std::execution::par_unseq, fwd1, fwd2); +#if _HAS_CXX20 + test_exec_fwd1_fwd2(std::execution::unseq, fwd1, fwd2); +#endif // _HAS_CXX20 #endif // HAS_PARALLEL_ALGORITHMS (void) std::find_end(fwd1, fwd1, fwd2, fwd2); @@ -503,6 +506,9 @@ namespace std_testing { test_exec_fwd1(std::execution::seq, fwd1); test_exec_fwd1(std::execution::par, fwd1); test_exec_fwd1(std::execution::par_unseq, fwd1); +#if _HAS_CXX20 + test_exec_fwd1(std::execution::unseq, fwd1); +#endif // _HAS_CXX20 #endif // HAS_PARALLEL_ALGORITHMS test_fwd1_fwd2(fwd1, FWDIT); @@ -592,6 +598,9 @@ namespace std_testing { test_exec_bid1_bid2_xxx_backward(std::execution::seq, bid1, bid2); test_exec_bid1_bid2_xxx_backward(std::execution::par, bid1, bid2); test_exec_bid1_bid2_xxx_backward(std::execution::par_unseq, bid1, bid2); +#if _HAS_CXX20 + test_exec_bid1_bid2_xxx_backward(std::execution::unseq, bid1, bid2); +#endif // _HAS_CXX20 #endif // HAS_PARALLEL_ALGORITHMS std::copy_backward(bid1, bid1, bid2); @@ -615,6 +624,9 @@ namespace std_testing { test_exec_bid1_fwd1(std::execution::seq, bid1, fwd1); test_exec_bid1_fwd1(std::execution::par, bid1, fwd1); test_exec_bid1_fwd1(std::execution::par_unseq, bid1, fwd1); +#if _HAS_CXX20 + test_exec_bid1_fwd1(std::execution::unseq, bid1, fwd1); +#endif // _HAS_CXX20 } template @@ -653,6 +665,9 @@ namespace std_testing { test_exec_bid1(std::execution::seq, bid1); test_exec_bid1(std::execution::par, bid1); test_exec_bid1(std::execution::par_unseq, bid1); +#if _HAS_CXX20 + test_exec_bid1(std::execution::unseq, bid1); +#endif // _HAS_CXX20 #endif // HAS_PARALLEL_ALGORITHMS std::reverse(bid1, bid1); @@ -700,6 +715,9 @@ namespace std_testing { test_exec_ran(std::execution::seq, ran); test_exec_ran(std::execution::par, ran); test_exec_ran(std::execution::par_unseq, ran); +#if _HAS_CXX20 + test_exec_ran(std::execution::unseq, ran); +#endif // _HAS_CXX20 #endif // HAS_PARALLEL_ALGORITHMS #if _HAS_AUTO_PTR_ETC diff --git a/tests/std/include/instantiate_algorithms_op_deref.hpp b/tests/std/include/instantiate_algorithms_op_deref.hpp index 4ec4e0435e6..d2bfbab5e81 100644 --- a/tests/std/include/instantiate_algorithms_op_deref.hpp +++ b/tests/std/include/instantiate_algorithms_op_deref.hpp @@ -7,8 +7,8 @@ #include -// DevDiv#758138 "Several algorithms break with iterators that overload the comma operator." -// DevDiv#758134 "uninitialized_copy and uninitialized_copy_n break with classes that overload operator &." +// DevDiv-758138 "Several algorithms break with iterators that overload the comma operator." +// DevDiv-758134 "uninitialized_copy and uninitialized_copy_n break with classes that overload operator &." // ADL will search this namespace for op,(). namespace Meow { @@ -310,7 +310,7 @@ void test() { Meow::BasicBidIt{}, Meow::BasicRanIt{}, Meow::BasicOutIt{11, 22}); } -// Also test DevDiv#938759 ": is_assignable should tolerate overloaded comma operators [libcxx]". +// Also test DevDiv-938759 ": is_assignable should tolerate overloaded comma operators [libcxx]". #define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) STATIC_ASSERT(std::is_assignable_v); diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index 1a6b09496eb..de785c12555 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -1028,11 +1028,6 @@ constexpr void test_contiguous() { with_contiguous_ranges::call(); } -template -constexpr void input_range_output_iterator_permutations() { - with_input_ranges, Element1>::call(); -} - template constexpr void test_in_in() { with_input_ranges, Element1>::call(); @@ -1053,6 +1048,11 @@ constexpr void test_bidi_bidi() { with_bidirectional_ranges, Element1>::call(); } +template +constexpr void input_range_output_iterator_permutations() { + with_input_ranges, Element1>::call(); +} + template constexpr void test_in_write() { with_input_ranges, Element1>::call(); @@ -1063,6 +1063,16 @@ constexpr void test_fwd_write() { with_forward_ranges, Element1>::call(); } +template +constexpr void test_bidi_write() { + with_bidirectional_ranges, Element1>::call(); +} + +template +constexpr void test_contiguous_write() { + with_contiguous_ranges, Element1>::call(); +} + template constexpr void test_read() { with_input_iterators::call(); diff --git a/tests/std/include/test_atomic_wait.hpp b/tests/std/include/test_atomic_wait.hpp new file mode 100644 index 00000000000..a856798f3aa --- /dev/null +++ b/tests/std/include/test_atomic_wait.hpp @@ -0,0 +1,215 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once + +#include +#include +#include +#include +#include +#include + +template +void test_atomic_wait_func(const UnderlyingType old_value, const UnderlyingType new_value, + const std::chrono::steady_clock::duration waiting_duration) { + constexpr int seq_max_size = 10; + char seq[seq_max_size + 1]; + std::atomic base = seq; + auto add_seq = [&](char ch) { + char* p = base.fetch_add(1, std::memory_order_relaxed); + assert(p - seq < seq_max_size); + *p = ch; + }; + + std::atomic a{old_value}; + a.wait(new_value); + + add_seq('1'); + + std::thread thd([&] { + std::this_thread::sleep_for(waiting_duration); + add_seq('2'); + a.notify_all(); + std::this_thread::sleep_for(waiting_duration); + add_seq('3'); + a.store(old_value); + a.notify_one(); + std::this_thread::sleep_for(waiting_duration); + add_seq('4'); + a.store(new_value); + a.notify_one(); +#ifdef CAN_FAIL_ON_TIMING_ASSUMPTION + // timing assumption that the main thread evaluates the `wait(old_value)` before this timeout expires + std::this_thread::sleep_for(waiting_duration); + add_seq('6'); +#endif // CAN_FAIL_ON_TIMING_ASSUMPTION + }); + + a.wait(old_value); + const auto loaded = a.load(); + assert(memcmp(&loaded, &new_value, sizeof(UnderlyingType)) == 0); + + add_seq('5'); + + thd.join(); + + add_seq('\0'); + +#ifdef CAN_FAIL_ON_TIMING_ASSUMPTION + assert(strcmp(seq, "123456") == 0); +#else + assert(strcmp(seq, "12345") == 0); +#endif +} + +template +void test_notify_all_notifies_all(const UnderlyingType old_value, const UnderlyingType new_value, + const std::chrono::steady_clock::duration waiting_duration) { + std::atomic c{old_value}; + const auto waitFn = [&c, old_value] { c.wait(old_value); }; + + std::thread w1{waitFn}; + std::thread w2{waitFn}; + std::thread w3{waitFn}; + + std::this_thread::sleep_for(waiting_duration); + c.store(new_value); + c.notify_all(); // if this doesn't really notify all, the following joins will deadlock + + w1.join(); + w2.join(); + w3.join(); +} + +template +void test_pad_bits(const std::chrono::steady_clock::duration waiting_duration) { + UnderlyingType old_value; + memset(&old_value, 0x66, sizeof(UnderlyingType)); + old_value.set(1); + + UnderlyingType same_old_value; + memset(&same_old_value, 0x99, sizeof(UnderlyingType)); + same_old_value.set(1); + + std::atomic c(old_value); + + bool trigger = false; + const auto waitFn = [&c, same_old_value, &trigger] { + c.wait(same_old_value); + trigger = true; + }; + + std::thread w1{waitFn}; + + std::this_thread::sleep_for(waiting_duration); + assert(!trigger); + + c.store(old_value); + c.notify_one(); + + std::this_thread::sleep_for(waiting_duration); + assert(!trigger); + + UnderlyingType new_value; + memset(&new_value, 0x99, sizeof(UnderlyingType)); + new_value.set(2); + c.store(new_value); + c.notify_one(); + + std::this_thread::sleep_for(waiting_duration); + assert(trigger); + + w1.join(); +} + +struct two_shorts { + short a; + short b; + + friend bool operator==(two_shorts, two_shorts) = delete; +}; + +struct three_chars { + char a; + char b; + char c; + + friend bool operator==(three_chars, three_chars) = delete; +}; + +struct big_char_like { + char value; + char unused[16]; + + explicit big_char_like(char value_) : value(value_), unused{} {} + + friend bool operator==(big_char_like, big_char_like) = delete; +}; + +template +struct with_padding_bits { + alignas(size) char value; + + void set(const char value_) { + value = value_; + } + + friend bool operator==(with_padding_bits, with_padding_bits) = delete; +}; + +inline void test_atomic_wait() { + // wait for all the threads to be waiting; if this value is too small the test might be ineffective but should not + // fail due to timing assumptions except where otherwise noted; if it is too large the test will only take longer + // than necessary + constexpr std::chrono::milliseconds waiting_duration{100}; + test_atomic_wait_func(1, 2, waiting_duration); + test_atomic_wait_func(1, 2, waiting_duration); + test_atomic_wait_func(1, 2, waiting_duration); + test_atomic_wait_func(1, 2, waiting_duration); + test_atomic_wait_func(1, 2, waiting_duration); + test_atomic_wait_func(1, 2, waiting_duration); + test_atomic_wait_func(1, 2, waiting_duration); + test_atomic_wait_func(1, 2, waiting_duration); + test_atomic_wait_func(1, 2, waiting_duration); + test_atomic_wait_func(1, 2, waiting_duration); + test_atomic_wait_func(1, 2, waiting_duration); + test_atomic_wait_func(1, 2, waiting_duration); + test_atomic_wait_func(1, 2, waiting_duration); + test_atomic_wait_func(1, 2, waiting_duration); + test_atomic_wait_func("1", "2", waiting_duration); + test_atomic_wait_func(two_shorts{1, 1}, two_shorts{1, 2}, waiting_duration); + test_atomic_wait_func(three_chars{1, 1, 3}, three_chars{1, 2, 3}, waiting_duration); + test_atomic_wait_func(big_char_like{'a'}, big_char_like{'b'}, waiting_duration); + + test_atomic_wait_func(std::make_shared('a'), std::make_shared('b'), waiting_duration); + test_atomic_wait_func( + std::weak_ptr{std::make_shared('a')}, std::weak_ptr{std::make_shared('b')}, waiting_duration); + + test_notify_all_notifies_all(1, 2, waiting_duration); + test_notify_all_notifies_all(1, 2, waiting_duration); + test_notify_all_notifies_all(1, 2, waiting_duration); + test_notify_all_notifies_all(1, 2, waiting_duration); + test_notify_all_notifies_all(1, 2, waiting_duration); + test_notify_all_notifies_all(1, 2, waiting_duration); + test_notify_all_notifies_all(1, 2, waiting_duration); + test_notify_all_notifies_all(1, 2, waiting_duration); + test_notify_all_notifies_all(1, 2, waiting_duration); + test_notify_all_notifies_all(1, 2, waiting_duration); + test_notify_all_notifies_all(1, 2, waiting_duration); + test_notify_all_notifies_all(1, 2, waiting_duration); + test_notify_all_notifies_all(1, 2, waiting_duration); + test_notify_all_notifies_all(1, 2, waiting_duration); + test_notify_all_notifies_all("1", "2", waiting_duration); + test_notify_all_notifies_all(two_shorts{1, 1}, two_shorts{1, 2}, waiting_duration); + test_notify_all_notifies_all(three_chars{1, 1, 3}, three_chars{1, 2, 3}, waiting_duration); + test_notify_all_notifies_all(big_char_like{'a'}, big_char_like{'b'}, waiting_duration); + +#ifndef __clang__ // TRANSITION, LLVM-46685 + test_pad_bits>(waiting_duration); + test_pad_bits>(waiting_duration); + test_pad_bits>(waiting_duration); + test_pad_bits>(waiting_duration); + test_pad_bits>(waiting_duration); +#endif // __clang__, TRANSITION, LLVM-46685 +} diff --git a/tests/std/test.lst b/tests/std/test.lst index 504942ff6d8..a1edb5c00f6 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -157,10 +157,14 @@ tests\Dev11_1158803_regex_thread_safety tests\Dev11_1180290_filesystem_error_code tests\GH_000457_system_error_message tests\GH_000545_include_compare +tests\GH_000625_vector_bool_optimization tests\GH_000685_condition_variable_any tests\GH_000690_overaligned_function tests\GH_000890_pow_template +tests\GH_000940_missing_valarray_copy tests\GH_001010_filesystem_error_encoding +tests\GH_001017_discrete_distribution_out_of_range +tests\GH_001086_partial_sort_copy tests\LWG2597_complex_branch_cut tests\LWG3018_shared_ptr_function tests\P0024R2_parallel_algorithms_adjacent_difference @@ -220,6 +224,7 @@ tests\P0433R2_deduction_guides tests\P0476R2_bit_cast tests\P0487R1_fixing_operator_shl_basic_istream_char_pointer tests\P0513R0_poisoning_the_hash +tests\P0528R3_cmpxchg_pad tests\P0553R4_bit_rotating_and_counting_functions tests\P0556R3_bit_integral_power_of_two_operations tests\P0586R2_integer_comparison @@ -235,7 +240,6 @@ tests\P0769R2_shift_left_shift_right tests\P0784R7_library_support_for_more_constexpr_containers tests\P0811R3_midpoint_lerp tests\P0896R4_P1614R2_comparisons -tests\P0896R4_ranges_algorithm_machinery tests\P0896R4_ranges_alg_adjacent_find tests\P0896R4_ranges_alg_all_of tests\P0896R4_ranges_alg_any_of @@ -259,15 +263,21 @@ tests\P0896R4_ranges_alg_for_each_n tests\P0896R4_ranges_alg_generate tests\P0896R4_ranges_alg_generate_n tests\P0896R4_ranges_alg_heap +tests\P0896R4_ranges_alg_includes tests\P0896R4_ranges_alg_is_permutation tests\P0896R4_ranges_alg_is_sorted +tests\P0896R4_ranges_alg_lexicographical_compare +tests\P0896R4_ranges_alg_merge tests\P0896R4_ranges_alg_minmax tests\P0896R4_ranges_alg_mismatch tests\P0896R4_ranges_alg_move +tests\P0896R4_ranges_alg_move_backward tests\P0896R4_ranges_alg_none_of +tests\P0896R4_ranges_alg_nth_element tests\P0896R4_ranges_alg_partition tests\P0896R4_ranges_alg_partition_copy tests\P0896R4_ranges_alg_partition_point +tests\P0896R4_ranges_alg_permutations tests\P0896R4_ranges_alg_remove tests\P0896R4_ranges_alg_remove_copy tests\P0896R4_ranges_alg_remove_copy_if @@ -277,11 +287,23 @@ tests\P0896R4_ranges_alg_replace_copy tests\P0896R4_ranges_alg_replace_copy_if tests\P0896R4_ranges_alg_replace_if tests\P0896R4_ranges_alg_reverse +tests\P0896R4_ranges_alg_reverse_copy +tests\P0896R4_ranges_alg_rotate +tests\P0896R4_ranges_alg_rotate_copy +tests\P0896R4_ranges_alg_sample tests\P0896R4_ranges_alg_search tests\P0896R4_ranges_alg_search_n +tests\P0896R4_ranges_alg_set_difference +tests\P0896R4_ranges_alg_set_intersection +tests\P0896R4_ranges_alg_set_symmetric_difference +tests\P0896R4_ranges_alg_set_union +tests\P0896R4_ranges_alg_shuffle tests\P0896R4_ranges_alg_swap_ranges tests\P0896R4_ranges_alg_transform_binary tests\P0896R4_ranges_alg_transform_unary +tests\P0896R4_ranges_alg_unique +tests\P0896R4_ranges_alg_unique_copy +tests\P0896R4_ranges_algorithm_machinery tests\P0896R4_ranges_iterator_machinery tests\P0896R4_ranges_range_machinery tests\P0896R4_ranges_subrange @@ -294,6 +316,8 @@ tests\P0966R1_string_reserve_should_not_shrink tests\P1023R0_constexpr_for_array_comparisons tests\P1032R1_miscellaneous_constexpr tests\P1135R6_atomic_flag_test +tests\P1135R6_atomic_wait +tests\P1135R6_atomic_wait_vista tests\P1165R1_consistently_propagating_stateful_allocators tests\P1423R3_char8_t_remediation tests\P1614R2_spaceship diff --git a/tests/std/tests/Dev09_119644_compiler_option_gz/test.cpp b/tests/std/tests/Dev09_119644_compiler_option_gz/test.cpp index 1f5964b0740..ffa08916d92 100644 --- a/tests/std/tests/Dev09_119644_compiler_option_gz/test.cpp +++ b/tests/std/tests/Dev09_119644_compiler_option_gz/test.cpp @@ -8,6 +8,6 @@ int main() { #else int __cdecl main() { #endif - // Test Dev10#465793 "iostreams: is incompatible with /Gr and /Gz". + // Test Dev10-465793 "iostreams: is incompatible with /Gr and /Gz". std::locale loc("english_US"); } diff --git a/tests/std/tests/Dev09_153419_tr1_allocators/test.cpp b/tests/std/tests/Dev09_153419_tr1_allocators/test.cpp index e82ad3bc489..50abbf12489 100644 --- a/tests/std/tests/Dev09_153419_tr1_allocators/test.cpp +++ b/tests/std/tests/Dev09_153419_tr1_allocators/test.cpp @@ -395,7 +395,7 @@ int main() { #endif // _HAS_FUNCTION_ALLOCATOR_SUPPORT { - // Test Dev10#531321 "function: tr1::function memory leak". + // Test Dev10-531321 "function: tr1::function memory leak". Big b(10, 20, 30, 40); function f; diff --git a/tests/std/tests/Dev09_158181_tr1_unordered_meow_swap/test.cpp b/tests/std/tests/Dev09_158181_tr1_unordered_meow_swap/test.cpp index 9696d173537..8692ce252ce 100644 --- a/tests/std/tests/Dev09_158181_tr1_unordered_meow_swap/test.cpp +++ b/tests/std/tests/Dev09_158181_tr1_unordered_meow_swap/test.cpp @@ -30,7 +30,7 @@ void assert_throws(Fx fn) noexcept { } } -// Test DDB#158181. unordered_set.swap() was O(N), throwing, and invalidating iterators, which was bad. +// Test DDB-158181. unordered_set.swap() was O(N), throwing, and invalidating iterators, which was bad. void test_ddb_158181() { unordered_set x; x.insert(11); diff --git a/tests/std/tests/Dev09_158457_tr1_mem_fn_calling_conventions/test.cpp b/tests/std/tests/Dev09_158457_tr1_mem_fn_calling_conventions/test.cpp index c303105ce08..1c2aa9b9537 100644 --- a/tests/std/tests/Dev09_158457_tr1_mem_fn_calling_conventions/test.cpp +++ b/tests/std/tests/Dev09_158457_tr1_mem_fn_calling_conventions/test.cpp @@ -168,6 +168,14 @@ STATIC_ASSERT(is_member_function_pointer_v); STATIC_ASSERT(is_member_function_pointer_v); STATIC_ASSERT(is_member_function_pointer_v); +STATIC_ASSERT(is_member_pointer_v); +STATIC_ASSERT(is_member_pointer_v); +STATIC_ASSERT(is_member_pointer_v); +STATIC_ASSERT(is_member_pointer_v); +STATIC_ASSERT(is_member_pointer_v); +STATIC_ASSERT(is_member_pointer_v); +STATIC_ASSERT(is_member_pointer_v); + STATIC_ASSERT(is_function_v); STATIC_ASSERT(is_function_v); STATIC_ASSERT(is_function_v); diff --git a/tests/std/tests/Dev09_172666_tr1_tuple_odr/__init__.py b/tests/std/tests/Dev09_172666_tr1_tuple_odr/__init__.py new file mode 100644 index 00000000000..2ac2a854cb0 --- /dev/null +++ b/tests/std/tests/Dev09_172666_tr1_tuple_odr/__init__.py @@ -0,0 +1,2 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception diff --git a/tests/std/tests/Dev09_172666_tr1_tuple_odr/custom_format.py b/tests/std/tests/Dev09_172666_tr1_tuple_odr/custom_format.py new file mode 100644 index 00000000000..323790ec7cb --- /dev/null +++ b/tests/std/tests/Dev09_172666_tr1_tuple_odr/custom_format.py @@ -0,0 +1,23 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +from pathlib import Path + +from stl.test.format import STLTestFormat, TestStep + + +class CustomTestFormat(STLTestFormat): + def getBuildSteps(self, test, lit_config, shared): + shared.exec_dir = test.getExecDir() + exe_source = Path(test.getSourcePath()) + test2_source = exe_source.parent / 'test2.cpp' + output_base = test.getOutputBaseName() + output_dir = test.getOutputDir() + + cmd, out_files, shared.exec_file = \ + test.cxx.executeBasedOnFlagsCmd([exe_source, test2_source], + output_dir, shared.exec_dir, + output_base, [], [], []) + + yield TestStep(cmd, shared.exec_dir, [exe_source, test2_source], + test.cxx.compile_env) diff --git a/tests/std/tests/Dev09_172666_tr1_tuple_odr/lit.local.cfg b/tests/std/tests/Dev09_172666_tr1_tuple_odr/lit.local.cfg new file mode 100644 index 00000000000..4c325b48119 --- /dev/null +++ b/tests/std/tests/Dev09_172666_tr1_tuple_odr/lit.local.cfg @@ -0,0 +1,10 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import Dev09_172666_tr1_tuple_odr.custom_format + +config.test_format = \ + Dev09_172666_tr1_tuple_odr.custom_format.CustomTestFormat(config.test_format.cxx, + config.test_format.execute_external, + config.test_format.build_executor, + config.test_format.test_executor) diff --git a/tests/std/tests/Dev09_172666_tr1_tuple_odr/one.cpp b/tests/std/tests/Dev09_172666_tr1_tuple_odr/test.cpp similarity index 62% rename from tests/std/tests/Dev09_172666_tr1_tuple_odr/one.cpp rename to tests/std/tests/Dev09_172666_tr1_tuple_odr/test.cpp index 05cd4dc827d..ae6e80c61e9 100644 --- a/tests/std/tests/Dev09_172666_tr1_tuple_odr/one.cpp +++ b/tests/std/tests/Dev09_172666_tr1_tuple_odr/test.cpp @@ -9,17 +9,18 @@ namespace fs = std::experimental::filesystem; int meow(); inline bool test_wchar_t_minus() { - // Test for DevDiv Bug#1004799: : /Zc:wchar_t- explodes. Calling file_size + // Test for DevDiv-1004799: : /Zc:wchar_t- explodes. Calling file_size // should cause the blow-up to occur if we are wchar_t incorrect. Test is disabled // (i.e. always passes) if compiled with /clr:pure and /Zc:wchar_t-, as it triggers // LNK2031: calling convention missing in metadata errors, which are irrelevant here. #if defined(_M_CEE_PURE) && !defined(_NATIVE_WCHAR_T_DEFINED) return true; -#else - return fs::file_size(fs::current_path() / "two.cpp") != 0u; -#endif +#else // ^^^ /clr:pure /Zc:wchar_t- / Other vvv + return fs::file_size(fs::current_path() / "Dev09_172666_tr1_tuple_odr.exe") != 0u; +#endif // defined(_M_CEE_PURE) && !defined(_NATIVE_WCHAR_T_DEFINED) } int main() { - assert(meow() == 1729 && test_wchar_t_minus()); + assert(meow() == 1729); + assert(test_wchar_t_minus()); } diff --git a/tests/std/tests/Dev09_172666_tr1_tuple_odr/two.cpp b/tests/std/tests/Dev09_172666_tr1_tuple_odr/test2.cpp similarity index 100% rename from tests/std/tests/Dev09_172666_tr1_tuple_odr/two.cpp rename to tests/std/tests/Dev09_172666_tr1_tuple_odr/test2.cpp diff --git a/tests/std/tests/Dev09_174589_tr1_function_storing_pmf_called_with_reference_or_pointer/test.cpp b/tests/std/tests/Dev09_174589_tr1_function_storing_pmf_called_with_reference_or_pointer/test.cpp index fb23c6e66f0..1b4ba4b844b 100644 --- a/tests/std/tests/Dev09_174589_tr1_function_storing_pmf_called_with_reference_or_pointer/test.cpp +++ b/tests/std/tests/Dev09_174589_tr1_function_storing_pmf_called_with_reference_or_pointer/test.cpp @@ -28,7 +28,7 @@ void test_orig() { } -// DevDiv#294051 ": std::function has lost the ability to invoke PMFs/PMDs on various things" +// DevDiv-294051 ": std::function has lost the ability to invoke PMFs/PMDs on various things" // FDIS 20.8.11.2 [func.wrap.func] specifies: // template class function diff --git a/tests/std/tests/Dev09_181509_tr1_inf_loop_uniform_int_ull/test.cpp b/tests/std/tests/Dev09_181509_tr1_inf_loop_uniform_int_ull/test.cpp index 9554a5615f7..0859477411d 100644 --- a/tests/std/tests/Dev09_181509_tr1_inf_loop_uniform_int_ull/test.cpp +++ b/tests/std/tests/Dev09_181509_tr1_inf_loop_uniform_int_ull/test.cpp @@ -28,7 +28,7 @@ void microtest() { using fp_t = void (*)(); template void add_tests(vector& tests) { - // Test DevDiv#83370 "uniform_int_distribution isn't uniform". + // Test DevDiv-83370 "uniform_int_distribution isn't uniform". tests.insert( tests.end(), { microtest, @@ -55,7 +55,7 @@ void add_tests(vector& tests) { microtest, microtest, microtest, - microtest, // Test DDB#181509 "TR1 VC9 SP1: Infinite loop in + microtest, // Test DDB-181509 "TR1 VC9 SP1: Infinite loop in // uniform_int::_Eval()". microtest, diff --git a/tests/std/tests/Dev09_196243_tr1_enable_shared_from_this_ops/test.cpp b/tests/std/tests/Dev09_196243_tr1_enable_shared_from_this_ops/test.cpp index 71d6180947b..374d3dee433 100644 --- a/tests/std/tests/Dev09_196243_tr1_enable_shared_from_this_ops/test.cpp +++ b/tests/std/tests/Dev09_196243_tr1_enable_shared_from_this_ops/test.cpp @@ -12,7 +12,7 @@ struct X : enable_shared_from_this { }; int main() { - // Test DDB#196243 "TR1 VC9 SP1: enable_shared_from_this's copy ctor and copy assignment operator do too much work". + // Test DDB-196243 "TR1 VC9 SP1: enable_shared_from_this's copy ctor and copy assignment operator do too much work". { const shared_ptr sp1(new X(11)); const shared_ptr sp2(new X(22)); @@ -37,7 +37,7 @@ int main() { assert(raw2->shared_from_this() != sp1); } - // Test DDB#197048 "[VS2008 / TR1] still got problems with shared_ptr". + // Test DDB-197048 "[VS2008 / TR1] still got problems with shared_ptr". { shared_ptr sp1(static_cast(new int(6))); shared_ptr sp2(static_cast(new int(7))); @@ -48,7 +48,7 @@ int main() { assert(*sp3 == 8); } - // Test Dev10#654944 "shared_ptr: assignment is messed up". + // Test Dev10-654944 "shared_ptr: assignment is messed up". { shared_ptr p(new int(1729)); shared_ptr z; @@ -63,7 +63,7 @@ int main() { assert(!z); } - // Test DevDiv#1178296 ": shared_ptr doesn't work with enable_shared_from_this". + // Test DevDiv-1178296 ": shared_ptr doesn't work with enable_shared_from_this". { const auto sp1 = make_shared(100); const auto sp2 = make_shared(200); diff --git a/tests/std/tests/Dev10_544258_heterogeneous_comparisons/test.cpp b/tests/std/tests/Dev10_544258_heterogeneous_comparisons/test.cpp index 55ed5f530d4..a2501ca712e 100644 --- a/tests/std/tests/Dev10_544258_heterogeneous_comparisons/test.cpp +++ b/tests/std/tests/Dev10_544258_heterogeneous_comparisons/test.cpp @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// Dev10#544258 "STL/xutility: Debug validation of predicate classes fail on bound predicates" -// DevDiv#813065 ": C++ stdlib comparator debugging breaks compile of custom Comparators in std::equal_range +// Dev10-544258 "STL/xutility: Debug validation of predicate classes fail on bound predicates" +// DevDiv-813065 ": C++ stdlib comparator debugging breaks compile of custom Comparators in std::equal_range // family of functions" #include diff --git a/tests/std/tests/Dev10_561430_list_and_tree_leaks/test.cpp b/tests/std/tests/Dev10_561430_list_and_tree_leaks/test.cpp index ae713cfd200..9c21039cc74 100644 --- a/tests/std/tests/Dev10_561430_list_and_tree_leaks/test.cpp +++ b/tests/std/tests/Dev10_561430_list_and_tree_leaks/test.cpp @@ -24,7 +24,7 @@ int g_mallocs = 0; -// Also test DevDiv#483844 and DevDiv#781187, minimal allocator requirements. +// Also test DevDiv-483844 and DevDiv-781187, minimal allocator requirements. template struct Mallocator { typedef T value_type; @@ -192,7 +192,7 @@ int main() { } -// Also test DevDiv#819467 ": Custom allocator with virtual max_size function causes infinite recursion". +// Also test DevDiv-819467 ": Custom allocator with virtual max_size function causes infinite recursion". template struct WeirdAllocator { diff --git a/tests/std/tests/Dev10_635436_shared_ptr_reset/test.cpp b/tests/std/tests/Dev10_635436_shared_ptr_reset/test.cpp index 176a3ff8d14..0dd30270dce 100644 --- a/tests/std/tests/Dev10_635436_shared_ptr_reset/test.cpp +++ b/tests/std/tests/Dev10_635436_shared_ptr_reset/test.cpp @@ -118,7 +118,7 @@ Kitten::~Kitten() { } int main() { - // Dev10#635436 "shared_ptr: reset() must behave as if it is implemented with swap()" + // Dev10-635436 "shared_ptr: reset() must behave as if it is implemented with swap()" results.emplace_back("BEGIN", 0); Cat* p0 = new Cat(1729); @@ -152,7 +152,7 @@ int main() { results.emplace_back("END", 3); - // DevDiv#523246 "std::unique_ptr deletes owned object before resetting pointer rather than after." + // DevDiv-523246 "std::unique_ptr deletes owned object before resetting pointer rather than after." results.emplace_back("BEGIN", 4); Kitten* p4 = new Kitten(257); diff --git a/tests/std/tests/Dev10_682964_stable_sort_warnings/test.cpp b/tests/std/tests/Dev10_682964_stable_sort_warnings/test.cpp index 9239d678c3c..a8653938c60 100644 --- a/tests/std/tests/Dev10_682964_stable_sort_warnings/test.cpp +++ b/tests/std/tests/Dev10_682964_stable_sort_warnings/test.cpp @@ -62,7 +62,7 @@ int main() { { - // Also test DevDiv#957501 ": stable_sort calls self-move-assignment operator". + // Also test DevDiv-957501 ": stable_sort calls self-move-assignment operator". class NoSelfMove { public: diff --git a/tests/std/tests/Dev10_722102_shared_ptr_nullptr/test.cpp b/tests/std/tests/Dev10_722102_shared_ptr_nullptr/test.cpp index bc6fa5e314e..0c753db3ddd 100644 --- a/tests/std/tests/Dev10_722102_shared_ptr_nullptr/test.cpp +++ b/tests/std/tests/Dev10_722102_shared_ptr_nullptr/test.cpp @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// Dev10#722102 "STL: Get nullptr overloads" -// DevDiv#520681 "Faulty implementation of shared_ptr(nullptr_t) constructor" +// Dev10-722102 "STL: Get nullptr overloads" +// DevDiv-520681 "Faulty implementation of shared_ptr(nullptr_t) constructor" #include #include diff --git a/tests/std/tests/Dev10_766948_insert_ambiguity/test.cpp b/tests/std/tests/Dev10_766948_insert_ambiguity/test.cpp index a73e3632715..8340d994fec 100644 --- a/tests/std/tests/Dev10_766948_insert_ambiguity/test.cpp +++ b/tests/std/tests/Dev10_766948_insert_ambiguity/test.cpp @@ -16,7 +16,7 @@ using namespace std; -// Dev10#766948 "STL: insert() ambiguity in all associative containers except map and set" +// Dev10-766948 "STL: insert() ambiguity in all associative containers except map and set" // LWG-2005 "unordered_map::insert(T&&) protection should apply to map too" // LWG-2354 "Unnecessary copying when inserting into maps with braced-init syntax" diff --git a/tests/std/tests/Dev10_851347_weak_ptr_virtual_inheritance/test.cpp b/tests/std/tests/Dev10_851347_weak_ptr_virtual_inheritance/test.cpp index 68863dd8e7d..c46ef9309f7 100644 --- a/tests/std/tests/Dev10_851347_weak_ptr_virtual_inheritance/test.cpp +++ b/tests/std/tests/Dev10_851347_weak_ptr_virtual_inheritance/test.cpp @@ -4,48 +4,109 @@ #include #include #include - using namespace std; +// Also test GH-1102 ": weak_ptr conversions don't preserve control blocks for expired objects" +template +[[nodiscard]] bool owner_equal(const weak_ptr& t, const weak_ptr& u) { + return !t.owner_before(u) && !u.owner_before(t); +} + +void test_owner_equal() { + shared_ptr sp_alive1(new int(0)); + shared_ptr sp_alive2(new int(0)); + shared_ptr sp_expiring3(new int(0)); + shared_ptr sp_expiring4(new int(0)); + + weak_ptr wp_empty; + weak_ptr wp_also_empty; + + weak_ptr wp_alive(sp_alive1); + weak_ptr wp_alive_same(sp_alive1); + weak_ptr wp_alive_different(sp_alive2); + + weak_ptr wp_expired(sp_expiring3); + weak_ptr wp_expired_same(sp_expiring3); + weak_ptr wp_expired_different(sp_expiring4); + + sp_expiring3.reset(); + sp_expiring4.reset(); + + assert(wp_empty.expired()); + assert(wp_also_empty.expired()); + + assert(!wp_alive.expired()); + assert(!wp_alive_same.expired()); + assert(!wp_alive_different.expired()); + + assert(wp_expired.expired()); + assert(wp_expired_same.expired()); + assert(wp_expired_different.expired()); + + assert(owner_equal(wp_empty, wp_also_empty)); + + assert(!owner_equal(wp_empty, wp_alive)); + assert(!owner_equal(wp_empty, wp_expired)); + + assert(!owner_equal(wp_alive, wp_empty)); + assert(!owner_equal(wp_expired, wp_empty)); + + assert(owner_equal(wp_alive, wp_alive_same)); + assert(owner_equal(wp_expired, wp_expired_same)); + + assert(!owner_equal(wp_alive, wp_alive_different)); + assert(!owner_equal(wp_alive, wp_expired)); + assert(!owner_equal(wp_expired, wp_alive)); + assert(!owner_equal(wp_expired, wp_expired_different)); +} + struct A { - int a; + int a{10}; }; -struct B : virtual public A { - int b; +struct B : virtual A { + int b{20}; }; -struct C : virtual public A { - int c; +struct C : virtual A { + int c{30}; }; -struct D : public B, public C { - int d; +struct D : B, C { + int d{40}; }; int main() { + test_owner_equal(); + shared_ptr spd(new D); - weak_ptr wpd(spd); - weak_ptr wpd2(spd); + const weak_ptr wpd_zero(spd); + weak_ptr wpd_one(spd); + weak_ptr wpd_two(spd); - spd.reset(); + weak_ptr wpa0(wpd_zero); + assert(!wpa0.expired()); + assert(owner_equal(wpa0, wpd_zero)); + assert(wpa0.lock()->a == 10); - weak_ptr wpa1(wpd); + spd.reset(); + weak_ptr wpa1(wpd_one); assert(wpa1.expired()); + assert(owner_equal(wpa1, wpd_zero)); weak_ptr wpa2; - - wpa2 = wpd; - + wpa2 = wpd_one; assert(wpa2.expired()); + assert(owner_equal(wpa2, wpd_zero)); - - weak_ptr wpa3(move(wpd)); + weak_ptr wpa3(move(wpd_one)); assert(wpa3.expired()); + assert(owner_equal(wpa3, wpd_zero)); weak_ptr wpa4; - wpa4 = move(wpd2); + wpa4 = move(wpd_two); assert(wpa4.expired()); + assert(owner_equal(wpa4, wpd_zero)); } diff --git a/tests/std/tests/Dev10_860410_bitset_ctors/test.cpp b/tests/std/tests/Dev10_860410_bitset_ctors/test.cpp index 62df05c9208..b6516c7fadc 100644 --- a/tests/std/tests/Dev10_860410_bitset_ctors/test.cpp +++ b/tests/std/tests/Dev10_860410_bitset_ctors/test.cpp @@ -140,7 +140,7 @@ int main() { test_Getword(); } -// Also test DevDiv#917456 ": none() and any() return incorrect count after call set() function in a +// Also test DevDiv-917456 ": none() and any() return incorrect count after call set() function in a // std::bitset<0> object [libcxx]". void test_bitset0(const bitset<0>& b) { assert(b.to_ulong() == 0); @@ -182,7 +182,7 @@ void test_DevDiv917456() { } } -// Also test DevDiv#931383 ": We need to validate all characters and use traits::eq()". +// Also test DevDiv-931383 ": We need to validate all characters and use traits::eq()". void test(const string& str, const size_t pos, const size_t n, const string& expected) noexcept { try { bitset<8> b(str, pos, n, 'o', 'i'); @@ -215,7 +215,7 @@ void test_DevDiv931383() { test("FFiioiiiGG", 2, 6, "00110111"); } -// Also test Dev10#479284 "C6326 when running static analysis with ". +// Also test Dev10-479284 "C6326 when running static analysis with ". template class std::bitset<7>; template class std::bitset<32>; diff --git a/tests/std/tests/Dev10_860421_deque_push_back_pop_front/test.cpp b/tests/std/tests/Dev10_860421_deque_push_back_pop_front/test.cpp index c19bdddfed6..c1857bde5ea 100644 --- a/tests/std/tests/Dev10_860421_deque_push_back_pop_front/test.cpp +++ b/tests/std/tests/Dev10_860421_deque_push_back_pop_front/test.cpp @@ -35,7 +35,7 @@ int main() { test_391805(); } -// Also test Dev10#391805 "STL: Prefast error in deque". +// Also test Dev10-391805 "STL: Prefast error in deque". void test_391805() { deque d; diff --git a/tests/std/tests/Dev10_908702_string_memory_leak/test.cpp b/tests/std/tests/Dev10_908702_string_memory_leak/test.cpp index a6e12c9e5ec..65ef7e531f7 100644 --- a/tests/std/tests/Dev10_908702_string_memory_leak/test.cpp +++ b/tests/std/tests/Dev10_908702_string_memory_leak/test.cpp @@ -36,7 +36,7 @@ int main() { assert(!_CrtDumpMemoryLeaks()); #endif - // Also test DevDiv#846054 ": Spurious memory leaks". + // Also test DevDiv-846054 ": Spurious memory leaks". locale::global(locale("")); #ifdef _DEBUG @@ -44,7 +44,7 @@ int main() { #endif } -// Also test DevDiv#810608 ": [torino][boost]error C2665: +// Also test DevDiv-810608 ": [torino][boost]error C2665: // 'std::_Crt_new_delete::operator new' : none of the 2 overloads could convert all the argument types". void meow(void* pv) { // Saying "new" instead of "::new" is intentional here. diff --git a/tests/std/tests/Dev11_0000000_quoted/test.cpp b/tests/std/tests/Dev11_0000000_quoted/test.cpp index cdc1102d33d..b2f0e43c8a0 100644 --- a/tests/std/tests/Dev11_0000000_quoted/test.cpp +++ b/tests/std/tests/Dev11_0000000_quoted/test.cpp @@ -169,12 +169,12 @@ int main() { // Also test VSO-666592 "std::quoted fails to properly extract delimiter" { - const std::string expectedStrings[] = { + const string expectedStrings[] = { "spaces"s, "with"s, "some quote"s, "and another quote"s, "then"s, "more"s, "spaces"s}; - std::istringstream iss13(R"(spaces with "some quote" "and another quote" then more spaces)"); - std::string out; + istringstream iss13(R"(spaces with "some quote" "and another quote" then more spaces)"); + string out; for (const auto& expected : expectedStrings) { - iss13 >> std::quoted(out); + iss13 >> quoted(out); assert(!iss13.fail()); assert(out == expected); } diff --git a/tests/std/tests/Dev11_0000000_tuple_cat/test.cpp b/tests/std/tests/Dev11_0000000_tuple_cat/test.cpp index 0dee5434c76..ff2a03fee36 100644 --- a/tests/std/tests/Dev11_0000000_tuple_cat/test.cpp +++ b/tests/std/tests/Dev11_0000000_tuple_cat/test.cpp @@ -211,7 +211,7 @@ int main() { } } -// Also test DevDiv#1205400 "C++ compiler: static_assert in std::tuple_element prevents SFINAE". +// Also test DevDiv-1205400 "C++ compiler: static_assert in std::tuple_element prevents SFINAE". template struct HasTupleElement : false_type {}; @@ -221,7 +221,7 @@ struct HasTupleElement>> : true_type {}; STATIC_ASSERT(!HasTupleElement::value); STATIC_ASSERT(HasTupleElement>::value); -// Also test DevDiv#1192603 ": tuple_size's static_assert is problematic". +// Also test DevDiv-1192603 ": tuple_size's static_assert is problematic". template struct HasTupleSize : false_type {}; diff --git a/tests/std/tests/Dev11_0135139_vector_bool_comparisons/test.cpp b/tests/std/tests/Dev11_0135139_vector_bool_comparisons/test.cpp index 9a17e51d2b1..43dfd67165b 100644 --- a/tests/std/tests/Dev11_0135139_vector_bool_comparisons/test.cpp +++ b/tests/std/tests/Dev11_0135139_vector_bool_comparisons/test.cpp @@ -107,7 +107,7 @@ int main() { } { - // Also test DevDiv#850453 ": Missing emplace methods in std::vector container". + // Also test DevDiv-850453 ": Missing emplace methods in std::vector container". vector v(47, allocator()); diff --git a/tests/std/tests/Dev11_0235721_async_and_packaged_task/test.cpp b/tests/std/tests/Dev11_0235721_async_and_packaged_task/test.cpp index b206a423ee3..69a285092b6 100644 --- a/tests/std/tests/Dev11_0235721_async_and_packaged_task/test.cpp +++ b/tests/std/tests/Dev11_0235721_async_and_packaged_task/test.cpp @@ -18,7 +18,7 @@ using namespace std::placeholders; #define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) -// DevDiv#235721 ": async() and packaged_task don't compile for void and T& return types" +// DevDiv-235721 ": async() and packaged_task don't compile for void and T& return types" void test_DevDiv_235721() { auto void_lambda = []() {}; @@ -52,7 +52,7 @@ void test_DevDiv_235721() { } -// DevDiv#586551 ": future_errc message() and what() don't work" +// DevDiv-586551 ": future_errc message() and what() don't work" void test_message(const future_errc fe, const string& s) { assert(make_error_code(fe).message() == s); @@ -70,7 +70,7 @@ void test_DevDiv_586551() { } -// DevDiv#725337 ": std::packaged_task where T is void or a reference class are not movable" +// DevDiv-725337 ": std::packaged_task where T is void or a reference class are not movable" void test_DevDiv_725337() { auto void_lambda = []() {}; diff --git a/tests/std/tests/Dev11_0253803_debug_pointer/test.cpp b/tests/std/tests/Dev11_0253803_debug_pointer/test.cpp index 034ab82dfb0..637cd1f97dd 100644 --- a/tests/std/tests/Dev11_0253803_debug_pointer/test.cpp +++ b/tests/std/tests/Dev11_0253803_debug_pointer/test.cpp @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// DevDiv#253803 ": merge() asserts when given null src/dest" +// DevDiv-253803 ": merge() asserts when given null src/dest" #include #include diff --git a/tests/std/tests/Dev11_0272959_make_signed/test.cpp b/tests/std/tests/Dev11_0272959_make_signed/test.cpp index 2bd4944d08e..eff551f993c 100644 --- a/tests/std/tests/Dev11_0272959_make_signed/test.cpp +++ b/tests/std/tests/Dev11_0272959_make_signed/test.cpp @@ -156,7 +156,7 @@ void example() { STATIC_ASSERT(!is_trivial_v); - // DevDiv#517460 "is_*_constructible type traits are broken for reference types" + // DevDiv-517460 "is_*_constructible type traits are broken for reference types" STATIC_ASSERT(is_copy_constructible_v); STATIC_ASSERT(is_copy_constructible_v); STATIC_ASSERT(is_move_constructible_v); diff --git a/tests/std/tests/Dev11_0299014_exception_ptr_requirements/test.cpp b/tests/std/tests/Dev11_0299014_exception_ptr_requirements/test.cpp index d13b7b90f31..e5d7ba58b18 100644 --- a/tests/std/tests/Dev11_0299014_exception_ptr_requirements/test.cpp +++ b/tests/std/tests/Dev11_0299014_exception_ptr_requirements/test.cpp @@ -170,7 +170,7 @@ int main() { } { - // Also test DevDiv#1210471 "std::rethrow_exception is not [[noreturn]]". + // Also test DevDiv-1210471 "std::rethrow_exception is not [[noreturn]]". auto lambda = []() -> double { rethrow_exception(make_exception_ptr(1729)); }; 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 6e6475be2f9..e621519332d 100644 --- a/tests/std/tests/Dev11_0316853_find_memchr_optimization/test.cpp +++ b/tests/std/tests/Dev11_0316853_find_memchr_optimization/test.cpp @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// DevDiv#316853 ": find()'s memchr() optimization is incorrect" -// DevDiv#468500 ": find()'s memchr() optimization is insufficiently aggressive" +// DevDiv-316853 ": find()'s memchr() optimization is incorrect" +// DevDiv-468500 ": find()'s memchr() optimization is insufficiently aggressive" #pragma warning(disable : 4389) // signed/unsigned mismatch #pragma warning(disable : 4805) // '==': unsafe mix of type '_Ty' and type 'const _Ty' in operation @@ -30,7 +30,7 @@ bool operator==(int x, const Cat& c) { } int main() { - { // DevDiv#316853 ": find()'s memchr() optimization is incorrect" + { // DevDiv-316853 ": find()'s memchr() optimization is incorrect" vector v; v.push_back(22); v.push_back(33); diff --git a/tests/std/tests/Dev11_0377755_thread_ctor_move_only_types/test.cpp b/tests/std/tests/Dev11_0377755_thread_ctor_move_only_types/test.cpp index e4e08f40504..d1c0d64c9f7 100644 --- a/tests/std/tests/Dev11_0377755_thread_ctor_move_only_types/test.cpp +++ b/tests/std/tests/Dev11_0377755_thread_ctor_move_only_types/test.cpp @@ -30,7 +30,7 @@ class A { int i; }; -int main() { // DevDiv#377755 ": thread's ctor doesn't compile with movable-only arguments" +int main() { // DevDiv-377755 ": thread's ctor doesn't compile with movable-only arguments" std::vector t; std::unique_ptr p = std::make_unique(-1); // Check if std::thread ctor accepts move-only arguments. diff --git a/tests/std/tests/Dev11_0417110_nullptr_t_is_scalar/test.cpp b/tests/std/tests/Dev11_0417110_nullptr_t_is_scalar/test.cpp index 97452de10e5..21972eaac86 100644 --- a/tests/std/tests/Dev11_0417110_nullptr_t_is_scalar/test.cpp +++ b/tests/std/tests/Dev11_0417110_nullptr_t_is_scalar/test.cpp @@ -6,7 +6,7 @@ #define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) // Regression test for: -// DevDiv2 #417110: is_scalar should be true +// DevDiv-417110: is_scalar should be true int main() { diff --git a/tests/std/tests/Dev11_0453373_codecvt_compiles/test.cpp b/tests/std/tests/Dev11_0453373_codecvt_compiles/test.cpp index cd66697b19a..892cec1301a 100644 --- a/tests/std/tests/Dev11_0453373_codecvt_compiles/test.cpp +++ b/tests/std/tests/Dev11_0453373_codecvt_compiles/test.cpp @@ -11,7 +11,7 @@ int main() {} // COMPILE-ONLY -// Regression test for DevDiv2 #453373 : codecvt_one_one compile errors +// Regression test for DevDiv-453373 : codecvt_one_one compile errors // Make sure we can instantiate the types from the problem headers: template class stdext::cvt::codecvt_one_one; diff --git a/tests/std/tests/Dev11_0483851_vector_debug_allocator_use/test.cpp b/tests/std/tests/Dev11_0483851_vector_debug_allocator_use/test.cpp index 41f62677079..a323504ec10 100644 --- a/tests/std/tests/Dev11_0483851_vector_debug_allocator_use/test.cpp +++ b/tests/std/tests/Dev11_0483851_vector_debug_allocator_use/test.cpp @@ -13,7 +13,7 @@ int main() {} // COMPILE-ONLY -// Regression test for DevDiv2 #483851 : [C++11] STL containers must use std::allocator_trait in debug mode +// Regression test for DevDiv-483851 : [C++11] STL containers must use std::allocator_traits in debug mode template struct simple_allocator : Base { diff --git a/tests/std/tests/Dev11_0485243_condition_variable_crash/test.cpp b/tests/std/tests/Dev11_0485243_condition_variable_crash/test.cpp index 41ac758338a..465c1b4c166 100644 --- a/tests/std/tests/Dev11_0485243_condition_variable_crash/test.cpp +++ b/tests/std/tests/Dev11_0485243_condition_variable_crash/test.cpp @@ -27,7 +27,7 @@ void assert_no_leaks() { void test_484720(); int main() { - // DevDiv#452211 ": init_at_thread_exit_mutex() creates a spurious memory leak" + // DevDiv-452211 ": init_at_thread_exit_mutex() creates a spurious memory leak" assert_no_leaks(); { @@ -54,7 +54,7 @@ int main() { assert_no_leaks(); { - // DevDiv#485243 "Crash in runtime library (msvcr110.dll)" + // DevDiv-485243 "Crash in runtime library (msvcr110.dll)" condition_variable cv; mutex m; @@ -85,7 +85,7 @@ int main() { assert_no_leaks(); { - // DevDiv#861298 "std::thread not fully initialized due to _Thr_set_null only setting id (not handle)" + // DevDiv-861298 "std::thread not fully initialized due to _Thr_set_null only setting id (not handle)" // native_handle()'s behavior is unspecified, but CreateThread() and _beginthreadex() // return null for failure, so this seems like a reasonable default. @@ -99,7 +99,7 @@ int main() { } -// DevDiv#484720 ": [c++std-lib-32966] Public service announcement concerning +// DevDiv-484720 ": [c++std-lib-32966] Public service announcement concerning // ~condition_variable_any()" template diff --git a/tests/std/tests/Dev11_0493504_error_category_lifetime/test.cpp b/tests/std/tests/Dev11_0493504_error_category_lifetime/test.cpp index 10891e785b1..99712972256 100644 --- a/tests/std/tests/Dev11_0493504_error_category_lifetime/test.cpp +++ b/tests/std/tests/Dev11_0493504_error_category_lifetime/test.cpp @@ -29,7 +29,7 @@ struct Global { Global global; int main() { - // Also test DevDiv#781294 ": Visual C++ 2013 RC system_category().equivalent function does not work". + // Also test DevDiv-781294 ": Visual C++ 2013 RC system_category().equivalent function does not work". const error_code code(ERROR_NOT_ENOUGH_MEMORY, system_category()); const error_condition cond = make_error_condition(errc::not_enough_memory); @@ -47,7 +47,7 @@ int main() { } -// Also test DevDiv#833886 ": comparisons should be free functions". +// Also test DevDiv-833886 ": comparisons should be free functions". bool test_code(const io_errc l, const error_code& r) { return l == r && l != r && l < r; } diff --git a/tests/std/tests/Dev11_0535636_functional_overhaul/test.cpp b/tests/std/tests/Dev11_0535636_functional_overhaul/test.cpp index 753569259ff..4369fb7fb11 100644 --- a/tests/std/tests/Dev11_0535636_functional_overhaul/test.cpp +++ b/tests/std/tests/Dev11_0535636_functional_overhaul/test.cpp @@ -685,7 +685,7 @@ STATIC_ASSERT(is_same_v, long>); STATIC_ASSERT(is_same_v, short>); STATIC_ASSERT(is_same_v, long>); -// Also test references to functions, DDB#198033. +// Also test references to functions, DDB-198033. using FuncRef = int (&)(float, double); STATIC_ASSERT(is_same_v, int>); @@ -724,7 +724,7 @@ int cube_noexcept(int n) noexcept { } -// Test DevDiv#391117 " reference_wrapper: reference_wrapper doesn't compile with pure virtual function call +// Test DevDiv-391117 " reference_wrapper: reference_wrapper doesn't compile with pure virtual function call // operators". struct BaseMeow { BaseMeow() {} @@ -751,7 +751,7 @@ void test_dev11_391117() { } -// Test DevDiv#535636 " reference_wrapper: reference_wrapper::get() doesn't compile". +// Test DevDiv-535636 " reference_wrapper: reference_wrapper::get() doesn't compile". void test_dev11_535636() { reference_wrapper rw(triple); @@ -767,7 +767,7 @@ void test_dev11_535636() { } -// Test DevDiv#794227 " reference_wrapper: ambiguous access of result_type - functional, xrefwrap". +// Test DevDiv-794227 " reference_wrapper: ambiguous access of result_type - functional, xrefwrap". template struct UnaryFunction { typedef Arg argument_type; @@ -806,7 +806,7 @@ void test_dev11_794227() { } -// Test DevDiv#868374 " reference_wrapper: Cannot assign a std::reference_wrapper object to another +// Test DevDiv-868374 " reference_wrapper: Cannot assign a std::reference_wrapper object to another // std::reference_wrapper object [libcxx]". void test_dev11_868374() { reference_wrapper rw(triple); @@ -927,7 +927,7 @@ struct Thing { class UnaryBinary { public: - // Originally for testing Dev10#539137 + // Originally for testing Dev10-539137 // "reference_wrapper: Doesn't handle classes that derive from both unary_function and binary_function". // The typedefs are tested elsewhere here (see SameResults and DifferentResults). @@ -1285,7 +1285,7 @@ STATIC_ASSERT(TestRWTypes::value); STATIC_ASSERT(TestRWTypes::value); STATIC_ASSERT(TestRWTypes::value); -// Test DevDiv#864867 " reference_wrapper: reference_wrapper should handle functors that are both unary and +// Test DevDiv-864867 " reference_wrapper: reference_wrapper should handle functors that are both unary and // binary [libs-conformance]". struct SameResults : UnaryFunction, BinaryFunction { @@ -1465,7 +1465,7 @@ void test_function() { // std::functions. - // Test DevDiv#759096 " function: std::function construction copies its target instead of moving". + // Test DevDiv-759096 " function: std::function construction copies its target instead of moving". { CopyMoveCounter<1> cmc0; CopyMoveCounter<1> cmc1(cmc0); @@ -1663,7 +1663,7 @@ void test_function() { } - // Test DevDiv#1010027 " function: std::function with return type void does not ignore return type on + // Test DevDiv-1010027 " function: std::function with return type void does not ignore return type on // assignment". { string s("ChooseAMovieTitle"); @@ -1712,8 +1712,8 @@ void test_function() { } - // Test DevDiv#294051 " function: std::function has lost the ability to invoke PMFs/PMDs on various - // things". Test DevDiv#789899 " function: std::function does not work for member functions". + // Test DevDiv-294051 " function: std::function has lost the ability to invoke PMFs/PMDs on various + // things". Test DevDiv-789899 " function: std::function does not work for member functions". { struct Y { int m_n; @@ -1862,7 +1862,7 @@ void test_function() { // Test bind(), user-reported bugs. void test_bind() { - // Test DDB#176058 "TR1: result_of doesn't accept result_type typedefs for references" (title is now bogus). + // Test DDB-176058 "TR1: result_of doesn't accept result_type typedefs for references" (title is now bogus). { struct PassThru { int& operator()(int& obj) const { @@ -1880,9 +1880,9 @@ void test_bind() { } - // Test DevDiv#343411 " bind: bind() and std::function don't work with rvalue references". - // Test DevDiv#410033 ": bind() doesn't work with rvalue reference signatures". - // Test DevDiv#862588 " bind: std::bind doesn't forward unbound arguments". + // Test DevDiv-343411 " bind: bind() and std::function don't work with rvalue references". + // Test DevDiv-410033 ": bind() doesn't work with rvalue reference signatures". + // Test DevDiv-862588 " bind: std::bind doesn't forward unbound arguments". { #ifndef _M_CEE_PURE @@ -1921,8 +1921,8 @@ void test_bind() { } -// Test DevDiv#487679 " bind: MSVS 2012 C++ std::bind illegal indirection compiler error". -// Test DevDiv#617421 " bind: Bind failing to compile with a vector of functions". +// Test DevDiv-487679 " bind: MSVS 2012 C++ std::bind illegal indirection compiler error". +// Test DevDiv-617421 " bind: Bind failing to compile with a vector of functions". { struct BaseFunctor { int operator()(int n) const { @@ -1940,7 +1940,7 @@ void test_bind() { } -// Test DevDiv#505570 " bind: Can't bind a pointer to a data member using a pointer, smart pointer or +// Test DevDiv-505570 " bind: Can't bind a pointer to a data member using a pointer, smart pointer or // iterator to the object". { struct Object { @@ -1964,7 +1964,7 @@ void test_bind() { } -// Test DevDiv#535246 " bind: Cannot call const forwarding call wrapper result of std::bind". +// Test DevDiv-535246 " bind: Cannot call const forwarding call wrapper result of std::bind". { const auto cb = bind(&quadruple, 11); @@ -2213,7 +2213,7 @@ _CONSTEXPR20 bool test_more_bind() { } -// Test DevDiv#1160769 ": bind()'s cv-overloaded function call operators are triggering Expression SFINAE +// Test DevDiv-1160769 ": bind()'s cv-overloaded function call operators are triggering Expression SFINAE // problems". struct Test1160769 { void method(const int&) {} diff --git a/tests/std/tests/Dev11_0696045_future_wait_for/test.cpp b/tests/std/tests/Dev11_0696045_future_wait_for/test.cpp index 8309c259829..d8a4987cb98 100644 --- a/tests/std/tests/Dev11_0696045_future_wait_for/test.cpp +++ b/tests/std/tests/Dev11_0696045_future_wait_for/test.cpp @@ -12,7 +12,7 @@ int func() { } int main() { - { // DevDiv#482796 "C++11 unexpected behavior for std::future::wait_for and std::packaged_task" + { // DevDiv-482796 "C++11 unexpected behavior for std::future::wait_for and std::packaged_task" packaged_task pt(func); future f = pt.get_future(); @@ -26,7 +26,7 @@ int main() { assert(f.get() == 1729); } - { // DevDiv#696045 ": The function wait_for() wait until timeout." + { // DevDiv-696045 ": The function wait_for() wait until timeout." future f = async(launch::deferred, func); const auto dur = chrono::minutes(5); diff --git a/tests/std/tests/Dev11_0835323_to_string/test.cpp b/tests/std/tests/Dev11_0835323_to_string/test.cpp index e3bec9d0513..cf9c341bc8d 100644 --- a/tests/std/tests/Dev11_0835323_to_string/test.cpp +++ b/tests/std/tests/Dev11_0835323_to_string/test.cpp @@ -20,9 +20,9 @@ void assert_out_of_range(F f) noexcept { } } -// DevDiv#96290 "[Product Issue] to_string doesn't work with the param LDBL_MAX" -// DevDiv#730419 ": std::to_string documentation & code disparity" -// DevDiv#835323 ": Bad to_string() result for infinity" +// DevDiv-96290 "[Product Issue] to_string doesn't work with the param LDBL_MAX" +// DevDiv-730419 ": std::to_string documentation & code disparity" +// DevDiv-835323 ": Bad to_string() result for infinity" int main() { assert(to_string(numeric_limits::min()) == "-2147483648"); @@ -145,7 +145,7 @@ int main() { assert(to_wstring(numeric_limits::infinity()) == L"inf"); - // Also test DevDiv#875295 ": std::stof returns 1.#INF instead of throwing out_of_range [libcxx]". + // Also test DevDiv-875295 ": std::stof returns 1.#INF instead of throwing out_of_range [libcxx]". assert_out_of_range([] { stof("1.2e60"); }); @@ -166,7 +166,7 @@ int main() { assert_out_of_range([] { stof(L"-1.5e63"); }); - // Also test DevDiv#1113936 "std::stod incorrectly throws exception on some inputs, violating STL specification". + // Also test DevDiv-1113936 "std::stod incorrectly throws exception on some inputs, violating STL specification". for (const char* const p : {"inf", "Inf", "INF", "infinity", "Infinity", "INFINITY"}) { assert(isinf(stof(p))); diff --git a/tests/std/tests/Dev11_0836436_get_time/test.cpp b/tests/std/tests/Dev11_0836436_get_time/test.cpp index aacf42f84b9..78c69d18074 100644 --- a/tests/std/tests/Dev11_0836436_get_time/test.cpp +++ b/tests/std/tests/Dev11_0836436_get_time/test.cpp @@ -13,9 +13,9 @@ using namespace std; -// DevDiv#821672 ": visual studio.net 2013 time libraries buggy (%x %X) - time_get" -// DevDiv#836436 ": get_time()'s AM/PM parsing is broken" -// DevDiv#872926 ": time_get::get parsing format string gets tm::tm_hour wrong [libcxx]" +// DevDiv-821672 ": visual studio.net 2013 time libraries buggy (%x %X) - time_get" +// DevDiv-836436 ": get_time()'s AM/PM parsing is broken" +// DevDiv-872926 ": time_get::get parsing format string gets tm::tm_hour wrong [libcxx]" tm helper(const char* const s, const char* const fmt) { tm t{}; @@ -117,7 +117,7 @@ int main() { typedef istreambuf_iterator Iter; -// DevDiv#640278 ": time_get::do_get_year() thinks the world will end in 2035" +// DevDiv-640278 ": time_get::do_get_year() thinks the world will end in 2035" void test_year(const string& str, const ios_base::iostate expected_err, const int expected_tm_year) { istringstream iss(str); ios_base::iostate err = ios_base::goodbit; @@ -152,7 +152,7 @@ void test_640278() { test_year("2013 frozen", ios_base::goodbit, 113); } -// DevDiv#990695 ": time_get should ignore ios_base::iostate's initial value" +// DevDiv-990695 ": time_get should ignore ios_base::iostate's initial value" void test_990695() { for (int k = 0; k < 2; ++k) { const auto Bit = k == 0 ? ios_base::goodbit : ios_base::failbit; diff --git a/tests/std/tests/Dev11_0845312_comprehensive_floating_point/test.cpp b/tests/std/tests/Dev11_0845312_comprehensive_floating_point/test.cpp index 28c4a31b82c..ee421c1135e 100644 --- a/tests/std/tests/Dev11_0845312_comprehensive_floating_point/test.cpp +++ b/tests/std/tests/Dev11_0845312_comprehensive_floating_point/test.cpp @@ -3,7 +3,7 @@ // Derived from: qa/VC/LibsWin/devcrt/tests/C/Dev14_845312_accurate_fp_parsing -// Basic regression test for DevDiv2 #845312: Floating point conversion accuracy +// Basic regression test for DevDiv-845312: Floating point conversion accuracy // improvements. This test verifies scanf-printf round-tripping of a set of // diverse floating point values (both single and double precision). diff --git a/tests/std/tests/Dev11_0863628_atomic_compare_exchange/test.cpp b/tests/std/tests/Dev11_0863628_atomic_compare_exchange/test.cpp index ceca79baf66..6e9a163345d 100644 --- a/tests/std/tests/Dev11_0863628_atomic_compare_exchange/test.cpp +++ b/tests/std/tests/Dev11_0863628_atomic_compare_exchange/test.cpp @@ -83,11 +83,11 @@ void test_nullptr_compares() { } // Also test: -// DevDiv#826403 ": passing volatile atomic to store won't compile [libs-conformance]" -// DevDiv#829873 ": Error when using atomic pointer to const" -// DevDiv#846428 ": std::atomic::store with volatile specifier does not work for non-integral type" -// DevDiv#879700 ": atomic constructor missing cast?" -// DevDiv#1181758 ": MSVC 2015 std::atomic is implemented using non-conforming C++" +// DevDiv-826403 ": passing volatile atomic to store won't compile [libs-conformance]" +// DevDiv-829873 ": Error when using atomic pointer to const" +// DevDiv-846428 ": std::atomic::store with volatile specifier does not work for non-integral type" +// DevDiv-879700 ": atomic constructor missing cast?" +// DevDiv-1181758 ": MSVC 2015 std::atomic is implemented using non-conforming C++" // This is compile-only. template diff --git a/tests/std/tests/Dev11_0920385_list_sort_allocator/test.cpp b/tests/std/tests/Dev11_0920385_list_sort_allocator/test.cpp index bc329164081..aa38f7b4c6a 100644 --- a/tests/std/tests/Dev11_0920385_list_sort_allocator/test.cpp +++ b/tests/std/tests/Dev11_0920385_list_sort_allocator/test.cpp @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// Test DevDiv#920385 ": list::sort shouldn't default-construct allocators". +// Test DevDiv-920385 ": list::sort shouldn't default-construct allocators". #define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING @@ -284,7 +284,7 @@ int main() { test_allocator_construct_const(); } -// Also test DevDiv#1119194 "The STL should handle allocators that aren't assignable". +// Also test DevDiv-1119194 "The STL should handle allocators that aren't assignable". template struct NoProp { @@ -353,7 +353,7 @@ void test_1119194() { test_swap_copy_move, AC>>(); } -// Test DevDiv#1184701 ": We should handle construct/destroy returning non-void". +// Test DevDiv-1184701 ": We should handle construct/destroy returning non-void". template struct NonVoid { diff --git a/tests/std/tests/Dev11_1086953_call_once_overhaul/test.cpp b/tests/std/tests/Dev11_1086953_call_once_overhaul/test.cpp index 6d2b9ad43f7..27338bff59b 100644 --- a/tests/std/tests/Dev11_1086953_call_once_overhaul/test.cpp +++ b/tests/std/tests/Dev11_1086953_call_once_overhaul/test.cpp @@ -111,7 +111,7 @@ void run_tests() { VERIFY(i2 == 5); } - // Test DevDiv#1086953 "Throwing exception from std::call_once does not allow other threads to enter". + // Test DevDiv-1086953 "Throwing exception from std::call_once does not allow other threads to enter". // EH, single-threaded. { once_flag flag; @@ -149,7 +149,7 @@ void run_tests() { } } - // Test DevDiv#1086953 "Throwing exception from std::call_once does not allow other threads to enter". + // Test DevDiv-1086953 "Throwing exception from std::call_once does not allow other threads to enter". // EH, multi-threaded. { long ready = 0; @@ -188,7 +188,7 @@ void run_tests() { VERIFY(i == 3); } - // Test DevDiv#1092852 "concurrent std::call_once calls seem to be blocking somewhere on a shared variable". + // Test DevDiv-1092852 "concurrent std::call_once calls seem to be blocking somewhere on a shared variable". // Also test stateful callable objects. { long atom = 0; diff --git a/tests/std/tests/Dev11_1114006_condition_variable_pred/test.cpp b/tests/std/tests/Dev11_1114006_condition_variable_pred/test.cpp index 52e12cb279c..0af65eccf50 100644 --- a/tests/std/tests/Dev11_1114006_condition_variable_pred/test.cpp +++ b/tests/std/tests/Dev11_1114006_condition_variable_pred/test.cpp @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// Test DevDiv#1114006 "[+ VS2015] conditional_variable with predicate does not behave according to the standard". +// Test DevDiv-1114006 "[+ VS2015] conditional_variable with predicate does not behave according to the standard". #include #include diff --git a/tests/std/tests/Dev11_1131212_uncaught_exceptions/test.cpp b/tests/std/tests/Dev11_1131212_uncaught_exceptions/test.cpp index 6cbad4be454..1c5ad910d27 100644 --- a/tests/std/tests/Dev11_1131212_uncaught_exceptions/test.cpp +++ b/tests/std/tests/Dev11_1131212_uncaught_exceptions/test.cpp @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// DevDiv2 #1131212: std::uncaught_exceptions is not implemented +// DevDiv-1131212: std::uncaught_exceptions is not implemented // // This test validates the implementation of std::uncaught_exceptions by recursively // throwing exceptions from a destructor. diff --git a/tests/std/tests/GH_000625_vector_bool_optimization/env.lst b/tests/std/tests/GH_000625_vector_bool_optimization/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_000625_vector_bool_optimization/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp b/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp new file mode 100644 index 00000000000..3f26088c87b --- /dev/null +++ b/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include + +using namespace std; + +constexpr int blockSize = 32; +static_assert(blockSize == _VBITS, "Invalid block size"); + +void test_fill_helper(const size_t length) { + // No offset + { + vector result_true(length, true); + result_true.resize(length + 3, false); + vector dest_true(length + 3, false); + fill(dest_true.begin(), prev(dest_true.end(), 3), true); + assert(dest_true == result_true); + + vector result_false(length, false); + result_false.resize(length + 3, true); + vector dest_false(length + 3, true); + fill(dest_false.begin(), prev(dest_false.end(), 3), false); + assert(dest_false == result_false); + + vector result_true_n(length, true); + result_true_n.resize(length + 3, false); + vector dest_true_n(length + 3, false); + const auto res_fill_n = fill_n(dest_true_n.begin(), length, true); + assert(dest_true_n == result_true_n); + assert(res_fill_n == prev(dest_true_n.end(), 3)); + + vector result_false_n(length, false); + result_false_n.resize(length + 3, true); + vector dest_false_n(length + 3, true); + fill_n(dest_false_n.begin(), length, false); + assert(dest_false_n == result_false_n); + } + + // With offset + { + vector result_true(length, true); + result_true.resize(length + 3, false); + result_true.insert(result_true.begin(), false); + vector dest_true(length + 4, false); + fill(next(dest_true.begin()), prev(dest_true.end(), 3), true); + assert(dest_true == result_true); + + vector result_false(length, false); + result_false.resize(length + 3, true); + result_false.insert(result_false.begin(), true); + vector dest_false(length + 4, true); + fill(next(dest_false.begin()), prev(dest_false.end(), 3), false); + assert(dest_false == result_false); + + vector result_true_n(length, true); + result_true_n.resize(length + 3, false); + result_true_n.insert(result_true_n.begin(), false); + vector dest_true_n(length + 4, false); + const auto res_fill_n = fill_n(next(dest_true_n.begin()), length, true); + assert(dest_true_n == result_true_n); + assert(res_fill_n == prev(dest_true_n.end(), 3)); + + vector result_false_n(length, false); + result_false_n.resize(length + 3, true); + result_false_n.insert(result_false_n.begin(), true); + vector dest_false_n(length + 4, true); + fill_n(next(dest_false_n.begin()), length, false); + assert(dest_false_n == result_false_n); + } +} + +bool test_fill() { + // Empty + test_fill_helper(0); + + // One block, ends within block + test_fill_helper(15); + + // One block, ends at block boundary + test_fill_helper(blockSize); + + // Multiple blocks, no memset, within block + test_fill_helper(blockSize + 11); + + // Multiple blocks, no memset, ends at block boundary + test_fill_helper(2 * blockSize); + + // Multiple blocks, with memset, within block + test_fill_helper(3 * blockSize + 5); + + // Multiple blocks, with memset, ends at block boundary + test_fill_helper(4 * blockSize); + return true; +} + +int main() { + test_fill(); +} diff --git a/tests/std/tests/GH_000940_missing_valarray_copy/env.lst b/tests/std/tests/GH_000940_missing_valarray_copy/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_000940_missing_valarray_copy/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_000940_missing_valarray_copy/test.cpp b/tests/std/tests/GH_000940_missing_valarray_copy/test.cpp new file mode 100644 index 00000000000..91106130487 --- /dev/null +++ b/tests/std/tests/GH_000940_missing_valarray_copy/test.cpp @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +template +bool eq(const std::valarray& v, std::initializer_list il) { + return std::equal(begin(v), end(v), il.begin(), il.end()); +} + +void test_slice() { + std::valarray v{0, 1, 2, 3, 4}; + + std::slice_array slice_array = v[std::slice(2, 2, 2)]; + std::slice_array slice_array_copy = slice_array; + (void) slice_array_copy; + + assert(eq(v, {0, 1, 2, 3, 4})); + + std::slice_array other_slice_array = v[std::slice(0, 2, 1)]; + other_slice_array = slice_array; + + assert(eq(v, {2, 4, 2, 3, 4})); +} + +void test_gslice() { + std::valarray v{0, 1, 2, 3, 4}; + + std::gslice gslice(2, std::valarray{2}, std::valarray{2}); + std::gslice_array gslice_array = v[gslice]; + std::gslice_array gslice_array_copy = gslice_array; + (void) gslice_array_copy; + + assert(eq(v, {0, 1, 2, 3, 4})); + + std::gslice other_gslice(0, std::valarray{2}, std::valarray{1}); + std::gslice_array other_gslice_array = v[other_gslice]; + other_gslice_array = gslice_array; + + assert(eq(v, {2, 4, 2, 3, 4})); +} + +void test_mask() { + std::valarray v{0, 1, 2, 3, 4}; + + std::valarray mask{true, false, false, false, true}; + std::mask_array mask_array = v[mask]; + std::mask_array mask_array_copy = mask_array; + (void) mask_array_copy; + + assert(eq(v, {0, 1, 2, 3, 4})); + + std::valarray other_mask{false, true, true, false, false}; + std::mask_array other_mask_array = v[other_mask]; + other_mask_array = mask_array; + + assert(eq(v, {0, 0, 4, 3, 4})); +} + +void test_indirect() { + std::valarray v{0, 1, 2, 3, 4}; + + std::valarray indices{2, 3}; + std::indirect_array indirect_array = v[indices]; + std::indirect_array indirect_array_copy = indirect_array; + (void) indirect_array_copy; + + assert(eq(v, {0, 1, 2, 3, 4})); + + std::valarray other_indices{4, 0}; + std::indirect_array other_indirect_array = v[other_indices]; + other_indirect_array = indirect_array; + + assert(eq(v, {3, 1, 2, 3, 2})); +} + +int main() { + test_slice(); + test_gslice(); + test_mask(); + test_indirect(); + return 0; +} diff --git a/tests/std/tests/GH_001017_discrete_distribution_out_of_range/bad_random_engine.hpp b/tests/std/tests/GH_001017_discrete_distribution_out_of_range/bad_random_engine.hpp new file mode 100644 index 00000000000..d57d20bb641 --- /dev/null +++ b/tests/std/tests/GH_001017_discrete_distribution_out_of_range/bad_random_engine.hpp @@ -0,0 +1,191 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#pragma once + +#include +#include +#include +#include +#include + +namespace detail { + struct bad_rng_pattern_sentinel {}; + + template ::digits> + class bad_rng_pattern_generator { // generates bit patterns for bad_random_engine + public: + using difference_type = std::ptrdiff_t; + using value_type = UInt; + using pointer = const UInt*; + using reference = const UInt&; + using iterator_category = std::input_iterator_tag; + using my_iter = bad_rng_pattern_generator; + using my_sentinel = bad_rng_pattern_sentinel; + + static constexpr value_type top_bit = value_type{1} << (Width - 1); + static constexpr value_type lower_bits = top_bit - 1; + static constexpr value_type mask_bits = top_bit | lower_bits; + static constexpr int final_bit_count = (Width - 1) / 2 + 1; + + constexpr reference operator*() const noexcept { // gets the current pattern + return current_value_; + } + + constexpr pointer operator->() const noexcept { + return ¤t_value_; + } + + constexpr my_iter& operator++() noexcept { // generates the next pattern + current_value_ = (current_value_ & lower_bits) << 1 | (current_value_ & top_bit) >> (Width - 1); + + if (current_shift_ < Width - 1 && current_bit_count_ != 0 && current_bit_count_ != Width) { + ++current_shift_; + return *this; + } + + current_shift_ = 0; + + if (current_bit_count_ < final_bit_count) { // n 1's -> n 0's + current_bit_count_ = Width - current_bit_count_; + current_value_ ^= mask_bits; + } else if (current_bit_count_ > final_bit_count) { // n 0's -> (n+1) 1's + current_bit_count_ = Width - current_bit_count_ + 1; + current_value_ = (current_value_ ^ mask_bits) << 1 | value_type{1}; + } else { // all bit patterns have been generated, back to all 0's + current_bit_count_ = 0; + current_value_ = value_type{0}; + } + + return *this; + } + + constexpr my_iter operator++(int) noexcept { + const my_iter old = *this; + ++*this; + return old; + } + + friend constexpr bool operator==(const my_iter& a, const my_iter& b) noexcept { + return *a == *b; + } + + friend constexpr bool operator!=(const my_iter& a, const my_iter& b) noexcept { + return !(a == b); + } + + friend constexpr bool operator==(const my_iter& iter, my_sentinel) noexcept { + return *iter == 0; + } + + friend constexpr bool operator!=(const my_iter& iter, const my_sentinel sentinel) noexcept { + return !(iter == sentinel); + } + + friend constexpr bool operator==(const my_sentinel sentinel, const my_iter& iter) noexcept { + return iter == sentinel; + } + + friend constexpr bool operator!=(const my_sentinel sentinel, const my_iter& iter) noexcept { + return !(iter == sentinel); + } + + private: + value_type current_value_ = 0; + int current_bit_count_ = 0; + int current_shift_ = 0; + }; +} // namespace detail + +template ::digits, int Dimension = 1> +class bad_random_engine { + // Generates bit patterns with at most two transitions between 0's and 1's. + // (e.g. 00000000, 11111111, 00001111, 11110000, 00011000, 11100111) + // When its output is grouped into subsequences of length Dimension, it cycles through all possible subsequences + // containing only such bit patterns. Bit patterns with few 1's or few 0's are generated first, starting from all + // 0's and all 1's. + + static_assert(std::is_integral_v, "bad_random_engine: UInt should be unsigned integral type"); + static_assert(std::is_unsigned_v, "bad_random_engine: UInt should be unsigned integral type"); + static_assert(Width > 0, "bad_random_engine: invalid value for Width"); + static_assert(Width <= std::numeric_limits::digits, "bad_random_engine: invalid value for Width"); + static_assert(Dimension > 0, "bad_random_engine: invalid value for Dimension"); + +public: + using result_type = UInt; + + static constexpr result_type(min)() noexcept { + return result_type{0}; + } + static constexpr result_type(max)() noexcept { + return generator::mask_bits; + } + + constexpr result_type operator()() noexcept { + const result_type result = *generators_[current_dimension_]; + + if (current_dimension_ < Dimension - 1) { + ++current_dimension_; + } else { + current_dimension_ = 0; + + if (!generate_next()) { + has_cycled_through_ = true; + } + } + + return result; + } + + constexpr bool has_cycled_through() const noexcept { // have we finished a full cycle? + return has_cycled_through_; + } + +private: + using generator = detail::bad_rng_pattern_generator; + using sentinel = detail::bad_rng_pattern_sentinel; + + constexpr bool generate_next() noexcept { // generates the next subsequence, returns false if back to all 0's + if (limit_value_ != sentinel{}) { + for (int i = 0; i < limit_dimension_; ++i) { + if (generators_[i] != limit_value_) { + ++generators_[i]; + return true; + } else { + generators_[i] = generator{}; + } + } + + for (int i = limit_dimension_ + 1; i < Dimension; ++i) { + ++generators_[i]; + if (generators_[i] != limit_value_) { + return true; + } else { + generators_[i] = generator{}; + } + } + + __analysis_assume(limit_dimension_ < Dimension); + generators_[limit_dimension_] = generator{}; + + if (limit_dimension_ < Dimension - 1) { + ++limit_dimension_; + generators_[limit_dimension_] = limit_value_; + return true; + } + } + + limit_dimension_ = 0; + generators_[0] = ++limit_value_; + return limit_value_ != sentinel{}; + } + + generator generators_[Dimension] = {}; + generator limit_value_{}; + int limit_dimension_ = 0; + int current_dimension_ = 0; + bool has_cycled_through_ = false; +}; + +// the cycle length of bad_random_generator is 32 546 312 +using bad_random_generator = bad_random_engine; diff --git a/tests/std/tests/GH_001017_discrete_distribution_out_of_range/env.lst b/tests/std/tests/GH_001017_discrete_distribution_out_of_range/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_001017_discrete_distribution_out_of_range/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_001017_discrete_distribution_out_of_range/test.cpp b/tests/std/tests/GH_001017_discrete_distribution_out_of_range/test.cpp new file mode 100644 index 00000000000..5a43af9b4df --- /dev/null +++ b/tests/std/tests/GH_001017_discrete_distribution_out_of_range/test.cpp @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include + +#include "bad_random_engine.hpp" + +int main() { + std::discrete_distribution dist{1, 1, 1, 1, 1, 1}; + bad_random_generator rng; + + while (!rng.has_cycled_through()) { + const auto rand_value = dist(rng); + assert(0 <= rand_value && rand_value < 6); + } +} diff --git a/tests/std/tests/GH_001086_partial_sort_copy/env.lst b/tests/std/tests/GH_001086_partial_sort_copy/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_001086_partial_sort_copy/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_001086_partial_sort_copy/test.cpp b/tests/std/tests/GH_001086_partial_sort_copy/test.cpp new file mode 100644 index 00000000000..fc5e54a56ed --- /dev/null +++ b/tests/std/tests/GH_001086_partial_sort_copy/test.cpp @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// GH-1086: "std::partial_sort_copy performs an unconstrained operation" +// partial_sort_copy was constructing an object of the source range's value type +// from the result of dereferencing an iterator, which is not allowed by the +// specification of the algorithm. + +#include +#include +#include + +using namespace std; + +struct wrapper { + wrapper() = default; + constexpr explicit wrapper(int i) : x{i} {} + + bool operator<(const wrapper& that) const { + return x < that.x; + } + + int x; +}; + +struct source : wrapper { + source() = default; + + using wrapper::wrapper; + + source(const source&) = delete; + source& operator=(const source&) = delete; +}; + +struct target : wrapper { + target() = default; + + using wrapper::wrapper; + + target(target&&) = default; + target& operator=(target&&) = default; + + target& operator=(const source& w) { + x = w.x; + return *this; + } +}; + +int main() { + constexpr int src_size = 4; + source src[src_size]; + constexpr int dst_size = 2; + target dst[dst_size]; + + for (int i = 0; i < src_size; ++i) { + src[i].x = src_size - 1 - i; + } + + partial_sort_copy(begin(src), end(src), begin(dst), end(dst)); + + for (int i = 0; i < dst_size; ++i) { + assert(dst[i].x == i); + } +} diff --git a/tests/std/tests/P0024R2_parallel_algorithms_for_each/test.cpp b/tests/std/tests/P0024R2_parallel_algorithms_for_each/test.cpp index bf168a6522d..f3dc7a969c3 100644 --- a/tests/std/tests/P0024R2_parallel_algorithms_for_each/test.cpp +++ b/tests/std/tests/P0024R2_parallel_algorithms_for_each/test.cpp @@ -48,26 +48,40 @@ const auto call_only_once = [](atomic& b) { assert(!b.exchange(true)); }; const auto atomic_identity = [](atomic& b) { return b.load(); }; template