Differential D138364 Diff 476707 libcxx/test/libcxx/algorithms/alg.modifying.operations/copy_move_nontrivial.pass.cpp
Changeset View
Changeset View
Standalone View
Standalone View
libcxx/test/libcxx/algorithms/alg.modifying.operations/copy_move_nontrivial.pass.cpp
- This file was added.
//===----------------------------------------------------------------------===// | |||||
// | |||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |||||
// See https://llvm.org/LICENSE.txt for license information. | |||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |||||
// | |||||
//===----------------------------------------------------------------------===// | |||||
// UNSUPPORTED: c++03, c++11, c++14, c++17 | |||||
// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-steps): -fconstexpr-steps=2000000 | |||||
// ADDITIONAL_COMPILE_FLAGS(has-fconstexpr-ops-limit): -fconstexpr-ops-limit=2000000 | |||||
// <algorithm> | |||||
#include <cassert> | |||||
#include <cstddef> | |||||
// These tests checks that `std::copy` and `std::move` (including their variations like `copy_n`) don't forward to | |||||
// `std::memmove` when doing so would be observable. | |||||
// This template is a better match than the actual `builtin_memmove` (it can match the pointer type exactly, without an | |||||
// implicit conversion to `void*`), so it should hijack the call inside `std::copy` and similar algorithms if it's made. | |||||
template <class Dst, class Src> | |||||
constexpr void* __builtin_memmove(Dst*, Src*, size_t) { | |||||
assert(false); | |||||
return nullptr; | |||||
} | |||||
#include <algorithm> | |||||
#include <cassert> | |||||
#include <iterator> | |||||
#include <ranges> | |||||
#include <type_traits> | |||||
#include "test_iterators.h" | |||||
#include "test_macros.h" | |||||
template <size_t N, class Iter> | |||||
requires (N == 0) | |||||
constexpr auto wrap_n_times(Iter i) { | |||||
return i; | |||||
} | |||||
template <size_t N, class Iter> | |||||
requires (N != 0) | |||||
constexpr auto wrap_n_times(Iter i) { | |||||
return std::make_reverse_iterator(wrap_n_times<N - 1>(i)); | |||||
} | |||||
static_assert(std::is_same_v<decltype(wrap_n_times<2>(std::declval<int*>())), | |||||
std::reverse_iterator<std::reverse_iterator<int*>>>); | |||||
struct NonTrivialMoveAssignment { | |||||
int i; | |||||
constexpr NonTrivialMoveAssignment() = default; | |||||
constexpr NonTrivialMoveAssignment(int set_i) : i(set_i) {} | |||||
constexpr NonTrivialMoveAssignment(NonTrivialMoveAssignment&& rhs) = default; | |||||
constexpr NonTrivialMoveAssignment& operator=(NonTrivialMoveAssignment&& rhs) noexcept { | |||||
i = rhs.i; | |||||
return *this; | |||||
} | |||||
constexpr friend bool operator==(const NonTrivialMoveAssignment&, const NonTrivialMoveAssignment&) = default; | |||||
}; | |||||
static_assert(!std::is_trivially_move_assignable_v<NonTrivialMoveAssignment>); | |||||
static_assert(!std::is_trivially_assignable<NonTrivialMoveAssignment&, NonTrivialMoveAssignment&>::value); | |||||
struct NonTrivialMoveCtr { | |||||
int i; | |||||
constexpr NonTrivialMoveCtr() = default; | |||||
constexpr NonTrivialMoveCtr(int set_i) : i(set_i) {} | |||||
constexpr NonTrivialMoveCtr(NonTrivialMoveCtr&& rhs) noexcept : i(rhs.i) {} | |||||
constexpr NonTrivialMoveCtr& operator=(NonTrivialMoveCtr&& rhs) = default; | |||||
constexpr friend bool operator==(const NonTrivialMoveCtr&, const NonTrivialMoveCtr&) = default; | |||||
}; | |||||
static_assert(std::is_trivially_move_assignable_v<NonTrivialMoveCtr>); | |||||
static_assert(!std::is_trivially_copyable_v<NonTrivialMoveCtr>); | |||||
struct NonTrivialCopyAssignment { | |||||
int i; | |||||
constexpr NonTrivialCopyAssignment() = default; | |||||
constexpr NonTrivialCopyAssignment(int set_i) : i(set_i) {} | |||||
constexpr NonTrivialCopyAssignment(const NonTrivialCopyAssignment& rhs) = default; | |||||
constexpr NonTrivialCopyAssignment& operator=(const NonTrivialCopyAssignment& rhs) { | |||||
i = rhs.i; | |||||
return *this; | |||||
} | |||||
constexpr friend bool operator==(const NonTrivialCopyAssignment&, const NonTrivialCopyAssignment&) = default; | |||||
}; | |||||
static_assert(!std::is_trivially_copy_assignable_v<NonTrivialCopyAssignment>); | |||||
struct NonTrivialCopyCtr { | |||||
int i; | |||||
constexpr NonTrivialCopyCtr() = default; | |||||
constexpr NonTrivialCopyCtr(int set_i) : i(set_i) {} | |||||
constexpr NonTrivialCopyCtr(const NonTrivialCopyCtr& rhs) : i(rhs.i) {} | |||||
constexpr NonTrivialCopyCtr& operator=(const NonTrivialCopyCtr& rhs) = default; | |||||
constexpr friend bool operator==(const NonTrivialCopyCtr&, const NonTrivialCopyCtr&) = default; | |||||
}; | |||||
static_assert(std::is_trivially_copy_assignable_v<NonTrivialCopyCtr>); | |||||
static_assert(!std::is_trivially_copyable_v<NonTrivialCopyCtr>); | |||||
template <class InIter, template <class> class SentWrapper, class OutIter, size_t W1, size_t W2, class Func> | |||||
constexpr void test_one(Func func) { | |||||
using From = typename std::iterator_traits<InIter>::value_type; | |||||
using To = typename std::iterator_traits<OutIter>::value_type; | |||||
{ | |||||
const size_t N = 4; | |||||
From input[N] = {From(1), From(2), From(3), From(4)}; | |||||
To output[N]; | |||||
auto in = wrap_n_times<W1>(InIter(input)); | |||||
auto in_end = wrap_n_times<W1>(InIter(input + N)); | |||||
auto sent = SentWrapper<decltype(in_end)>(in_end); | |||||
auto out = wrap_n_times<W2>(OutIter(output)); | |||||
func(in, sent, out, N); | |||||
if constexpr (!std::same_as<To, bool>) { | |||||
assert(std::equal(input, input + N, output)); | |||||
} else { | |||||
assert(std::all_of(output, output + N, [](bool e) { return e; })); | |||||
} | |||||
} | |||||
{ | |||||
const size_t N = 0; | |||||
From input[1] = {1}; | |||||
To output[1] = {To(2)}; | |||||
auto in = wrap_n_times<W1>(InIter(input)); | |||||
auto in_end = wrap_n_times<W1>(InIter(input + N)); | |||||
auto sent = SentWrapper<decltype(in_end)>(in_end); | |||||
auto out = wrap_n_times<W2>(OutIter(output)); | |||||
func(in, sent, out, N); | |||||
assert(output[0] == To(2)); | |||||
} | |||||
} | |||||
template <class InIter, template <class> class SentWrapper, class OutIter, size_t W1, size_t W2> | |||||
constexpr void test_copy() { | |||||
// Classic. | |||||
if constexpr (std::same_as<InIter, SentWrapper<InIter>>) { | |||||
test_one<InIter, SentWrapper, OutIter, W1, W2>([](auto first, auto last, auto out, size_t) { | |||||
std::copy(first, last, out); | |||||
}); | |||||
test_one<InIter, SentWrapper, OutIter, W1, W2>([](auto first, auto last, auto out, size_t n) { | |||||
std::copy_backward(first, last, out + n); | |||||
}); | |||||
test_one<InIter, SentWrapper, OutIter, W1, W2>([](auto first, auto, auto out, size_t n) { | |||||
std::copy_n(first, n, out); | |||||
}); | |||||
} | |||||
// Ranges. | |||||
test_one<InIter, SentWrapper, OutIter, W1, W2>([](auto first, auto last, auto out, size_t) { | |||||
std::ranges::copy(first, last, out); | |||||
}); | |||||
test_one<InIter, SentWrapper, OutIter, W1, W2>([](auto first, auto last, auto out, size_t n) { | |||||
std::ranges::copy_backward(first, last, out + n); | |||||
}); | |||||
test_one<InIter, SentWrapper, OutIter, W1, W2>([](auto first, auto, auto out, size_t n) { | |||||
std::ranges::copy_n(first, n, out); | |||||
}); | |||||
} | |||||
template <class InIter, template <class> class SentWrapper, class OutIter, size_t W1, size_t W2> | |||||
constexpr void test_move() { | |||||
if constexpr (std::same_as<InIter, SentWrapper<InIter>>) { | |||||
test_one<InIter, SentWrapper, OutIter, W1, W2>([](auto first, auto last, auto out, size_t) { | |||||
std::move(first, last, out); | |||||
}); | |||||
test_one<InIter, SentWrapper, OutIter, W1, W2>([](auto first, auto last, auto out, size_t n) { | |||||
std::move_backward(first, last, out + n); | |||||
}); | |||||
} | |||||
// Ranges. | |||||
test_one<InIter, SentWrapper, OutIter, W1, W2>([](auto first, auto last, auto out, size_t) { | |||||
std::ranges::move(first, last, out); | |||||
}); | |||||
test_one<InIter, SentWrapper, OutIter, W1, W2>([](auto first, auto last, auto out, size_t n) { | |||||
std::ranges::move_backward(first, last, out + n); | |||||
}); | |||||
} | |||||
template <size_t W1, size_t W2, class T, class U = T> | |||||
constexpr void test_copy_with_type() { | |||||
using FromIter = contiguous_iterator<T*>; | |||||
using ToIter = contiguous_iterator<U*>; | |||||
test_copy<FromIter, std::type_identity_t, ToIter, W1, W2>(); | |||||
test_copy<FromIter, sized_sentinel, ToIter, W1, W2>(); | |||||
test_copy<FromIter, std::type_identity_t, U*, W1, W2>(); | |||||
test_copy<T*, std::type_identity_t, U*, W1, W2>(); | |||||
test_copy<T*, std::type_identity_t, ToIter, W1, W2>(); | |||||
} | |||||
template <size_t W1, size_t W2, class T, class U = T> | |||||
constexpr void test_move_with_type() { | |||||
using FromIter = contiguous_iterator<T*>; | |||||
using ToIter = contiguous_iterator<U*>; | |||||
test_move<FromIter, std::type_identity_t, ToIter, W1, W2>(); | |||||
test_move<FromIter, sized_sentinel, ToIter, W1, W2>(); | |||||
test_move<FromIter, std::type_identity_t, U*, W1, W2>(); | |||||
test_move<T*, std::type_identity_t, U*, W1, W2>(); | |||||
test_move<T*, std::type_identity_t, ToIter, W1, W2>(); | |||||
} | |||||
template <size_t W1, size_t W2, class T, class U> | |||||
constexpr void test_both_directions() { | |||||
test_copy_with_type<W1, W2, T, U>(); | |||||
test_move_with_type<W1, W2, T, U>(); | |||||
test_copy_with_type<W1, W2, U, T>(); | |||||
test_move_with_type<W1, W2, U, T>(); | |||||
} | |||||
template <size_t W1, size_t W2> | |||||
constexpr void test_copy_and_move() { | |||||
test_copy_with_type<W1, W2, NonTrivialCopyAssignment>(); | |||||
test_move_with_type<W1, W2, NonTrivialMoveAssignment>(); | |||||
// Copying from a smaller type into a larger type and vice versa. | |||||
test_both_directions<W1, W2, char, int>(); | |||||
// Copying between types with different representations. | |||||
test_both_directions<W1, W2, int, float>(); | |||||
test_both_directions<W1, W2, bool, char>(); | |||||
} | |||||
constexpr bool test() { | |||||
test_copy_and_move<0, 0>(); | |||||
test_copy_and_move<0, 2>(); | |||||
test_copy_and_move<2, 0>(); | |||||
test_copy_and_move<2, 2>(); | |||||
test_copy_and_move<2, 4>(); | |||||
test_copy_and_move<4, 4>(); | |||||
return true; | |||||
} | |||||
int main(int, char**) { | |||||
test(); | |||||
static_assert(test()); | |||||
return 0; | |||||
} |