-
Notifications
You must be signed in to change notification settings - Fork 13.9k
[libc++][C++03] Split libc++-specific tests for the frozen headers #144093
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
You can test this locally with the following command:darker --check --diff -r HEAD~1...HEAD libcxx/test/libcxx-03/clang_modules_include.gen.py libcxx/test/libcxx-03/clang_tidy.gen.py libcxx/test/libcxx-03/clang_tidy.sh.py libcxx/test/libcxx-03/double_include.gen.py libcxx/test/libcxx-03/feature_test_macro/ftm_metadata.sh.py libcxx/test/libcxx-03/feature_test_macro/generate_header_test.sh.py libcxx/test/libcxx-03/feature_test_macro/implemented_ftms.sh.py libcxx/test/libcxx-03/feature_test_macro/invalid.sh.py libcxx/test/libcxx-03/feature_test_macro/is_implemented.sh.py libcxx/test/libcxx-03/feature_test_macro/standard_ftms.sh.py libcxx/test/libcxx-03/feature_test_macro/standard_library_headers.sh.py libcxx/test/libcxx-03/feature_test_macro/std_dialects.sh.py libcxx/test/libcxx-03/feature_test_macro/version_header.sh.py libcxx/test/libcxx-03/feature_test_macro/version_header_implementation.sh.py libcxx/test/libcxx-03/gdb/gdb_pretty_printer_test.py libcxx/test/libcxx-03/header_inclusions.gen.py libcxx/test/libcxx-03/headers_in_modulemap.sh.py libcxx/test/libcxx-03/libcpp_version.gen.py libcxx/test/libcxx-03/lint/lint_cmakelists.sh.py libcxx/test/libcxx-03/lint/lint_headers.sh.py libcxx/test/libcxx-03/module_std.gen.py libcxx/test/libcxx-03/module_std_compat.gen.py libcxx/test/libcxx-03/no_assert_include.gen.py libcxx/test/libcxx-03/selftest/dsl/dsl.sh.py libcxx/test/libcxx-03/system_reserved_names.gen.py libcxx/test/libcxx-03/transitive_includes.gen.py libcxx/test/libcxx-03/transitive_includes/to_csv.py libcxx/test/libcxx-03/xopen_source.gen.py View the diff from darker here.--- clang_modules_include.gen.py 2025-06-13 15:24:09.000000 +0000
+++ clang_modules_include.gen.py 2025-06-13 15:29:32.426876 +0000
@@ -14,10 +14,11 @@
# block Lit from interpreting a RUN/XFAIL/etc inside the generation script
# END.
import sys
+
sys.path.append(sys.argv[1])
from libcxx.header_information import (
lit_header_restrictions,
lit_header_undeprecations,
public_headers,
--- clang_tidy.gen.py 2025-06-13 15:24:09.000000 +0000
+++ clang_tidy.gen.py 2025-06-13 15:29:32.435253 +0000
@@ -13,15 +13,21 @@
# block Lit from interpreting a RUN/XFAIL/etc inside the generation script
# END.
import sys
+
sys.path.append(sys.argv[1])
-from libcxx.header_information import lit_header_restrictions, lit_header_undeprecations, public_headers
+from libcxx.header_information import (
+ lit_header_restrictions,
+ lit_header_undeprecations,
+ public_headers,
+)
for header in public_headers:
- print(f"""\
+ print(
+ f"""\
//--- {header}.sh.cpp
// REQUIRES: has-clang-tidy
// The frozen headers should not be updated to the latest libc++ style, so don't test.
@@ -35,6 +41,7 @@
// TODO: run clang-tidy with modules enabled once they are supported
// RUN: %{{clang-tidy}} %s --warnings-as-errors=* -header-filter=.* --config-file=%{{libcxx-dir}}/.clang-tidy --load=%{{test-tools-dir}}/clang_tidy_checks/libcxx-tidy.plugin -- -Wweak-vtables %{{compile_flags}} -fno-modules
#include <{header}>
-""")
+"""
+ )
--- double_include.gen.py 2025-06-13 15:24:09.000000 +0000
+++ double_include.gen.py 2025-06-13 15:29:32.448903 +0000
@@ -12,10 +12,11 @@
# Block Lit from interpreting a RUN/XFAIL/etc inside the generation script.
# END.
import sys
+
sys.path.append(sys.argv[1])
from libcxx.header_information import (
lit_header_restrictions,
lit_header_undeprecations,
public_headers,
--- feature_test_macro/implemented_ftms.sh.py 2025-06-13 15:24:09.000000 +0000
+++ feature_test_macro/implemented_ftms.sh.py 2025-06-13 15:29:32.501056 +0000
@@ -23,11 +23,10 @@
def setUp(self):
self.ftm = FeatureTestMacros(TEST_DATA, ["charconv"])
self.maxDiff = None # This causes the diff to be printed when the test fails
def test_implementation(self):
-
expected = {
"__cpp_lib_any": {
"c++17": "201606L",
"c++20": "201606L",
"c++23": "201606L",
--- headers_in_modulemap.sh.py 2025-06-13 15:24:09.000000 +0000
+++ headers_in_modulemap.sh.py 2025-06-13 15:29:32.633182 +0000
@@ -1,8 +1,9 @@
# RUN: %{python} %s %{libcxx-dir}/utils
import sys
+
sys.path.append(sys.argv[1])
from libcxx.header_information import all_headers, libcxx_include
with open(libcxx_include / "module.modulemap.in") as f:
modulemap = f.read()
--- libcpp_version.gen.py 2025-06-13 15:24:09.000000 +0000
+++ libcpp_version.gen.py 2025-06-13 15:29:32.641292 +0000
@@ -9,10 +9,11 @@
# Test that all headers define the _LIBCPP_VERSION macro.
# RUN: %{python} %s %{libcxx-dir}/utils
import sys
+
sys.path.append(sys.argv[1])
from libcxx.header_information import (
lit_header_restrictions,
lit_header_undeprecations,
public_headers,
--- selftest/dsl/dsl.sh.py 2025-06-13 15:24:09.000000 +0000
+++ selftest/dsl/dsl.sh.py 2025-06-13 15:29:32.868638 +0000
@@ -308,11 +308,11 @@
def test_basic(self):
macros = dsl.compilerMacros(self.config)
self.assertIsInstance(macros, dict)
self.assertGreater(len(macros), 0)
- for (k, v) in macros.items():
+ for k, v in macros.items():
self.assertIsInstance(k, str)
self.assertIsInstance(v, str)
def test_no_flag(self):
macros = dsl.compilerMacros(self.config)
@@ -341,11 +341,11 @@
def test_basic(self):
macros = dsl.featureTestMacros(self.config)
self.assertIsInstance(macros, dict)
self.assertGreater(len(macros), 0)
- for (k, v) in macros.items():
+ for k, v in macros.items():
self.assertIsInstance(k, str)
self.assertIsInstance(v, int)
class TestFeature(SetupConfigs):
--- transitive_includes/to_csv.py 2025-06-13 15:24:09.000000 +0000
+++ transitive_includes/to_csv.py 2025-06-13 15:29:32.920269 +0000
@@ -14,13 +14,16 @@
import os
import pathlib
import re
import sys
-libcxx_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
+libcxx_root = os.path.dirname(
+ os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+)
sys.path.append(os.path.join(libcxx_root, "utils"))
from libcxx.header_information import Header
+
def parse_line(line: str) -> Tuple[int, str]:
"""
Parse a single line of --trace-includes output.
@@ -31,10 +34,11 @@
raise ArgumentError(f"Line {line} contains invalid data.")
# The number of periods in front of the header name is the nesting level of
# that header.
return (len(match.group(1)), match.group(2))
+
def make_cxx_v1_relative(header: str) -> Optional[str]:
"""
Returns the path of the header as relative to <whatever>/c++/v1, or None if the path
doesn't contain c++/v1.
@@ -51,10 +55,11 @@
match = re.match(CXX_V1_REGEX, header)
if not match:
return None
else:
return match.group(1)
+
def parse_file(file: io.TextIOBase) -> List[Tuple[Header, Header]]:
"""
Parse a file containing --trace-includes output to generate a list of the
transitive includes contained in it.
@@ -79,25 +84,27 @@
else:
assert includer is not None
result.append((includer, Header(relative)))
return result
+
def print_csv(includes: List[Tuple[Header, Header]]) -> None:
"""
Print the transitive includes as space-delimited CSV.
This function only prints public libc++ headers that are not C compatibility headers.
"""
# Sort and group by includer
by_includer = lambda t: t[0]
includes = itertools.groupby(sorted(includes, key=by_includer), key=by_includer)
- for (includer, includees) in includes:
+ for includer, includees in includes:
includees = map(lambda t: t[1], includees)
for h in sorted(set(includees)):
if h.is_public() and not h.is_C_compatibility():
print(f"{includer} {h}")
+
def main(argv):
parser = argparse.ArgumentParser(
description="""
Given a list of headers produced by --trace-includes, produce a list of libc++ headers in that output.
@@ -106,15 +113,22 @@
information for this script to run.
The output of this script is provided in space-delimited CSV format where each line contains:
<header performing inclusion> <header being included>
- """)
- parser.add_argument("inputs", type=argparse.FileType("r"), nargs='+', default=None,
- help="One or more files containing the result of --trace-includes")
+ """
+ )
+ parser.add_argument(
+ "inputs",
+ type=argparse.FileType("r"),
+ nargs="+",
+ default=None,
+ help="One or more files containing the result of --trace-includes",
+ )
args = parser.parse_args(argv)
includes = [line for file in args.inputs for line in parse_file(file)]
print_csv(includes)
+
if __name__ == "__main__":
main(sys.argv[1:])
--- transitive_includes.gen.py 2025-06-13 15:24:09.000000 +0000
+++ transitive_includes.gen.py 2025-06-13 15:29:32.937220 +0000
@@ -20,10 +20,11 @@
# block Lit from interpreting a RUN/XFAIL/etc inside the generation script
# END.
import sys
+
sys.path.append(sys.argv[1])
from libcxx.header_information import lit_header_restrictions, public_headers
import re
|
@llvm/pr-subscribers-libcxx Author: Nikolas Klauser (philnik777) ChangesThe C++03 headers are essentially a separate implementation, so it doesn't make a ton of sense to try to test two implementations with a single set of implementation-specific tests. Most of the tests will be removed in a follow-up, since they don't run in C++03 mode. This patch adds them to make as few changes to the copy as possible. The most notable changes are that This also modifies This is part of https://discourse.llvm.org/t/rfc-freezing-c-03-headers-in-libc. Patch is 2.92 MiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/144093.diff 921 Files Affected:
diff --git a/libcxx/test/libcxx-03/Wnon_modular_include_in_module.compile.pass.cpp b/libcxx/test/libcxx-03/Wnon_modular_include_in_module.compile.pass.cpp
new file mode 100644
index 0000000000000..aa7a6d98d7d68
--- /dev/null
+++ b/libcxx/test/libcxx-03/Wnon_modular_include_in_module.compile.pass.cpp
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: target={{.*}}-apple-{{.*}}
+// UNSUPPORTED: c++03
+
+// This test ensures that libc++ supports being compiled with modules enabled and with
+// -Wnon-modular-include-in-module. This effectively checks that we don't include any
+// non-modular header from the library.
+//
+// Since most underlying platforms are not modularized properly, this test currently only
+// works on Apple platforms.
+
+// ADDITIONAL_COMPILE_FLAGS: -Wnon-modular-include-in-module -Wsystem-headers-in-module=std -fmodules -fcxx-modules
+
+#include <vector>
diff --git a/libcxx/test/libcxx-03/algorithms/alg.modifying.operations/alg.random.shuffle/random_shuffle.cxx1z.pass.cpp b/libcxx/test/libcxx-03/algorithms/alg.modifying.operations/alg.random.shuffle/random_shuffle.cxx1z.pass.cpp
new file mode 100644
index 0000000000000..4e51014f20b18
--- /dev/null
+++ b/libcxx/test/libcxx-03/algorithms/alg.modifying.operations/alg.random.shuffle/random_shuffle.cxx1z.pass.cpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// template <class RandomAccessIterator>
+// void
+// random_shuffle(RandomAccessIterator first, RandomAccessIterator last);
+//
+// template <class RandomAccessIterator, class RandomNumberGenerator>
+// void
+// random_shuffle(RandomAccessIterator first, RandomAccessIterator last,
+// RandomNumberGenerator& rand);
+
+//
+// In C++17, random_shuffle has been removed.
+// However, for backwards compatibility, if _LIBCPP_ENABLE_CXX17_REMOVED_RANDOM_SHUFFLE
+// is defined before including <algorithm>, then random_shuffle will be restored.
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX17_REMOVED_RANDOM_SHUFFLE
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_DISABLE_DEPRECATION_WARNINGS
+
+#include <algorithm>
+#include <cstddef>
+#include <vector>
+
+#include "test_macros.h"
+
+struct gen
+{
+ std::ptrdiff_t operator()(std::ptrdiff_t n)
+ {
+ return n-1;
+ }
+};
+
+
+int main(int, char**)
+{
+ std::vector<int> v;
+ std::random_shuffle(v.begin(), v.end());
+ gen r;
+ std::random_shuffle(v.begin(), v.end(), r);
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx-03/algorithms/alg.modifying.operations/alg.random.shuffle/random_shuffle.depr_in_cxx14.verify.cpp b/libcxx/test/libcxx-03/algorithms/alg.modifying.operations/alg.random.shuffle/random_shuffle.depr_in_cxx14.verify.cpp
new file mode 100644
index 0000000000000..057f126a93cfe
--- /dev/null
+++ b/libcxx/test/libcxx-03/algorithms/alg.modifying.operations/alg.random.shuffle/random_shuffle.depr_in_cxx14.verify.cpp
@@ -0,0 +1,43 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <algorithm>
+
+// template <class RandomAccessIterator>
+// void
+// random_shuffle(RandomAccessIterator first, RandomAccessIterator last);
+//
+// template <class RandomAccessIterator, class RandomNumberGenerator>
+// void
+// random_shuffle(RandomAccessIterator first, RandomAccessIterator last,
+// RandomNumberGenerator& rand);
+
+// UNSUPPORTED: c++03, c++11
+
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_CXX17_REMOVED_RANDOM_SHUFFLE
+
+#include <algorithm>
+#include <cstddef>
+
+#include "test_macros.h"
+
+struct gen
+{
+ std::ptrdiff_t operator()(std::ptrdiff_t n)
+ {
+ return n-1;
+ }
+};
+
+
+void f() {
+ int v[1] = {1};
+ std::random_shuffle(&v[0], &v[1]); // expected-warning {{'random_shuffle<int *>' is deprecated}}
+ gen r;
+ std::random_shuffle(&v[0], &v[1], r); // expected-warning {{'random_shuffle<int *, gen &>' is deprecated}}
+}
diff --git a/libcxx/test/libcxx-03/algorithms/alg.modifying.operations/copy_move_nontrivial.pass.cpp b/libcxx/test/libcxx-03/algorithms/alg.modifying.operations/copy_move_nontrivial.pass.cpp
new file mode 100644
index 0000000000000..0c5ae84d97700
--- /dev/null
+++ b/libcxx/test/libcxx-03/algorithms/alg.modifying.operations/copy_move_nontrivial.pass.cpp
@@ -0,0 +1,331 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// In the modules build, adding another overload of `memmove` doesn't work.
+// UNSUPPORTED: clang-modules-build
+// GCC complains about "ambiguating" `__builtin_memmove`.
+// UNSUPPORTED: gcc
+
+// <algorithm>
+
+#include <cassert>
+#include <cstddef>
+
+// These tests check 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*, std::size_t) {
+ assert(false);
+ return nullptr;
+}
+
+#include <algorithm>
+#include <cassert>
+#include <cstdint>
+#include <iterator>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+
+// S1 and S2 are simple structs that are convertible to each other and have the same bit representation.
+struct S1 {
+ int x;
+
+ constexpr S1() = default;
+ constexpr S1(int set_x) : x(set_x) {}
+
+ friend constexpr bool operator==(const S1& lhs, const S1& rhs) { return lhs.x == rhs.x; }
+};
+
+struct S2 {
+ int x;
+
+ constexpr S2() = default;
+ constexpr S2(int set_x) : x(set_x) {}
+ constexpr S2(S1 from) : x(from.x) {}
+
+ friend constexpr bool operator==(const S1& lhs, const S2& rhs) { return lhs.x == rhs.x; }
+ friend constexpr bool operator==(const S2& lhs, const S2& rhs) { return lhs.x == rhs.x; }
+};
+
+// U1 and U2 are simple unions that are convertible to each other and have the same bit representation.
+union U1 {
+ int x;
+
+ constexpr U1() = default;
+ constexpr U1(int set_x) : x(set_x) {}
+
+ friend constexpr bool operator==(const U1& lhs, const U1& rhs) { return lhs.x == rhs.x; }
+};
+
+union U2 {
+ int x;
+
+ constexpr U2() = default;
+ constexpr U2(int set_x) : x(set_x) {}
+ constexpr U2(U1 from) : x(from.x) {}
+
+ friend constexpr bool operator==(const U1& lhs, const U2& rhs) { return lhs.x == rhs.x; }
+ friend constexpr bool operator==(const U2& lhs, const U2& rhs) { return lhs.x == rhs.x; }
+};
+
+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 T>
+constexpr T make(int from) {
+ return T(from);
+}
+
+template <typename PtrT, typename T = std::remove_pointer_t<PtrT>>
+static T make_internal_array[5] = {T(), T(), T(), T(), T()};
+
+template <class T>
+requires std::is_pointer_v<T>
+constexpr T make(int i) {
+ if constexpr (!std::same_as<std::remove_pointer_t<T>, void>) {
+ return make_internal_array<T> + i;
+ } else {
+ return make_internal_array<int> + i;
+ }
+}
+
+template <class InIter, template <class> class SentWrapper, class OutIter, 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 std::size_t N = 5;
+
+ From input[N] = {make<From>(0), make<From>(1), make<From>(2), make<From>(3), make<From>(4)};
+ To output[N];
+
+ auto in = InIter(input);
+ auto in_end = InIter(input + N);
+ auto sent = SentWrapper<decltype(in_end)>(in_end);
+ auto out = OutIter(output);
+
+ func(in, sent, out, N);
+ if constexpr (!std::same_as<To, bool>) {
+ assert(std::equal(input, input + N, output));
+ } else {
+ bool expected[N] = {false, true, true, true, true};
+ assert(std::equal(output, output + N, expected));
+ }
+ }
+
+ {
+ const std::size_t N = 0;
+
+ From input[1] = {make<From>(1)};
+ To output[1] = {make<To>(2)};
+
+ auto in = InIter(input);
+ auto in_end = InIter(input + N);
+ auto sent = SentWrapper<decltype(in_end)>(in_end);
+ auto out = OutIter(output);
+
+ func(in, sent, out, N);
+ assert(output[0] == make<To>(2));
+ }
+}
+
+template <class InIter, template <class> class SentWrapper, class OutIter>
+constexpr void test_copy() {
+ // Classic.
+ if constexpr (std::same_as<InIter, SentWrapper<InIter>>) {
+ test_one<InIter, SentWrapper, OutIter>([](auto first, auto last, auto out, std::size_t) {
+ std::copy(first, last, out);
+ });
+ test_one<InIter, SentWrapper, OutIter>([](auto first, auto last, auto out, std::size_t n) {
+ std::copy_backward(first, last, out + n);
+ });
+ test_one<InIter, SentWrapper, OutIter>([](auto first, auto, auto out, std::size_t n) {
+ std::copy_n(first, n, out);
+ });
+ }
+
+ // Ranges.
+ test_one<InIter, SentWrapper, OutIter>([](auto first, auto last, auto out, std::size_t) {
+ std::ranges::copy(first, last, out);
+ });
+ test_one<InIter, SentWrapper, OutIter>([](auto first, auto last, auto out, std::size_t n) {
+ std::ranges::copy_backward(first, last, out + n);
+ });
+ test_one<InIter, SentWrapper, OutIter>([](auto first, auto, auto out, std::size_t n) {
+ std::ranges::copy_n(first, n, out);
+ });
+}
+
+template <class InIter, template <class> class SentWrapper, class OutIter>
+constexpr void test_move() {
+ if constexpr (std::same_as<InIter, SentWrapper<InIter>>) {
+ test_one<InIter, SentWrapper, OutIter>([](auto first, auto last, auto out, std::size_t) {
+ std::move(first, last, out);
+ });
+ test_one<InIter, SentWrapper, OutIter>([](auto first, auto last, auto out, std::size_t n) {
+ std::move_backward(first, last, out + n);
+ });
+ }
+
+ // Ranges.
+ test_one<InIter, SentWrapper, OutIter>([](auto first, auto last, auto out, std::size_t) {
+ std::ranges::move(first, last, out);
+ });
+ test_one<InIter, SentWrapper, OutIter>([](auto first, auto last, auto out, std::size_t n) {
+ std::ranges::move_backward(first, last, out + n);
+ });
+}
+
+template <class From, class To = From>
+constexpr void test_copy_with_type() {
+ using FromIter = contiguous_iterator<From*>;
+ using ToIter = contiguous_iterator<To*>;
+
+ test_copy<FromIter, std::type_identity_t, ToIter>();
+ test_copy<FromIter, sized_sentinel, ToIter>();
+ test_copy<FromIter, std::type_identity_t, To*>();
+ test_copy<From*, std::type_identity_t, To*>();
+ test_copy<From*, std::type_identity_t, ToIter>();
+}
+
+template <class From, class To = From>
+constexpr void test_move_with_type() {
+ using FromIter = contiguous_iterator<From*>;
+ using ToIter = contiguous_iterator<To*>;
+
+ test_move<FromIter, std::type_identity_t, ToIter>();
+ test_move<FromIter, sized_sentinel, ToIter>();
+ test_move<FromIter, std::type_identity_t, To*>();
+ test_move<From*, std::type_identity_t, To*>();
+ test_move<From*, std::type_identity_t, ToIter>();
+}
+
+template <class From, class To>
+constexpr void test_copy_and_move() {
+ test_copy_with_type<From, To>();
+ test_move_with_type<From, To>();
+}
+
+template <class From, class To>
+constexpr void test_both_directions() {
+ test_copy_and_move<From, To>();
+ if (!std::same_as<From, To>) {
+ test_copy_and_move<To, From>();
+ }
+}
+
+constexpr bool test() {
+ test_copy_with_type<NonTrivialCopyAssignment>();
+ test_move_with_type<NonTrivialMoveAssignment>();
+
+ // Copying from a smaller type into a larger type and vice versa.
+ test_both_directions<char, int>();
+ test_both_directions<std::int32_t, std::int64_t>();
+
+ // Copying between types with different representations.
+ test_both_directions<int, float>();
+ // Copying from `bool` to `char` will invoke the optimization, so only check one direction.
+ test_copy_and_move<char, bool>();
+
+ // Copying between different structs with the same representation (there is no way to guarantee the representation is
+ // the same).
+ test_copy_and_move<S1, S2>();
+ // Copying between different unions with the same representation.
+ test_copy_and_move<U1, U2>();
+
+ // Copying from a regular pointer to a void pointer (these are not considered trivially copyable).
+ test_copy_and_move<int*, void*>();
+ // Copying from a non-const pointer to a const pointer (these are not considered trivially copyable).
+ test_copy_and_move<int*, const int*>();
+
+ // `memmove` does not support volatile pointers.
+ // (See also https://github.com/llvm/llvm-project/issues/28901).
+ if (!std::is_constant_evaluated()) {
+ test_both_directions<volatile int, int>();
+ test_both_directions<volatile int, volatile int>();
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx-03/algorithms/alg.modifying.operations/copy_move_trivial.pass.cpp b/libcxx/test/libcxx-03/algorithms/alg.modifying.operations/copy_move_trivial.pass.cpp
new file mode 100644
index 0000000000000..ff10c7919200d
--- /dev/null
+++ b/libcxx/test/libcxx-03/algorithms/alg.modifying.operations/copy_move_trivial.pass.cpp
@@ -0,0 +1,334 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// In the modules build, adding another overload of `memmove` doesn't work.
+// UNSUPPORTED: clang-modules-build
+// GCC complains about "ambiguating" `__builtin_memmove`.
+// UNSUPPORTED: gcc
+
+// <algorithm>
+
+// These tests check that `std::copy` and `std::move` (including their variations like `copy_n`) forward to
+// `memmove` when possible.
+
+#include <cstddef>
+
+struct Foo {
+ int i = 0;
+
+ Foo() = default;
+ Foo(int set_i) : i(set_i) {}
+
+ friend bool operator==(const Foo&, const Foo&) = default;
+};
+
+static bool memmove_called = false;
+
+// 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* dst, Src* src, std::size_t count) {
+ memmove_called = true;
+ return __builtin_memmove(static_cast<void*>(dst), static_cast<const void*>(src), count);
+}
+
+#include <algorithm>
+#include <cassert>
+#include <cstdint>
+#include <iterator>
+#include <limits>
+#include <ranges>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+static_assert(std::is_trivially_copyable_v<Foo>);
+
+// To test pointers to functions.
+void Func() {}
+using FuncPtr = decltype(&Func);
+
+// To test pointers to members.
+struct S {
+ int mem_obj = 0;
+ void MemFunc() {}
+};
+using MemObjPtr = decltype(&S::mem_obj);
+using MemFuncPtr = decltype(&S::MemFunc);
+
+// To test bitfields.
+struct BitfieldS {
+ unsigned char b1 : 3;
+ unsigned char : 2;
+ unsigned char b2 : 5;
+ friend bool operator==(const BitfieldS&, const BitfieldS&) = default;
+};
+
+// To test non-default alignment.
+struct AlignedS {
+ alignas(64) int x;
+ alignas(8) int y;
+ friend bool operator==(const AlignedS&, const AlignedS&) = default;
+};
+
+template <class T>
+T make(int from) {
+ return T(from);
+}
+
+template <class T>
+requires (std::is_pointer_v<T> && !std::is_function_v<std::remove_pointer_t<T>>)
+T make(int i) {
+ static std::remove_pointer_t<T> arr[8];
+ return arr + i;
+}
+
+template <class T>
+requires std::same_as<T, FuncPtr>
+FuncPtr make(int) {
+ return &Func;
+}
+
+template <class T>
+requires std::same_as<T, MemObjPtr>
+MemObjPtr make(int) {
+ return &S::mem_obj;
+}
+
+template <class T>
+requires std::same_as<T, MemFuncPtr>
+MemFuncPtr make(int) {
+ return &S::MemFunc;
+}
+
+template <class T>
+requires std::same_as<T, BitfieldS>
+BitfieldS make(int x) {
+ BitfieldS result = {};
+ result.b1 = x;
+ result.b2 = x;
+ return result;
+}
+
+template <class T>
+requires std::same_as<T, AlignedS>
+AlignedS make(int x) {
+ AlignedS result;
+ result.x = x;
+ result.y = x;
+ return result;
+}
+
+template <class InIter, template <class> class SentWrapper, class OutIter, class Func>
+void test_one(Func func) {
+ using From = std::iter_value_t<InIter>;
+ using To = std::iter_value_t<OutIter>;
+
+ // Normal case.
+ {
+ const std::size_t N = 4;
+
+ From input[N] = {make<From>(1), make<From>(2), make<From>(3), make<From>(4)...
[truncated]
|
The C++03 headers are essentially a separate implementation, so it doesn't make a ton of sense to try to test two implementations with a single set of implementation-specific tests.
Most of the tests will be removed in a follow-up, since they don't run in C++03 mode. This patch adds them to make as few changes to the copy as possible. The most notable changes are that
lit.local.cfg
files are touched to change the path fromlibcxx/test/libcxx
tolibcxx/test/libcxx-03
in a few places.This also modifies
lit.local.cfg
files to runlibcxx/test/libcxx-03
only when using the frozen headers andlbcxx/test/libcxx
tests only when not using the frozen headers.This is part of https://discourse.llvm.org/t/rfc-freezing-c-03-headers-in-libc.