diff --git a/stl/inc/execution b/stl/inc/execution index 0d51ba3b540..feef95947db 100644 --- a/stl/inc/execution +++ b/stl/inc/execution @@ -137,6 +137,35 @@ template <> struct is_execution_policy : true_type {}; #endif // _HAS_CXX20 +template +void _Construct_in_place_by_deref(_Ty& _Val, const _FwdIt& _Iter) { + ::new (static_cast(_STD addressof(_Val))) _Ty(*_Iter); +} + +template +void _Construct_in_place_by_transform_deref(_Ty& _Val, _UnaryOp _Transform_op, const _FwdIt& _Iter) { + ::new (static_cast(_STD addressof(_Val))) _Ty(_Transform_op(*_Iter)); +} + +template +void _Implicitly_construct_in_place_by_binary_op(_Ty& _Val, _BinaryOp _Reduce_op, _ArgTy& _Left, _ArgTy& _Right) { + ::new (static_cast(_STD addressof(_Val))) _Ty([&]() -> _Ty { return _Reduce_op(_Left, _Right); }()); +} + +template +void _Implicitly_construct_in_place_by_binary_op_deref_rhs( + _Ty& _Val, _BinaryOp _Reduce_op, _LeftTy&& _Left, const _FwdIt& _Iter) { + ::new (static_cast(_STD addressof(_Val))) + _Ty([&]() -> _Ty { return _Reduce_op(_STD forward<_LeftTy>(_Left), *_Iter); }()); +} + +template +void _Implicitly_construct_in_place_by_binary_op_transform_deref_rhs( + _Ty& _Val, _BinaryOp _Reduce_op, _UnaryOp _Transform_op, _LeftTy&& _Left, const _FwdIt& _Iter) { + ::new (static_cast(_STD addressof(_Val))) + _Ty([&]() -> _Ty { return _Reduce_op(_STD forward<_LeftTy>(_Left), _Transform_op(*_Iter)); }()); +} + struct _Parallelism_resources_exhausted : exception { _NODISCARD const char* __CLR_OR_THIS_CALL what() const noexcept override { // return pointer to message string @@ -3602,7 +3631,7 @@ struct _Scan_decoupled_lookback { template void _Apply_exclusive_predecessor(_Ty& _Preceding, _FwdIt _First, const _FwdIt _Last, _BinOp _Reduce_op) { // apply _Preceding to [_First, _Last) and _Sum._Ref(), using _Reduce_op - _STD _Construct_in_place(_Sum._Ref(), _Reduce_op(_Preceding, _Local._Ref())); + _STD _Implicitly_construct_in_place_by_binary_op(_Sum._Ref(), _Reduce_op, _Preceding, _Local._Ref()); _State.store(_Local_available | _Sum_available); *_First = _Preceding; @@ -3615,7 +3644,7 @@ struct _Scan_decoupled_lookback { template void _Apply_inclusive_predecessor(_Ty& _Preceding, _FwdIt _First, const _FwdIt _Last, _BinOp _Reduce_op) { // apply _Preceding to [_First, _Last) and _Sum._Ref(), using _Reduce_op - _STD _Construct_in_place(_Sum._Ref(), _Reduce_op(_Preceding, _Local._Ref())); + _STD _Implicitly_construct_in_place_by_binary_op(_Sum._Ref(), _Reduce_op, _Preceding, _Local._Ref()); _State.store(_Local_available | _Sum_available); #pragma loop(ivdep) @@ -3645,8 +3674,8 @@ typename _Iter_value_t<_BidIt>::value_type _Get_lookback_sum(const _BidIt _Curre auto _Prev = _Current; --_Prev; auto _Prev_state = _Prev->_Get_available_state(); - typename _Iter_value_t<_BidIt>::value_type _Result( - _Reduce_op(_Prev_state & _Sum_available ? _Prev->_Sum._Ref() : _Prev->_Local._Ref(), _Current->_Local._Ref())); + typename _Iter_value_t<_BidIt>::value_type _Result = + _Reduce_op(_Prev_state & _Sum_available ? _Prev->_Sum._Ref() : _Prev->_Local._Ref(), _Current->_Local._Ref()); while (!(_Prev_state & _Sum_available)) { --_Prev; _Prev_state = _Prev->_Get_available_state(); @@ -4334,9 +4363,9 @@ _FwdIt2 _Exclusive_scan_per_chunk(_FwdIt1 _First, const _FwdIt1 _Last, _FwdIt2 _ return _Dest; } - _Ty _Tmp(_Reduce_op(_Val, *_First)); // temp to enable _First == _Dest - *_Dest = _Val; - _Val = _STD move(_Tmp); + _Ty _Tmp = _Reduce_op(_Val, *_First); // temp to enable _First == _Dest + *_Dest = _Val; + _Val = _STD move(_Tmp); } } @@ -4346,13 +4375,13 @@ void _Exclusive_scan_per_chunk_complete( // Sum for parallel exclusive_scan with predecessor available, into [_Dest, _Dest + (_Last - _First)) and stores // successor sum in _Val. // Pre: _Val is *uninitialized* && _First != _Last && predecessor sum is in _Init - _STD _Construct_in_place(_Val, _Reduce_op(_Init, *_First)); + _STD _Implicitly_construct_in_place_by_binary_op_deref_rhs(_Val, _Reduce_op, _Init, _First); *_Dest = _Init; while (++_First != _Last) { ++_Dest; - _Ty _Tmp(_Reduce_op(_Val, *_First)); // temp to enable _First == _Dest - *_Dest = _STD move(_Val); - _Val = _STD move(_Tmp); + _Ty _Tmp = _Reduce_op(_Val, *_First); // temp to enable _First == _Dest + *_Dest = _STD move(_Val); + _Val = _STD move(_Tmp); } } @@ -4471,9 +4500,10 @@ _FwdIt2 _Inclusive_scan_per_chunk( // _Val. // pre: _Val is *uninitialized* && _First != _Last if constexpr (is_same_v<_No_init_tag, remove_const_t>>) { - _STD _Construct_in_place(_Val, *_First); + _STD _Construct_in_place_by_deref(_Val, _First); } else { - _STD _Construct_in_place(_Val, _Reduce_op(_STD forward<_Ty_fwd>(_Predecessor), *_First)); + _STD _Implicitly_construct_in_place_by_binary_op_deref_rhs( + _Val, _Reduce_op, _STD forward<_Ty_fwd>(_Predecessor), _First); } for (;;) { @@ -4645,7 +4675,7 @@ _FwdIt2 _Transform_exclusive_scan_per_chunk( // Local-sum for parallel transform_exclusive_scan; writes local sums into [_Dest + 1, _Dest + (_Last - _First)) and // stores successor sum in _Val. // pre: _Val is *uninitialized* && _First != _Last - _STD _Construct_in_place(_Val, _Transform_op(*_First)); + _STD _Construct_in_place_by_transform_deref(_Val, _Transform_op, _First); for (;;) { ++_First; ++_Dest; @@ -4653,9 +4683,9 @@ _FwdIt2 _Transform_exclusive_scan_per_chunk( return _Dest; } - _Ty _Tmp(_Reduce_op(_Val, _Transform_op(*_First))); // temp to enable _First == _Dest - *_Dest = _Val; - _Val = _STD move(_Tmp); + _Ty _Tmp = _Reduce_op(_Val, _Transform_op(*_First)); // temp to enable _First == _Dest + *_Dest = _Val; + _Val = _STD move(_Tmp); } } @@ -4665,13 +4695,14 @@ void _Transform_exclusive_scan_per_chunk_complete(_FwdIt1 _First, const _FwdIt1 // Sum for parallel transform_exclusive_scan with predecessor available, into [_Dest, _Dest + (_Last - _First)) and // stores successor sum in _Val. // pre: _Val is *uninitialized* && _First != _Last && predecessor sum is in _Init - _STD _Construct_in_place(_Val, _Reduce_op(_Init, _Transform_op(*_First))); + _STD _Implicitly_construct_in_place_by_binary_op_transform_deref_rhs( + _Val, _Reduce_op, _Transform_op, _Init, _First); *_Dest = _Init; while (++_First != _Last) { ++_Dest; - _Ty _Tmp(_Reduce_op(_Val, _Transform_op(*_First))); // temp to enable _First == _Dest - *_Dest = _STD move(_Val); - _Val = _STD move(_Tmp); + _Ty _Tmp = _Reduce_op(_Val, _Transform_op(*_First)); // temp to enable _First == _Dest + *_Dest = _STD move(_Val); + _Val = _STD move(_Tmp); } } @@ -4791,9 +4822,10 @@ _FwdIt2 _Transform_inclusive_scan_per_chunk(_FwdIt1 _First, const _FwdIt1 _Last, // sum in _Val // pre: _Val is *uninitialized* && _First != _Last if constexpr (is_same_v<_No_init_tag, remove_const_t>>) { - _STD _Construct_in_place(_Val, _Transform_op(*_First)); + _STD _Construct_in_place_by_transform_deref(_Val, _Transform_op, _First); } else { - _STD _Construct_in_place(_Val, _Reduce_op(_STD forward<_Ty_fwd>(_Predecessor), _Transform_op(*_First))); + _STD _Implicitly_construct_in_place_by_binary_op_transform_deref_rhs( + _Val, _Reduce_op, _Transform_op, _STD forward<_Ty_fwd>(_Predecessor), _First); } for (;;) { diff --git a/stl/inc/numeric b/stl/inc/numeric index f10480a3d8c..ffe819a36e3 100644 --- a/stl/inc/numeric +++ b/stl/inc/numeric @@ -281,8 +281,8 @@ _CONSTEXPR20 _OutIt exclusive_scan(const _InIt _First, const _InIt _Last, _OutIt auto _UDest = _STD _Get_unwrapped_n(_Dest, _STD _Idl_distance<_InIt>(_UFirst, _ULast)); if (_UFirst != _ULast) { for (;;) { - _Ty _Tmp(_Reduce_op(_Val, *_UFirst)); // temp to enable _First == _Dest, also requirement missing - *_UDest = _Val; + _Ty _Tmp = _Reduce_op(_Val, *_UFirst); // temp to enable _First == _Dest, also requirement missing + *_UDest = _Val; ++_UDest; ++_UFirst; if (_UFirst == _ULast) { @@ -389,8 +389,8 @@ _CONSTEXPR20 _OutIt transform_exclusive_scan( auto _UDest = _STD _Get_unwrapped_n(_Dest, _STD _Idl_distance<_InIt>(_UFirst, _ULast)); if (_UFirst != _ULast) { for (;;) { - _Ty _Tmp(_Reduce_op(_Val, _Transform_op(*_UFirst))); // temp to enable _First == _Dest - *_UDest = _Val; + _Ty _Tmp = _Reduce_op(_Val, _Transform_op(*_UFirst)); // temp to enable _First == _Dest + *_UDest = _Val; ++_UDest; ++_UFirst; if (_UFirst == _ULast) { diff --git a/tests/std/test.lst b/tests/std/test.lst index 88e899d0149..7f2914d7a79 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -237,6 +237,7 @@ tests\GH_003867_output_nan tests\GH_004023_mdspan_fwd_prod_overflow tests\GH_004040_container_nonmember_functions tests\GH_004109_iter_value_t_direct_initialization +tests\GH_004129_conversion_in_new_numeric_algorithms tests\GH_004201_chrono_formatter tests\GH_004275_seeking_fancy_iterators tests\GH_004388_unordered_meow_operator_equal diff --git a/tests/std/tests/GH_004129_conversion_in_new_numeric_algorithms/env.lst b/tests/std/tests/GH_004129_conversion_in_new_numeric_algorithms/env.lst new file mode 100644 index 00000000000..2de7aab2959 --- /dev/null +++ b/tests/std/tests/GH_004129_conversion_in_new_numeric_algorithms/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_17_matrix.lst diff --git a/tests/std/tests/GH_004129_conversion_in_new_numeric_algorithms/test.cpp b/tests/std/tests/GH_004129_conversion_in_new_numeric_algorithms/test.cpp new file mode 100644 index 00000000000..9cf3820ad63 --- /dev/null +++ b/tests/std/tests/GH_004129_conversion_in_new_numeric_algorithms/test.cpp @@ -0,0 +1,186 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// intentionally test narrowing conversion from int64_t to int32_t +#pragma warning(disable : 4244) + +#include +#include +#include +#include +#include + +using namespace std; +using namespace std::execution; + +struct implicitly_convertible_to_i32_only { + int32_t n; + + template + explicit operator int32_t() const = delete; + + operator int64_t() const noexcept { + return n; + } +}; + +static_assert(!is_constructible_v); +static_assert(is_convertible_v); + +struct implicitly_validating_converter { + implicitly_convertible_to_i32_only operator()(int n) const noexcept { + return {n}; + } +}; + +struct explicitly_convertible_to_i32_only { + int32_t n; + + explicit operator int32_t() const noexcept { + return n; + } +}; + +static_assert(is_constructible_v); +static_assert(!is_convertible_v); + +struct transformation_validating_converter { + explicitly_convertible_to_i32_only operator()(int n) const noexcept { + return {n}; + } +}; + +struct implicitly_validating_plus { + implicitly_convertible_to_i32_only operator()( + implicitly_convertible_to_i32_only l, implicitly_convertible_to_i32_only r) const noexcept { + return implicitly_convertible_to_i32_only{l.n + r.n}; + } + + implicitly_convertible_to_i32_only operator()(int32_t l, implicitly_convertible_to_i32_only r) const noexcept { + return implicitly_convertible_to_i32_only{l + r.n}; + } + + implicitly_convertible_to_i32_only operator()(implicitly_convertible_to_i32_only l, int32_t r) const noexcept { + return implicitly_convertible_to_i32_only{l.n + r}; + } + + implicitly_convertible_to_i32_only operator()(int32_t l, int32_t r) const noexcept { + return implicitly_convertible_to_i32_only{l + r}; + } +}; + +struct implicitly_validating_plus_for_transformation { + implicitly_convertible_to_i32_only operator()( + explicitly_convertible_to_i32_only l, explicitly_convertible_to_i32_only r) const noexcept { + return implicitly_convertible_to_i32_only{l.n + r.n}; + } + + implicitly_convertible_to_i32_only operator()(int32_t l, explicitly_convertible_to_i32_only r) const noexcept { + return implicitly_convertible_to_i32_only{l + r.n}; + } + + implicitly_convertible_to_i32_only operator()(explicitly_convertible_to_i32_only l, int32_t r) const noexcept { + return implicitly_convertible_to_i32_only{l.n + r}; + } + + implicitly_convertible_to_i32_only operator()(int32_t l, int32_t r) const noexcept { + return implicitly_convertible_to_i32_only{l + r}; + } +}; + +struct implicitly_validating_multiplies { + implicitly_convertible_to_i32_only operator()( + implicitly_convertible_to_i32_only l, implicitly_convertible_to_i32_only r) const noexcept { + return implicitly_convertible_to_i32_only{l.n * r.n}; + } + + implicitly_convertible_to_i32_only operator()(int32_t l, implicitly_convertible_to_i32_only r) const noexcept { + return implicitly_convertible_to_i32_only{l * r.n}; + } + + implicitly_convertible_to_i32_only operator()(implicitly_convertible_to_i32_only l, int32_t r) const noexcept { + return implicitly_convertible_to_i32_only{l.n * r}; + } + + implicitly_convertible_to_i32_only operator()(int32_t l, int32_t r) const noexcept { + return implicitly_convertible_to_i32_only{l * r}; + } +}; + +void test_copy_initialization_for_numeric_algorithms() { + int arr[1]{}; + implicitly_convertible_to_i32_only brr[1]{}; + + assert(reduce(arr, arr, int32_t{}, implicitly_validating_plus{}) == 0); + assert(reduce(brr, brr, int32_t{}, implicitly_validating_plus{}) == 0); + + assert(transform_reduce(arr, arr, arr, int32_t{}, implicitly_validating_plus{}, implicitly_validating_multiplies{}) + == 0); + assert(transform_reduce(arr, arr, brr, int32_t{}, implicitly_validating_plus{}, implicitly_validating_multiplies{}) + == 0); + + assert(transform_reduce(arr, arr, int32_t{}, implicitly_validating_plus{}, implicitly_validating_converter{}) == 0); + assert(transform_reduce(brr, brr, int32_t{}, implicitly_validating_plus{}, implicitly_validating_converter{}) == 0); + + assert(exclusive_scan(arr, arr, arr, int32_t{}, implicitly_validating_plus{}) == arr); + + assert(inclusive_scan(arr, arr, arr, implicitly_validating_plus{}) == arr); + assert(inclusive_scan(arr, arr, arr, implicitly_validating_plus{}, int32_t{}) == arr); + + assert(transform_exclusive_scan(arr, arr, arr, int32_t{}, implicitly_validating_plus_for_transformation{}, + transformation_validating_converter{}) + == arr); + + assert(transform_inclusive_scan(brr, brr, brr, implicitly_validating_plus{}, implicitly_validating_converter{}) + == brr); + assert(transform_inclusive_scan(arr, arr, arr, implicitly_validating_plus_for_transformation{}, + transformation_validating_converter{}, int32_t{}) + == arr); +} + +template +void test_copy_initialization_for_parallel_numeric_algorithms() { + int arr[1]{}; + implicitly_convertible_to_i32_only brr[1]{}; + + assert(reduce(ExPo, arr, arr, int32_t{}, implicitly_validating_plus{}) == 0); + assert(reduce(ExPo, brr, brr, int32_t{}, implicitly_validating_plus{}) == 0); + + assert(transform_reduce( + ExPo, arr, arr, arr, int32_t{}, implicitly_validating_plus{}, implicitly_validating_multiplies{}) + == 0); + assert(transform_reduce( + ExPo, arr, arr, brr, int32_t{}, implicitly_validating_plus{}, implicitly_validating_multiplies{}) + == 0); + + assert(transform_reduce(ExPo, arr, arr, int32_t{}, implicitly_validating_plus{}, implicitly_validating_converter{}) + == 0); + assert(transform_reduce(ExPo, brr, brr, int32_t{}, implicitly_validating_plus{}, implicitly_validating_converter{}) + == 0); + + assert(exclusive_scan(ExPo, arr, arr, arr, int32_t{}, implicitly_validating_plus{}) == arr); + + assert(inclusive_scan(ExPo, arr, arr, arr, implicitly_validating_plus{}) == arr); + assert(inclusive_scan(ExPo, arr, arr, arr, implicitly_validating_plus{}, int32_t{}) == arr); + + assert(transform_exclusive_scan(ExPo, arr, arr, arr, int32_t{}, implicitly_validating_plus_for_transformation{}, + transformation_validating_converter{}) + == arr); + + assert( + transform_inclusive_scan(ExPo, brr, brr, brr, implicitly_validating_plus{}, implicitly_validating_converter{}) + == brr); + assert(transform_inclusive_scan(ExPo, arr, arr, arr, implicitly_validating_plus_for_transformation{}, + transformation_validating_converter{}, int32_t{}) + == arr); +} + +int main() { + test_copy_initialization_for_numeric_algorithms(); + test_copy_initialization_for_parallel_numeric_algorithms(); + test_copy_initialization_for_parallel_numeric_algorithms(); + test_copy_initialization_for_parallel_numeric_algorithms(); +#if _HAS_CXX20 + test_copy_initialization_for_parallel_numeric_algorithms(); +#endif // _HAS_CXX20 +} diff --git a/tests/std/tests/P0024R2_parallel_algorithms_exclusive_scan/test.cpp b/tests/std/tests/P0024R2_parallel_algorithms_exclusive_scan/test.cpp index cdb0c7578dc..5a00a8d07b9 100644 --- a/tests/std/tests/P0024R2_parallel_algorithms_exclusive_scan/test.cpp +++ b/tests/std/tests/P0024R2_parallel_algorithms_exclusive_scan/test.cpp @@ -126,7 +126,8 @@ struct intermediateType { intermediateType() = delete; explicit intermediateType(int) {} // so that the test can make one of these explicit intermediateType(inputType&) {} // Intermediate tmp(*first) - explicit intermediateType(bopResult&&) {} // Intermediate tmp(binary_op((one of tmp, move(tmp), *first), *first)) + // Intermediate tmp = binary_op((one of tmp, move(tmp), *first), *first); + /* implicit */ intermediateType(bopResult&&) {} intermediateType(const intermediateType&) = delete; intermediateType(intermediateType&&) = default; // tmp = move(tmp) intermediateType& operator=(const intermediateType&) = delete; diff --git a/tests/std/tests/P0024R2_parallel_algorithms_inclusive_scan/test.cpp b/tests/std/tests/P0024R2_parallel_algorithms_inclusive_scan/test.cpp index eddb7b3b6b8..a3ecee5b3bd 100644 --- a/tests/std/tests/P0024R2_parallel_algorithms_inclusive_scan/test.cpp +++ b/tests/std/tests/P0024R2_parallel_algorithms_inclusive_scan/test.cpp @@ -135,7 +135,8 @@ struct intermediateType { intermediateType() = delete; explicit intermediateType(int) {} // so that the test can make one of these explicit intermediateType(inputType&) {} // Intermediate tmp(*first) - explicit intermediateType(bopResult&&) {} // Intermediate tmp(binary_op((one of tmp, move(tmp), *first), *first)) + // Intermediate tmp = binary_op((one of tmp, move(tmp), *first), *first); + /* implicit */ intermediateType(bopResult&&) {} intermediateType(const intermediateType&) = delete; intermediateType(intermediateType&&) = default; // tmp = move(tmp) intermediateType& operator=(const intermediateType&) = delete; diff --git a/tests/std/tests/P0024R2_parallel_algorithms_transform_exclusive_scan/test.cpp b/tests/std/tests/P0024R2_parallel_algorithms_transform_exclusive_scan/test.cpp index 96c6bd1f85d..7cb3d6f11d6 100644 --- a/tests/std/tests/P0024R2_parallel_algorithms_transform_exclusive_scan/test.cpp +++ b/tests/std/tests/P0024R2_parallel_algorithms_transform_exclusive_scan/test.cpp @@ -115,8 +115,8 @@ struct intermediateType { intermediateType() = delete; explicit intermediateType(int) {} // so that the test can make one of these explicit intermediateType(transformedType&&) {} // Intermediate tmp(unary_op(*first)) - // Intermediate tmp(binary_op((one of tmp, move(tmp), unary_op(*first)), unary_op(*first))) - explicit intermediateType(bopResult&&) {} + // Intermediate tmp = binary_op((one of tmp, move(tmp), unary_op(*first)), unary_op(*first)); + /* implicit */ intermediateType(bopResult&&) {} intermediateType(const intermediateType&) = delete; intermediateType(intermediateType&&) = default; // tmp = move(tmp) intermediateType& operator=(const intermediateType&) = delete; diff --git a/tests/std/tests/P0024R2_parallel_algorithms_transform_inclusive_scan/test.cpp b/tests/std/tests/P0024R2_parallel_algorithms_transform_inclusive_scan/test.cpp index 7608cfc7e63..c54e01aa69d 100644 --- a/tests/std/tests/P0024R2_parallel_algorithms_transform_inclusive_scan/test.cpp +++ b/tests/std/tests/P0024R2_parallel_algorithms_transform_inclusive_scan/test.cpp @@ -139,8 +139,8 @@ struct intermediateType { intermediateType() = delete; explicit intermediateType(int) {} // so that the test can make one of these explicit intermediateType(transformedType&&) {} // Intermediate tmp(unary_op(*first)) - // Intermediate tmp(binary_op((one of tmp, move(tmp), unary_op(*first)), unary_op(*first))) - explicit intermediateType(bopResult&&) {} + // Intermediate tmp = binary_op((one of tmp, move(tmp), unary_op(*first)), unary_op(*first)); + /* implicit */ intermediateType(bopResult&&) {} intermediateType(const intermediateType&) = delete; intermediateType(intermediateType&&) = default; // tmp = move(tmp) intermediateType& operator=(const intermediateType&) = delete;