Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -5616,6 +5616,10 @@ namespace ranges {
} // namespace ranges
#endif // _HAS_CXX20

template <class _Ty>
struct _Is_trivially_copy_assignable_returning_same_reference
: bool_constant<_Is_trivially_assignable_returning_same_reference_v<_Ty&, const _Ty&>> {};

_EXPORT_STD template <class _BidIt, class _OutIt>
_CONSTEXPR20 _OutIt reverse_copy(_BidIt _First, _BidIt _Last, _OutIt _Dest) {
// copy reversing elements in [_First, _Last)
Expand All @@ -5629,8 +5633,8 @@ _CONSTEXPR20 _OutIt reverse_copy(_BidIt _First, _BidIt _Last, _OutIt _Dest) {
using _Elem = remove_reference_t<_Iter_ref_t<remove_const_t<decltype(_UFirst)>>>;
using _DestElem = remove_reference_t<_Iter_ref_t<decltype(_UDest)>>;
constexpr bool _Allow_vectorization = conjunction_v<is_same<remove_const_t<_Elem>, _DestElem>,
bool_constant<_Iterators_are_contiguous<decltype(_ULast), decltype(_UDest)>>, is_trivially_copyable<_Elem>,
negation<is_volatile<_Elem>>>;
bool_constant<_Iterators_are_contiguous<decltype(_ULast), decltype(_UDest)>>,
_Is_trivially_copy_assignable_returning_same_reference<_Elem>, negation<is_volatile<_Elem>>>;
constexpr size_t _Nx = sizeof(_Elem);

if constexpr (_Allow_vectorization && _Nx <= 8 && (_Nx & (_Nx - 1)) == 0) {
Expand Down Expand Up @@ -5719,7 +5723,7 @@ namespace ranges {
using _Elem = remove_reference_t<iter_reference_t<_It>>;
using _DestElem = remove_reference_t<iter_reference_t<_Out>>;
constexpr bool _Allow_vectorization = conjunction_v<is_same<remove_const_t<_Elem>, _DestElem>,
is_trivially_copyable<_Elem>, negation<is_volatile<_Elem>>>;
_Is_trivially_copy_assignable_returning_same_reference<_Elem>, negation<is_volatile<_Elem>>>;
constexpr size_t _Nx = sizeof(_Elem);

if constexpr (_Allow_vectorization && _Nx <= 8 && (_Nx & (_Nx - 1)) == 0) {
Expand Down
24 changes: 22 additions & 2 deletions stl/inc/xutility
Original file line number Diff line number Diff line change
Expand Up @@ -4683,6 +4683,25 @@ constexpr bool _Is_pointer_address_convertible = is_void_v<_Source>
#endif // defined(__cpp_lib_is_pointer_interconvertible)
;

#pragma warning(push)
#pragma warning(disable : 4242) // '%s': conversion from '%s' to '%s', possible loss of data
#pragma warning(disable : 4244) // '%s': conversion from '%s' to '%s', possible loss of data (Yes, duplicated message.)
#pragma warning(disable : 4267) // '%s': conversion from '%s' to '%s', possible loss of data (Yes, duplicated message!)
#pragma warning(disable : 4365) // '%s': conversion from '%s' to '%s', signed/unsigned mismatch
#pragma warning(disable : 5267) // definition of implicit assignment operator for '%s' is deprecated because it has a
// user-provided copy constructor

// Determines whether _DestRef is trivially assignable from _SourceRef, and if _Remove_cvref_t<_DestRef> is a class, the
// assignment uses _Remove_cvref_t<_DestRef>::operator=.
// Not pedantically reliable for vectorization, but working due to bugs of MSVC, Clang, and EDG. See LLVM-37038.
template <class _DestRef, class _SourceRef, bool = is_trivially_assignable_v<_DestRef, _SourceRef>>
constexpr bool _Is_trivially_assignable_returning_same_reference_v =
is_same_v<_DestRef, decltype(_STD declval<_DestRef>() = _STD declval<_SourceRef>())>;
template <class _DestRef, class _SourceRef>
constexpr bool _Is_trivially_assignable_returning_same_reference_v<_DestRef, _SourceRef, false> = false;

#pragma warning(pop)

template <class _Source, class _Dest, class _SourceRef, class _DestRef>
struct _Trivial_cat {
using _USource = _Unwrap_enum_t<_Source>;
Expand All @@ -4701,7 +4720,7 @@ struct _Trivial_cat {
_Same_size_and_compatible && is_trivially_constructible_v<_Dest, _SourceRef>;

static constexpr bool _Bitcopy_assignable =
_Same_size_and_compatible && is_trivially_assignable_v<_DestRef, _SourceRef>;
_Same_size_and_compatible && _Is_trivially_assignable_returning_same_reference_v<_DestRef, _SourceRef>;
};

template <class _Source, class _Dest, class _SourceRef, class _DestRef>
Expand All @@ -4710,7 +4729,8 @@ struct _Trivial_cat<_Source*, _Dest*, _SourceRef, _DestRef> {
_Is_pointer_address_convertible<_Source, _Dest> && is_trivially_constructible_v<_Dest*, _SourceRef>;

static constexpr bool _Bitcopy_assignable =
_Is_pointer_address_convertible<_Source, _Dest> && is_trivially_assignable_v<_DestRef, _SourceRef>;
_Is_pointer_address_convertible<_Source, _Dest>
&& _Is_trivially_assignable_returning_same_reference_v<_DestRef, _SourceRef>;
};

struct _False_trivial_cat {
Expand Down
1 change: 1 addition & 0 deletions tests/std/test.lst
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ tests\GH_004609_heterogeneous_cmp_overloads
tests\GH_004618_mixed_operator_usage_keeps_statistical_properties
tests\GH_004618_normal_distribution_avoids_resets
tests\GH_004657_expected_constraints_permissive
tests\GH_004686_vectorization_on_trivial_assignability
tests\GH_004845_logical_operator_traits_with_non_bool_constant
tests\GH_004929_internal_tag_constructors
tests\GH_004930_char_traits_user_specialization
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

RUNALL_INCLUDE ..\usual_matrix.lst
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
// Copyright (c) Microsoft Corporation.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <algorithm>
#include <cassert>
#include <iterator>
#include <utility>

#if _HAS_CXX20
#define CONSTEXPR20 constexpr
#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv
#define CONSTEXPR20 inline
#endif // ^^^ !_HAS_CXX20 ^^^

using namespace std;

struct Cat {};
struct Leopard : Cat {
int spots_;

Leopard() = default;
Leopard(const Leopard&) = default;
Leopard(Leopard&&) = default;
Leopard& operator=(Leopard&) && = delete;
using Cat::operator=;
};

constexpr pair<int, int> expected_results[]{{5, 6}, {3, 4}, {1, 2}};

CONSTEXPR20 void test_reverse_copy() {
{
pair<int, int> src[] = {{1, 2}, {3, 4}, {5, 6}};
pair<int, int> dst[] = {{3, 1}, {4, 1}, {5, 9}};
pair<int&, int&> srcref[] = {
{src[0].first, src[0].second}, {src[1].first, src[1].second}, {src[2].first, src[2].second}};
pair<int&, int&> dstref[] = {
{dst[0].first, dst[0].second}, {dst[1].first, dst[1].second}, {dst[2].first, dst[2].second}};

reverse_copy(begin(srcref), end(srcref), dstref);
assert(equal(begin(dst), end(dst), begin(expected_results), end(expected_results)));
}
#if _HAS_CXX20
{
pair<int, int> src[] = {{1, 2}, {3, 4}, {5, 6}};
pair<int, int> dst[] = {{3, 1}, {4, 1}, {5, 9}};
pair<int&, int&> srcref[] = {
{src[0].first, src[0].second}, {src[1].first, src[1].second}, {src[2].first, src[2].second}};
pair<int&, int&> dstref[] = {
{dst[0].first, dst[0].second}, {dst[1].first, dst[1].second}, {dst[2].first, dst[2].second}};

ranges::reverse_copy(srcref, dstref);
assert(ranges::equal(dst, expected_results));
}
#endif // _HAS_CXX20
#if _HAS_CXX23
{
pair<int, int> src[] = {{1, 2}, {3, 4}, {5, 6}};
pair<int, int> dst[] = {{3, 1}, {4, 1}, {5, 9}};
pair<int&, int&> srcref[] = {src[0], src[1], src[2]};
pair<int&, int&> dstref[] = {dst[0], dst[1], dst[2]};

reverse_copy(begin(srcref), end(srcref), dstref);
assert(equal(begin(dst), end(dst), begin(expected_results), end(expected_results)));
}
{
pair<int, int> src[] = {{1, 2}, {3, 4}, {5, 6}};
pair<int, int> dst[] = {{3, 1}, {4, 1}, {5, 9}};
pair<int&, int&> srcref[] = {src[0], src[1], src[2]};
pair<int&, int&> dstref[] = {dst[0], dst[1], dst[2]};

ranges::reverse_copy(srcref, dstref);
assert(ranges::equal(dst, expected_results));
}
#endif // _HAS_CXX23
}

constexpr Leopard make_leopard(const int n) noexcept {
Leopard result{};
result.spots_ = n;
return result;
}

CONSTEXPR20 void test_copy_move_leopards() {
constexpr Leopard expected_leopards[]{
make_leopard(3), make_leopard(1), make_leopard(4), make_leopard(1), make_leopard(5), make_leopard(9)};
constexpr Leopard zero_leopards[6]{};
auto equal_leopard = [](const Leopard& lhs, const Leopard& rhs) { return lhs.spots_ == rhs.spots_; };
{
Leopard dst[]{
make_leopard(3), make_leopard(1), make_leopard(4), make_leopard(1), make_leopard(5), make_leopard(9)};
copy(begin(zero_leopards), end(zero_leopards), begin(dst));
assert(equal(begin(dst), end(dst), begin(expected_leopards), end(expected_leopards), equal_leopard));
}
{
Leopard dst[]{
make_leopard(3), make_leopard(1), make_leopard(4), make_leopard(1), make_leopard(5), make_leopard(9)};
copy_n(begin(zero_leopards), size(zero_leopards), begin(dst));
assert(equal(begin(dst), end(dst), begin(expected_leopards), end(expected_leopards), equal_leopard));
}
{
Leopard dst[]{
make_leopard(3), make_leopard(1), make_leopard(4), make_leopard(1), make_leopard(5), make_leopard(9)};
copy_backward(begin(zero_leopards), end(zero_leopards), end(dst));
assert(equal(begin(dst), end(dst), begin(expected_leopards), end(expected_leopards), equal_leopard));
}
{
Leopard dst[]{
make_leopard(3), make_leopard(1), make_leopard(4), make_leopard(1), make_leopard(5), make_leopard(9)};
move(begin(zero_leopards), end(zero_leopards), begin(dst));
assert(equal(begin(dst), end(dst), begin(expected_leopards), end(expected_leopards), equal_leopard));
}
{
Leopard dst[]{
make_leopard(3), make_leopard(1), make_leopard(4), make_leopard(1), make_leopard(5), make_leopard(9)};
move_backward(begin(zero_leopards), end(zero_leopards), end(dst));
assert(equal(begin(dst), end(dst), begin(expected_leopards), end(expected_leopards), equal_leopard));
}
#if _HAS_CXX20
{
Leopard dst[]{
make_leopard(3), make_leopard(1), make_leopard(4), make_leopard(1), make_leopard(5), make_leopard(9)};
ranges::copy(zero_leopards, dst);
assert(ranges::equal(dst, expected_leopards, equal_leopard));
}
{
Leopard dst[]{
make_leopard(3), make_leopard(1), make_leopard(4), make_leopard(1), make_leopard(5), make_leopard(9)};
ranges::copy_n(ranges::begin(zero_leopards), ranges::distance(zero_leopards), dst);
assert(ranges::equal(dst, expected_leopards, equal_leopard));
}
{
Leopard dst[]{
make_leopard(3), make_leopard(1), make_leopard(4), make_leopard(1), make_leopard(5), make_leopard(9)};
ranges::copy_backward(zero_leopards, ranges::end(dst));
assert(ranges::equal(dst, expected_leopards, equal_leopard));
}
{
Leopard dst[]{
make_leopard(3), make_leopard(1), make_leopard(4), make_leopard(1), make_leopard(5), make_leopard(9)};
ranges::move(zero_leopards, dst);
assert(ranges::equal(dst, expected_leopards, equal_leopard));
}
{
Leopard dst[]{
make_leopard(3), make_leopard(1), make_leopard(4), make_leopard(1), make_leopard(5), make_leopard(9)};
ranges::move_backward(zero_leopards, ranges::end(dst));
assert(ranges::equal(dst, expected_leopards, equal_leopard));
}
#endif // _HAS_CXX20
}

// Pedantically, all of MSVC, Clang, and EDG are currently wrong on this, see LLVM-37038.
// However, if compilers get corrected, the assignment operators of `DerivedLeopard` and `LeopardHouse` will be trivial
// but no-op, and the library side can't correctly conclude that assignments for them shouldn't be vectorized.
// As a result, we keep this as a regression test.
CONSTEXPR20 void test_llvm_37038() {
struct DerivedLeopard : Leopard {};
static_assert(is_trivially_move_assignable_v<DerivedLeopard>, "");

auto make_derived_leopard = [](int n) {
DerivedLeopard ret{};
ret.spots_ = n;
return ret;
};

auto equal_derived = [](const DerivedLeopard& lhs, const DerivedLeopard& rhs) { return lhs.spots_ == rhs.spots_; };

{
DerivedLeopard src[]{
make_derived_leopard(1), make_derived_leopard(7), make_derived_leopard(2), make_derived_leopard(9)};
DerivedLeopard dst[4]{};
move(begin(src), end(src), dst);
assert(equal(begin(dst), end(dst), begin(src), end(src), equal_derived));
}
{
DerivedLeopard src[]{
make_derived_leopard(1), make_derived_leopard(7), make_derived_leopard(2), make_derived_leopard(9)};
DerivedLeopard dst[4]{};
move_backward(begin(src), end(src), end(dst));
assert(equal(begin(dst), end(dst), begin(src), end(src), equal_derived));
}
#if _HAS_CXX20
{
DerivedLeopard src[]{
make_derived_leopard(1), make_derived_leopard(7), make_derived_leopard(2), make_derived_leopard(9)};
DerivedLeopard dst[4]{};
ranges::move(src, dst);
assert(ranges::equal(src, dst, equal_derived));
}
{
DerivedLeopard src[]{
make_derived_leopard(1), make_derived_leopard(7), make_derived_leopard(2), make_derived_leopard(9)};
DerivedLeopard dst[4]{};
ranges::move_backward(src, ranges::end(dst));
assert(ranges::equal(src, dst, equal_derived));
}
#endif // _HAS_CXX20

struct LeopardHouse {
Leopard bigcat_;
};
static_assert(is_trivially_move_assignable_v<LeopardHouse>, "");

auto make_leopard_house = [](int n) {
LeopardHouse ret{};
ret.bigcat_.spots_ = n;
return ret;
};

auto equal_house = [](const LeopardHouse& lhs, const LeopardHouse& rhs) {
return lhs.bigcat_.spots_ == rhs.bigcat_.spots_;
};

{
LeopardHouse src[]{make_leopard_house(1), make_leopard_house(7), make_leopard_house(2), make_leopard_house(9)};
LeopardHouse dst[4]{};
move(begin(src), end(src), dst);
assert(equal(begin(dst), end(dst), begin(src), end(src), equal_house));
}
{
LeopardHouse src[]{make_leopard_house(1), make_leopard_house(7), make_leopard_house(2), make_leopard_house(9)};
LeopardHouse dst[4]{};
move_backward(begin(src), end(src), end(dst));
assert(equal(begin(dst), end(dst), begin(src), end(src), equal_house));
}
#if _HAS_CXX20
{
LeopardHouse src[]{make_leopard_house(1), make_leopard_house(7), make_leopard_house(2), make_leopard_house(9)};
LeopardHouse dst[4]{};
ranges::move(src, dst);
assert(ranges::equal(src, dst, equal_house));
}
{
LeopardHouse src[]{make_leopard_house(1), make_leopard_house(7), make_leopard_house(2), make_leopard_house(9)};
LeopardHouse dst[4]{};
ranges::move_backward(src, ranges::end(dst));
assert(ranges::equal(src, dst, equal_house));
}
#endif // _HAS_CXX20
}

CONSTEXPR20 bool test() {
test_reverse_copy();
test_copy_move_leopards();
test_llvm_37038();

return true;
}

#if _HAS_CXX20
static_assert(test());
#endif // _HAS_CXX20

int main() {
test();
}