diff options
Diffstat (limited to 'libstdc++-v3/testsuite')
669 files changed, 35011 insertions, 1749 deletions
diff --git a/libstdc++-v3/testsuite/17_intro/headers/c++1998/49745.cc b/libstdc++-v3/testsuite/17_intro/headers/c++1998/49745.cc index 7fafe7b..3b9d2eb 100644 --- a/libstdc++-v3/testsuite/17_intro/headers/c++1998/49745.cc +++ b/libstdc++-v3/testsuite/17_intro/headers/c++1998/49745.cc @@ -131,5 +131,3 @@ #endif int truncate = 0; - -// { dg-xfail-if "PR libstdc++/99995" { c++20 } } diff --git a/libstdc++-v3/testsuite/17_intro/headers/c++1998/stdc++_assert_neg.cc b/libstdc++-v3/testsuite/17_intro/headers/c++1998/stdc++_assert_neg.cc index eb380c4..7ce90a2 100644 --- a/libstdc++-v3/testsuite/17_intro/headers/c++1998/stdc++_assert_neg.cc +++ b/libstdc++-v3/testsuite/17_intro/headers/c++1998/stdc++_assert_neg.cc @@ -1,8 +1,5 @@ // { dg-do compile } -// { dg-options "-D_GLIBCXX_NO_ASSERT" } // { dg-require-effective-target hosted } -// NB: This is done to force any generated and possibly included PCH -// to be invalid, and also to remove cassert from the include set. // 2005-05-24 bkoz diff --git a/libstdc++-v3/testsuite/17_intro/headers/c++2011/42319.cc b/libstdc++-v3/testsuite/17_intro/headers/c++2011/42319.cc index cd576ca..350a548 100644 --- a/libstdc++-v3/testsuite/17_intro/headers/c++2011/42319.cc +++ b/libstdc++-v3/testsuite/17_intro/headers/c++2011/42319.cc @@ -1,5 +1,5 @@ // { dg-do compile } -// { dg-options "-std=gnu++11" } +// { dg-add-options no_pch } // Copyright (C) 2009-2025 Free Software Foundation, Inc. // @@ -19,4 +19,4 @@ // <http://www.gnu.org/licenses/>. // libstdc++/42319 -#include <bits/char_traits.h> +#include <ios> diff --git a/libstdc++-v3/testsuite/17_intro/names.cc b/libstdc++-v3/testsuite/17_intro/names.cc index f67818d..befb5aa 100644 --- a/libstdc++-v3/testsuite/17_intro/names.cc +++ b/libstdc++-v3/testsuite/17_intro/names.cc @@ -248,6 +248,10 @@ #undef r #undef x #undef y +// <stdlib.h> defines drand48_data::a +#undef a +// <sys/localedef.h> defines _LC_weight_t::n +#undef n // <sys/poll.h> defines pollfd_ext::u on AIX 7.3 #undef u // <sys/var.h> defines vario::v @@ -282,6 +286,8 @@ // <sys/ucontext.h> defines fpreg_t::d and fpreg_t::f #undef d #undef f +// <asm/types.h> defines __vector128::u +#undef u #endif #if defined (__linux__) && defined (__sparc__) @@ -323,6 +329,7 @@ #ifdef __sun__ // <fenv.h> defines these as members of fex_numeric_t +#undef i #undef l #undef f #undef d @@ -332,8 +339,11 @@ #undef ptr // <sys/timespec_util.h> uses this as parameter #undef r -// <stdlib.h> uses this as member of drand48_data +// <stdlib.h> uses these as members of drand48_data +#undef a #undef x +// <string.h> defines this as a parameter of timingsafe_memcmp +#undef n #endif #ifdef __VXWORKS__ @@ -396,4 +406,8 @@ # endif #endif +// PR libstdc++/119496 +// _Temporary_buffer used to have a member with this name +#define requested_size 1 + #include <bits/stdc++.h> diff --git a/libstdc++-v3/testsuite/18_support/comparisons/categories/zero_neg.cc b/libstdc++-v3/testsuite/18_support/comparisons/categories/zero_neg.cc index 2e6b9c1..ce0ca8e 100644 --- a/libstdc++-v3/testsuite/18_support/comparisons/categories/zero_neg.cc +++ b/libstdc++-v3/testsuite/18_support/comparisons/categories/zero_neg.cc @@ -23,6 +23,16 @@ // C++20 [cmp.categories.pre] // "an argument other than a literal 0 is undefined" +struct PtrConv +{ + template<typename T> + consteval operator T*() + { return nullptr; } + + consteval operator std::nullptr_t() + { return nullptr; } +}; + void test01() { @@ -48,6 +58,12 @@ test01() std::partial_ordering::equivalent == nullptr; std::weak_ordering::equivalent == nullptr; std::strong_ordering::equivalent == nullptr; + + constexpr PtrConv c; + // requires two user-defined conversion + std::partial_ordering::equivalent == c; // { dg-error "no match for 'operator=='" } + std::weak_ordering::equivalent == c; // { dg-error "no match for 'operator=='" } + std::strong_ordering::equivalent == c; // { dg-error "no match for 'operator=='" } } // { dg-prune-output "reinterpret_cast.* is not a constant expression" } diff --git a/libstdc++-v3/testsuite/18_support/comparisons/type_order/1.cc b/libstdc++-v3/testsuite/18_support/comparisons/type_order/1.cc new file mode 100644 index 0000000..b510494 --- /dev/null +++ b/libstdc++-v3/testsuite/18_support/comparisons/type_order/1.cc @@ -0,0 +1,95 @@ +// Copyright (C) 2025 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-do compile { target c++26 } } + +#include <compare> + +#if __cpp_lib_type_order != 202506L +# error "__cpp_lib_type_order != 202506" +#endif + +static_assert (std::is_same_v <decltype (std::type_order <int, int>::value), + const std::strong_ordering>); +static_assert (std::is_same_v <decltype (std::type_order_v <char, short>), + const std::strong_ordering>); +struct S; +struct T; +template <typename T> +struct U +{ +}; +typedef int int2; +struct V {}; +namespace +{ + struct W {}; +} + +template <typename T, typename U> +struct eq +{ + constexpr eq () + { + static_assert (std::type_order <T, U>::value == std::strong_ordering::equal); + static_assert (std::type_order <U, T>::value == std::strong_ordering::equal); + static_assert (std::type_order_v <T, U> == std::strong_ordering::equal); + static_assert (std::type_order_v <U, T> == std::strong_ordering::equal); + } +}; +template <typename T, typename U> +struct ne +{ + constexpr ne () + { + static_assert (std::type_order <T, U>::value != std::strong_ordering::equal); + static_assert (std::type_order <U, T>::value != std::strong_ordering::equal); + static_assert (std::type_order <T, U>::value == std::strong_ordering::greater + ? std::type_order <U, T>::value == std::strong_ordering::less + : std::type_order <U, T>::value == std::strong_ordering::greater); + static_assert (std::type_order_v <T, U> != std::strong_ordering::equal); + static_assert (std::type_order_v <U, T> != std::strong_ordering::equal); + static_assert (std::type_order_v <T, U> == std::strong_ordering::greater + ? std::type_order_v <U, T> == std::strong_ordering::less + : std::type_order_v <U, T> == std::strong_ordering::greater); + } +}; + +constexpr eq <void, void> a; +constexpr eq <const void, const void> b; +constexpr eq <int, int> c; +constexpr eq <long int, long int> d; +constexpr eq <const volatile unsigned, const volatile unsigned> e; +constexpr eq <S, S> f; +constexpr eq <U <int>, U <int>> g; +constexpr eq <unsigned[2], unsigned[2]> h; +constexpr eq <int, int2> i; +constexpr eq <int (*) (int, long), int (*) (int, long)> j; +constexpr ne <int, long> k; +constexpr ne <const int, int> l; +constexpr ne <S, T> m; +constexpr ne <int &, int &&> n; +constexpr ne <U <S>, U <T>> o; +constexpr ne <U <short>, U <char>> p; +static_assert (std::type_order_v <S, T> != std::strong_ordering::less + || std::type_order_v <T, V> != std::strong_ordering::less + || std::type_order_v <S, V> == std::strong_ordering::less); +constexpr ne <int (*) (int, long), int (*) (int, int)> q; +constexpr eq <W, W> r; +constexpr ne <V, W> s; +constexpr eq <U <W>, U <W>> t; +constexpr ne <U <V>, U <W>> u; diff --git a/libstdc++-v3/testsuite/18_support/exception/version.cc b/libstdc++-v3/testsuite/18_support/exception/version.cc new file mode 100644 index 0000000..5707abd --- /dev/null +++ b/libstdc++-v3/testsuite/18_support/exception/version.cc @@ -0,0 +1,9 @@ +// { dg-do preprocess { target c++26 } } +// { dg-add-options no_pch } + +#include <exception> + +#ifdef __cpp_lib_constexpr_exceptions +# error "Feature test macro for constexpr_exceptions should not be provided by <exception>" +#endif + diff --git a/libstdc++-v3/testsuite/18_support/exception_ptr/62258.cc b/libstdc++-v3/testsuite/18_support/exception_ptr/62258.cc index 0a4d092..a49f347 100644 --- a/libstdc++-v3/testsuite/18_support/exception_ptr/62258.cc +++ b/libstdc++-v3/testsuite/18_support/exception_ptr/62258.cc @@ -1,4 +1,4 @@ -// { dg-options "-Wno-deprecated" } +// { dg-options "-Wno-deprecated-declarations" } // { dg-do run { target c++11 } } // Copyright (C) 2015-2025 Free Software Foundation, Inc. diff --git a/libstdc++-v3/testsuite/18_support/exception_ptr/exception_ptr_cast.cc b/libstdc++-v3/testsuite/18_support/exception_ptr/exception_ptr_cast.cc new file mode 100644 index 0000000..de371d1 --- /dev/null +++ b/libstdc++-v3/testsuite/18_support/exception_ptr/exception_ptr_cast.cc @@ -0,0 +1,92 @@ +// { dg-do run { target c++26 } } + +// Copyright (C) 2025 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// exception_ptr_cast. + +#include <exception> +#include <testsuite_hooks.h> + +#if __cpp_lib_exception_ptr_cast != 202506L +# error "__cpp_lib_exception_ptr_cast != 202506" +#endif + +struct A { int a; }; +struct B : A {}; +struct C : B {}; +struct D {}; +struct E : virtual C { int e; constexpr virtual ~E () {} }; +struct F : virtual E, virtual C { int f; }; +struct G : virtual F, virtual C, virtual E { + constexpr G () : g (4) { a = 1; e = 2; f = 3; } int g; +}; + +constexpr bool test01(bool x) +{ + auto a = std::make_exception_ptr(C{ 42 }); + auto b = std::exception_ptr_cast<C>(a); + VERIFY( b != nullptr ); + VERIFY( b->a == 42 ); + auto c = std::exception_ptr_cast<B>(a); + VERIFY( c == static_cast<const B*>(b) ); + auto d = std::exception_ptr_cast<A>(a); + VERIFY( d == static_cast<const A*>(b) ); + auto e = std::exception_ptr_cast<D>(a); + VERIFY( e == nullptr ); + auto f = std::make_exception_ptr(42L); + auto g = std::exception_ptr_cast<long>(f); + VERIFY( g != nullptr ); + VERIFY( *g == 42L ); + try + { + throw G (); + } + catch (...) + { + auto h = std::current_exception(); + auto i = std::exception_ptr_cast<G>(h); + VERIFY( i != nullptr ); + VERIFY( i->a == 1 && i->e == 2 && i->f == 3 && i->g == 4 ); + auto j = std::exception_ptr_cast<A>(h); + VERIFY( j == static_cast<const A*>(i) ); + auto k = std::exception_ptr_cast<C>(h); + VERIFY( k == static_cast<const C*>(i) ); + auto l = std::exception_ptr_cast<E>(h); + VERIFY( l == static_cast<const E*>(i) ); + auto m = std::exception_ptr_cast<F>(h); + VERIFY( m == static_cast<const F*>(i) ); + auto n = std::exception_ptr_cast<G>(a); + VERIFY( n == nullptr ); + } + if (x) + throw 1; + return true; +} + +static_assert(test01(false)); + +int main() +{ + try + { + test01(true); + } + catch (...) + { + } +} diff --git a/libstdc++-v3/testsuite/18_support/headers/ciso646/macros-2.cc b/libstdc++-v3/testsuite/18_support/headers/ciso646/macros-2.cc new file mode 100644 index 0000000..a492924 --- /dev/null +++ b/libstdc++-v3/testsuite/18_support/headers/ciso646/macros-2.cc @@ -0,0 +1,7 @@ +// { dg-options " -Wdeprecated -fno-operator-names" } +// { dg-do preprocess } + +// Should get a warning for C++20 and up without -D_GLIBCXX_USE_DEPRECATED=0 +// { dg-warning "not a standard header" "" { target c++20 } 0 } + +#include "macros.cc" diff --git a/libstdc++-v3/testsuite/18_support/headers/ciso646/macros.cc b/libstdc++-v3/testsuite/18_support/headers/ciso646/macros.cc index ab3a041..0cb5168 100644 --- a/libstdc++-v3/testsuite/18_support/headers/ciso646/macros.cc +++ b/libstdc++-v3/testsuite/18_support/headers/ciso646/macros.cc @@ -3,7 +3,6 @@ #include <ciso646> -// { dg-warning "deprecated" "" { target c++17_only } 0 } // { dg-error "not a standard header" "" { target c++20 } 0 } #ifdef and diff --git a/libstdc++-v3/testsuite/18_support/headers/climits/values.cc b/libstdc++-v3/testsuite/18_support/headers/climits/values.cc index ac13f9f..bf38369 100644 --- a/libstdc++-v3/testsuite/18_support/headers/climits/values.cc +++ b/libstdc++-v3/testsuite/18_support/headers/climits/values.cc @@ -49,4 +49,12 @@ namespace gnu unsigned short us = USHRT_MAX; unsigned long ul = ULONG_MAX; +#if __cplusplus >= 201103L + // long long + long long ll1 = LLONG_MIN; + long long ll2 = LLONG_MAX; + + // unsigned long long + unsigned long long ull1 = ULLONG_MAX; +#endif } diff --git a/libstdc++-v3/testsuite/18_support/headers/csignal/macros.cc b/libstdc++-v3/testsuite/18_support/headers/csignal/macros.cc index 6d6c821..1076cbc 100644 --- a/libstdc++-v3/testsuite/18_support/headers/csignal/macros.cc +++ b/libstdc++-v3/testsuite/18_support/headers/csignal/macros.cc @@ -37,6 +37,10 @@ namespace gnu #error "SIG_DFL_must_be_a_macro" #endif +#ifndef SIG_IGN + #error "SIG_IGN_must_be_a_macro" +#endif + #ifndef SIGFPE #error "SIGFPE_must_be_a_macro" #endif diff --git a/libstdc++-v3/testsuite/18_support/headers/new/synopsis.cc b/libstdc++-v3/testsuite/18_support/headers/new/synopsis.cc index ee61f91..6d8f4cd 100644 --- a/libstdc++-v3/testsuite/18_support/headers/new/synopsis.cc +++ b/libstdc++-v3/testsuite/18_support/headers/new/synopsis.cc @@ -53,8 +53,8 @@ void operator delete[](void* ptr, const std::nothrow_t&) throw(); #endif CXX26_CONSTEXPR void* operator new (std::size_t size, void* ptr) throw(); CXX26_CONSTEXPR void* operator new[](std::size_t size, void* ptr) throw(); -void operator delete (void* ptr, void*) throw(); -void operator delete[](void* ptr, void*) throw(); +CXX26_CONSTEXPR void operator delete (void* ptr, void*) throw(); +CXX26_CONSTEXPR void operator delete[](void* ptr, void*) throw(); #if __cplusplus >= 201402L // C++14 sized deallocation functions diff --git a/libstdc++-v3/testsuite/18_support/numeric_limits/128bit.cc b/libstdc++-v3/testsuite/18_support/numeric_limits/128bit.cc index b13d837..bf12b65 100644 --- a/libstdc++-v3/testsuite/18_support/numeric_limits/128bit.cc +++ b/libstdc++-v3/testsuite/18_support/numeric_limits/128bit.cc @@ -4,6 +4,11 @@ #if __SIZEOF_FLOAT128__ __extension__ template class std::numeric_limits<__float128>; + +# if __cplusplus >= 201103L +static_assert( std::numeric_limits<__float128>::max_digits10 == 36, + "PR libstdc++/121374" ); +# endif #endif #if __SIZEOF_INT128__ diff --git a/libstdc++-v3/testsuite/18_support/uncaught_exception/14026.cc b/libstdc++-v3/testsuite/18_support/uncaught_exception/14026.cc index 58f241b..7f41c80 100644 --- a/libstdc++-v3/testsuite/18_support/uncaught_exception/14026.cc +++ b/libstdc++-v3/testsuite/18_support/uncaught_exception/14026.cc @@ -18,7 +18,7 @@ // PR 14026 // 18.6.4 uncaught_exception -// { dg-options "-Wno-deprecated" } +// { dg-options "-Wno-deprecated-declarations" } #include <cstdlib> #include <exception> diff --git a/libstdc++-v3/testsuite/19_diagnostics/debugging/breakpoint.cc b/libstdc++-v3/testsuite/19_diagnostics/debugging/breakpoint.cc new file mode 100644 index 0000000..ad24177 --- /dev/null +++ b/libstdc++-v3/testsuite/19_diagnostics/debugging/breakpoint.cc @@ -0,0 +1,13 @@ +// { dg-do run { target c++26 xfail c++26 } } +// { dg-options "-lstdc++exp" } +// { dg-require-cpp-feature-test __cpp_lib_debugging } +#include <debugging> +#include <type_traits> + +static_assert( noexcept(std::breakpoint()) ); +static_assert( std::is_void_v<decltype(std::breakpoint())> ); + +int main() +{ + std::breakpoint(); +} diff --git a/libstdc++-v3/testsuite/19_diagnostics/debugging/breakpoint_if_debugging.cc b/libstdc++-v3/testsuite/19_diagnostics/debugging/breakpoint_if_debugging.cc new file mode 100644 index 0000000..2646183 --- /dev/null +++ b/libstdc++-v3/testsuite/19_diagnostics/debugging/breakpoint_if_debugging.cc @@ -0,0 +1,13 @@ +// { dg-do run { target c++26 } } +// { dg-options "-lstdc++exp" } +// { dg-require-cpp-feature-test __cpp_lib_debugging } +#include <debugging> +#include <type_traits> + +static_assert( noexcept(std::breakpoint_if_debugging()) ); +static_assert( std::is_void_v<decltype(std::breakpoint_if_debugging())> ); + +int main() +{ + std::breakpoint_if_debugging(); +} diff --git a/libstdc++-v3/testsuite/19_diagnostics/debugging/is_debugger_present-2.cc b/libstdc++-v3/testsuite/19_diagnostics/debugging/is_debugger_present-2.cc new file mode 100644 index 0000000..aa4690c --- /dev/null +++ b/libstdc++-v3/testsuite/19_diagnostics/debugging/is_debugger_present-2.cc @@ -0,0 +1,19 @@ +// { dg-do run { target c++26 } } +// { dg-options "-lstdc++exp" } +// { dg-require-cpp-feature-test __cpp_lib_debugging } +// { dg-xfail-run-if "no replaceable functions on AIX" { powerpc-ibm-aix* } } + +// P2810R4 is_debugger_present is_replaceable + +#include <debugging> +#include <testsuite_hooks.h> + +bool called = false; + +bool std::is_debugger_present() noexcept { called = true; return true; } + +int main() +{ + VERIFY( std::is_debugger_present() ); + VERIFY( called ); +} diff --git a/libstdc++-v3/testsuite/19_diagnostics/debugging/is_debugger_present.cc b/libstdc++-v3/testsuite/19_diagnostics/debugging/is_debugger_present.cc new file mode 100644 index 0000000..8dbfa69 --- /dev/null +++ b/libstdc++-v3/testsuite/19_diagnostics/debugging/is_debugger_present.cc @@ -0,0 +1,14 @@ +// { dg-do run { target c++26 } } +// { dg-options "-lstdc++exp" } +// { dg-require-cpp-feature-test __cpp_lib_debugging } +#include <debugging> +#include <type_traits> +#include <testsuite_hooks.h> + +static_assert( noexcept(std::is_debugger_present()) ); +static_assert( std::is_same_v<decltype(std::is_debugger_present()), bool> ); + +int main() +{ + VERIFY( ! std::is_debugger_present() ); +} diff --git a/libstdc++-v3/testsuite/20_util/aligned_storage/value.cc b/libstdc++-v3/testsuite/20_util/aligned_storage/value.cc index e2c6f7b5..6a2c424 100644 --- a/libstdc++-v3/testsuite/20_util/aligned_storage/value.cc +++ b/libstdc++-v3/testsuite/20_util/aligned_storage/value.cc @@ -21,14 +21,14 @@ #include <type_traits> #include <testsuite_tr1.h> -struct MSAlignType { } __attribute__((__aligned__)); +struct MSAlignType { } __attribute__((__aligned__)); void test01() { using std::aligned_storage; using std::alignment_of; using namespace __gnu_test; - + const std::size_t align_c = alignment_of<char>::value; static_assert(sizeof(aligned_storage<4, align_c>::type) >= 4, ""); static_assert(__alignof__(aligned_storage<4, align_c>::type) == align_c, ""); @@ -55,9 +55,11 @@ void test01() static_assert(__alignof__(aligned_storage<11, align_ct>::type) == align_ct, ""); +#if !_GLIBCXX_INLINE_VERSION const std::size_t align_msa = alignment_of<MSAlignType>::value; static_assert(sizeof(aligned_storage<5>::type) >= 5, ""); static_assert(__alignof__(aligned_storage<5>::type) == align_msa, ""); +#endif } // { dg-warning "deprecated" "" { target c++23 } 0 } diff --git a/libstdc++-v3/testsuite/20_util/allocator_traits/members/allocate_hint.cc b/libstdc++-v3/testsuite/20_util/allocator_traits/members/allocate_hint.cc index 214be6a..94c3fbd 100644 --- a/libstdc++-v3/testsuite/20_util/allocator_traits/members/allocate_hint.cc +++ b/libstdc++-v3/testsuite/20_util/allocator_traits/members/allocate_hint.cc @@ -74,7 +74,7 @@ void test02() { typedef std::allocator_traits<unhintable_allocator<X>> traits_type; traits_type::allocator_type a; - traits_type::const_void_pointer v; + traits_type::const_void_pointer v = nullptr; X* p __attribute__((unused)) = traits_type::allocate(a, 1, v); VERIFY( a.called ); } diff --git a/libstdc++-v3/testsuite/20_util/allocator_traits/members/allocate_hint_nonpod.cc b/libstdc++-v3/testsuite/20_util/allocator_traits/members/allocate_hint_nonpod.cc index 32bbf2b..c305b67 100644 --- a/libstdc++-v3/testsuite/20_util/allocator_traits/members/allocate_hint_nonpod.cc +++ b/libstdc++-v3/testsuite/20_util/allocator_traits/members/allocate_hint_nonpod.cc @@ -58,7 +58,7 @@ void test01() { typedef std::allocator_traits<Alloc<int>> traits_type; traits_type::allocator_type a; - traits_type::const_void_pointer v; + traits_type::const_void_pointer v = nullptr; traits_type::pointer p = traits_type::allocate(a, 1, v); traits_type::deallocate(a, p, 1); } diff --git a/libstdc++-v3/testsuite/20_util/any/misc/any_cast_neg.cc b/libstdc++-v3/testsuite/20_util/any/misc/any_cast_neg.cc index 9740e09..9e5c64c 100644 --- a/libstdc++-v3/testsuite/20_util/any/misc/any_cast_neg.cc +++ b/libstdc++-v3/testsuite/20_util/any/misc/any_cast_neg.cc @@ -27,6 +27,7 @@ void test01() const any y(1); any_cast<int&>(y); // { dg-error "here" } // { dg-error "Template argument must be constructible from a const value" "" { target { *-*-* } } 0 } + // { dg-error "binding reference of type 'int&' to 'const int' discards qualifiers" "" { target { *-*-* } } 0 } } void test02() @@ -34,6 +35,7 @@ void test02() any y(1); any_cast<int&&>(y); // { dg-error "here" } // { dg-error "Template argument must be constructible from an lvalue" "" { target { *-*-* } } 0 } + // { dg-error "cannot bind rvalue reference of type 'int&&' to lvalue of type 'int'" "" { target { *-*-* } } 0 } } void test03() @@ -41,6 +43,7 @@ void test03() any y(1); any_cast<int&>(std::move(y)); // { dg-error "here" } // { dg-error "Template argument must be constructible from an rvalue" "" { target { *-*-* } } 0 } + // { dg-error "cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'" "" { target { *-*-* } } 0 } } // { dg-prune-output "invalid 'static_cast'" } diff --git a/libstdc++-v3/testsuite/20_util/bind/80564.cc b/libstdc++-v3/testsuite/20_util/bind/80564.cc new file mode 100644 index 0000000..f6e1a1e --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/bind/80564.cc @@ -0,0 +1,50 @@ +// PR libstdc++/80564 - bind on SFINAE unfriendly generic lambda +// { dg-do compile { target c++14 } } + +#include <functional> + +struct A +{ + template<typename T> + auto operator()(T&) + { } + + template<typename T> + auto operator()(T&) const + { T::fail; } +}; + +void +test01() +{ + A a; + std::bind(a, 0)(); // doesn't consider the const overload + std::bind<void>(a, 0)(); +} + +void +test02() +{ + auto f = [] (auto& x) { x = 1; }; + int i; + std::bind(f, i)(); // doesn't try const-invoking the lambda + std::bind<void>(f, i)(); +} + +#if __cpp_variadic_using +template<typename... Ts> +struct overloaded : private Ts... +{ + overloaded(Ts... ts) : Ts(ts)... { } + using Ts::operator()...; +}; + +void +test03() +{ + A a; + auto f = std::bind(a, 0); + overloaded<decltype(f)> g(f); + g(); +} +#endif diff --git a/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc b/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc index e4c348f..6d37cc4 100644 --- a/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc +++ b/libstdc++-v3/testsuite/20_util/bind/cv_quals_2.cc @@ -44,6 +44,9 @@ void test01() // { dg-error "no match" "" { target c++20 } 43 } } +// Ignore the reasons for deduction/substitution failure in the headers. +// { dg-prune-output "no type named 'type' in 'struct std::enable_if<false" } + int main() { test01(); diff --git a/libstdc++-v3/testsuite/20_util/bind/dangling_ref.cc b/libstdc++-v3/testsuite/20_util/bind/dangling_ref.cc index 17e7b21..32af0a2 100644 --- a/libstdc++-v3/testsuite/20_util/bind/dangling_ref.cc +++ b/libstdc++-v3/testsuite/20_util/bind/dangling_ref.cc @@ -1,4 +1,5 @@ // { dg-do compile { target c++11 } } +// { dg-additional-options "-Wsystem-headers" } #include <functional> int f(); diff --git a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc index dd47c43..46cc4bb 100644 --- a/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc +++ b/libstdc++-v3/testsuite/20_util/bind/ref_neg.cc @@ -51,6 +51,7 @@ void test02() // Ignore the reasons for deduction/substitution failure in the headers. // Arrange for the match to work on installed trees as well as build trees. // { dg-prune-output "no type named 'type' in 'struct std::__invoke_result" } +// { dg-prune-output "no type named 'type' in 'struct std::enable_if<false" } int main() { diff --git a/libstdc++-v3/testsuite/20_util/bitset/121054.cc b/libstdc++-v3/testsuite/20_util/bitset/121054.cc new file mode 100644 index 0000000..0e8f32d --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/bitset/121054.cc @@ -0,0 +1,233 @@ +// { dg-do run } + +// PR libstdc++/121054 std::bitset<0>("zero") should throw std::invalid_argument +#include <bitset> +#include <stdexcept> +#include <testsuite_hooks.h> + +void +test01() +{ + try { + std::bitset<0>(std::string("x")); + VERIFY( false ); + } + catch(std::invalid_argument& fail) { + VERIFY( true ); + } + catch(...) { + VERIFY( false ); + } + + try { + std::bitset<1>(std::string("0x")); + VERIFY( false ); + } + catch(std::invalid_argument& fail) { + VERIFY( true ); + } + catch(...) { + VERIFY( false ); + } + + try { + std::bitset<0>(std::string("01")); + VERIFY( true ); + } + catch(std::invalid_argument& fail) { + VERIFY( false ); + } + catch(...) { + VERIFY( false ); + } + + try { + std::bitset<1>(std::string("x0"), 1); + VERIFY( true ); + } + catch(std::invalid_argument& fail) { + VERIFY( false ); + } + catch(...) { + VERIFY( false ); + } + +#if _GLIBCXX_USE_C99_WCHAR + try { + std::bitset<1>(std::wstring(L"0x")); + VERIFY( false ); + } + catch(std::invalid_argument& fail) { + VERIFY( true ); + } + catch(...) { + VERIFY( false ); + } + + try { + std::bitset<1>(std::wstring(L"x0"), 1); + VERIFY( true ); + } + catch(std::invalid_argument& fail) { + VERIFY( false ); + } + catch(...) { + VERIFY( false ); + } +#endif +} + +void +test02() +{ +#if __cplusplus >= 201103L + try { + std::bitset<0>("x"); + VERIFY( false ); + } + catch(std::invalid_argument& fail) { + VERIFY( true ); + } + catch(...) { + VERIFY( false ); + } + + try { + std::bitset<1>("0x", 2); + VERIFY( false ); + } + catch(std::invalid_argument& fail) { + VERIFY( true ); + } + catch(...) { + VERIFY( false ); + } + + try { + std::bitset<1>("0x", 1); + VERIFY( true ); + } + catch(std::invalid_argument& fail) { + VERIFY( false ); + } + catch(...) { + VERIFY( false ); + } + + try { + std::bitset<0>("01"); + VERIFY( true ); + } + catch(std::invalid_argument& fail) { + VERIFY( false ); + } + catch(...) { + VERIFY( false ); + } + +#if _GLIBCXX_USE_C99_WCHAR + try { + std::bitset<1>(L"0x", 2); + VERIFY( false ); + } + catch(std::invalid_argument& fail) { + VERIFY( true ); + } + catch(...) { + VERIFY( false ); + } + + try { + std::bitset<1>(L"0x", 1); + VERIFY( true ); + } + catch(std::invalid_argument& fail) { + VERIFY( false ); + } + catch(...) { + VERIFY( false ); + } +#endif +#endif +} + +void +test03() +{ +#if __cpp_lib_bitset >= 202202L + try { + std::bitset<0>(std::string_view("x")); + VERIFY( false ); + } + catch(std::invalid_argument& fail) { + VERIFY( true ); + } + catch(...) { + VERIFY( false ); + } + + try { + std::bitset<1>(std::string_view("0x")); + VERIFY( false ); + } + catch(std::invalid_argument& fail) { + VERIFY( true ); + } + catch(...) { + VERIFY( false ); + } + + try { + std::bitset<0>(std::string_view("01")); + VERIFY( true ); + } + catch(std::invalid_argument& fail) { + VERIFY( false ); + } + catch(...) { + VERIFY( false ); + } + + try { + std::bitset<1>(std::string_view("x0"), 1); + VERIFY( true ); + } + catch(std::invalid_argument& fail) { + VERIFY( false ); + } + catch(...) { + VERIFY( false ); + } + +#if _GLIBCXX_USE_C99_WCHAR + try { + std::bitset<1>(std::wstring_view(L"0x")); + VERIFY( false ); + } + catch(std::invalid_argument& fail) { + VERIFY( true ); + } + catch(...) { + VERIFY( false ); + } + + try { + std::bitset<1>(std::wstring_view(L"x0"), 1); + VERIFY( true ); + } + catch(std::invalid_argument& fail) { + VERIFY( false ); + } + catch(...) { + VERIFY( false ); + } +#endif +#endif +} + +int main() +{ + test01(); + test02(); + test03(); +} diff --git a/libstdc++-v3/testsuite/20_util/bitset/cons/1.cc b/libstdc++-v3/testsuite/20_util/bitset/cons/1.cc index 0a40b6c..6441332 100644 --- a/libstdc++-v3/testsuite/20_util/bitset/cons/1.cc +++ b/libstdc++-v3/testsuite/20_util/bitset/cons/1.cc @@ -47,6 +47,7 @@ void test01(void) const size_t n3 = 128; try { std::bitset<n3> bit03(str01, 5); + VERIFY(false); } catch(std::invalid_argument& fail) { VERIFY( true ); diff --git a/libstdc++-v3/testsuite/20_util/bitset/cons/6282.cc b/libstdc++-v3/testsuite/20_util/bitset/cons/6282.cc index fafa9fc..ee348c3 100644 --- a/libstdc++-v3/testsuite/20_util/bitset/cons/6282.cc +++ b/libstdc++-v3/testsuite/20_util/bitset/cons/6282.cc @@ -39,6 +39,8 @@ void test02(void) std::bitset<0> z3(std::string("10101010101")); VERIFY( z3.any() == false ); + VERIFY( z1.to_ulong() == 0 ); + VERIFY( (z1.to_string<char,char_traits<char>,allocator<char> >().empty() )); try { z1.set(0); VERIFY( false ); @@ -49,9 +51,6 @@ void test02(void) catch(...) { VERIFY( false ); } - - VERIFY( z1.to_ulong() == 0 ); - VERIFY( (z1.to_string<char,char_traits<char>,allocator<char> >().empty() )); } int main() diff --git a/libstdc++-v3/testsuite/20_util/bitset/cons/constexpr_c++23.cc b/libstdc++-v3/testsuite/20_util/bitset/cons/constexpr_c++23.cc index 7e2eba5..0a94094 100644 --- a/libstdc++-v3/testsuite/20_util/bitset/cons/constexpr_c++23.cc +++ b/libstdc++-v3/testsuite/20_util/bitset/cons/constexpr_c++23.cc @@ -18,8 +18,6 @@ constexpr bool test_ntbs() VERIFY( std::bitset<0>("000").all() ); VERIFY( std::bitset<0>("000", 2).all() ); VERIFY( std::bitset<1>("100", 2).all() ); - VERIFY( std::bitset<1>("z00", 2, 'z').none() ); - VERIFY( std::bitset<2>("ab0", 3, 'a', 'b').count() == 1 ); return true; } diff --git a/libstdc++-v3/testsuite/20_util/bitset/cons/string_view.cc b/libstdc++-v3/testsuite/20_util/bitset/cons/string_view.cc new file mode 100644 index 0000000..ec3a6c86 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/bitset/cons/string_view.cc @@ -0,0 +1,132 @@ +// C++26 [bitset.cons] + +// { dg-do run { target c++26 } } + +#ifndef C +# define C char +# define L(s) s +#endif + +#include <bitset> +#include <string> +#include <string_view> +#include <algorithm> // std::reverse, std::transform +#include <stdexcept> +#include <testsuite_hooks.h> + +void test01() +{ + // template<_C,_T> + // constexpr explicit + // bitset(const basic_string_view<_C,_T>, + // size_type pos, size_type n, _C zero, _C one) + try { + std::basic_string_view<C> str(L("stuff smith sessions")); + const std::size_t n = 128; + std::bitset<n> bit(str, 5); + VERIFY(false); + } + catch(std::invalid_argument& fail) { + VERIFY( true ); + } + catch(...) { + VERIFY( false ); + } + + try { + std::basic_string_view<C> str(L("010101000011")); + const std::size_t n = 128; + const auto sz = str.size(); + std::bitset<n> bit(str, 0); + std::basic_string<C> str02; + for (std::size_t i = 0; i < sz; ++i) + str02 += (bit.test(i) ? C('1') : C('0')); + std::reverse(str02.begin(), str02.end()); + VERIFY( str02 == str ); + } + catch(std::invalid_argument& fail) { + VERIFY( false ); + } + catch(...) { + VERIFY( false ); + } + + // substring<C> "010101000011" + // "10100001" + try { + std::basic_string_view<C> str(L("010101000011")); + const std::size_t n = 128; + const auto sz = 8; + std::bitset<n> bit(str, 3, sz); + std::basic_string<C> str02; + for (std::size_t i = 0; i < sz; ++i) + str02 += (bit.test(i) ? C('1') : C('0')); + std::reverse(str02.begin(), str02.end()); + VERIFY( str02 == str.substr(3, sz) ); + } + catch(std::invalid_argument& fail) { + VERIFY( false ); + } + catch(...) { + VERIFY( false ); + } + + // "abababaaaabb", zero = 'a', one = 'b' + try { + std::basic_string_view<C> str(L("010101000011")); + const std::size_t n = 128; + const auto sz = str.size(); + std::basic_string<C> str02(str); + std::transform(str02.cbegin(), str02.cend(), str02.begin(), [](auto c) { + return (c - C('0')) + C('a'); + }); + std::basic_string_view<C> str03(str02); + std::bitset<n> bit(str03, 0, 100, C('a'), C('b')); + std::basic_string<C> str04; + for (std::size_t i = 0; i < sz; ++i) + str04 += (bit.test(i) ? C('1') : C('0')); + std::reverse(str04.begin(), str04.end()); + VERIFY( str04 == str ); + } + catch(std::invalid_argument& fail) { + VERIFY( false ); + } + catch(...) { + VERIFY( false ); + } + + // "aba0aba", zero = 'a', one = 'b', '0' appears + try { + const std::size_t n = 128; + std::basic_string_view<C> str05(L("aba0aba")); + std::bitset<n> bit(str05, 0, 100, C('a'), C('b')); + VERIFY( false ); + } + catch(std::invalid_argument& fail) { + VERIFY( true ); + } + catch(...) { + VERIFY( false ); + } + + // pos > str.size() + try { + std::basic_string_view<C> str(L("010101000011")); + const std::size_t n = 128; + const auto sz = str.size(); + std::bitset<n> bit(str, sz + 1, 100); + VERIFY( false ); + } + catch(std::out_of_range& fail) { + VERIFY( true ); + } + catch(...) { + VERIFY( false ); + } +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/20_util/bitset/cons/string_view_wide.cc b/libstdc++-v3/testsuite/20_util/bitset/cons/string_view_wide.cc new file mode 100644 index 0000000..b53fe74 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/bitset/cons/string_view_wide.cc @@ -0,0 +1,8 @@ +// C++26 [bitset.cons] + +// { dg-do run { target c++26 } } + +#define C wchar_t +#define L(s) L ## s + +#include "./string_view.cc" diff --git a/libstdc++-v3/testsuite/20_util/common_reference/p2655r3.cc b/libstdc++-v3/testsuite/20_util/common_reference/p2655r3.cc new file mode 100644 index 0000000..4188dd2 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/common_reference/p2655r3.cc @@ -0,0 +1,15 @@ +// P2655R3 - common_reference_t of reference_wrapper Should Be a Reference Type +// Implemented as a DR against C++20 +// { dg-do compile { target c++20 } } + +#include <type_traits> + +#if __cpp_lib_common_reference != 202302L +# error "Feature-test macro __cpp_lib_common_reference has wrong value in <type_traits>" +#endif + +struct A { }; +struct B { operator A&() const; }; + +static_assert( std::is_same_v<std::common_reference_t<A&, const B&>, A&> ); +static_assert( std::is_same_v<std::common_reference_t<const B&, A&>, A&> ); diff --git a/libstdc++-v3/testsuite/20_util/constant_wrapper/adl.cc b/libstdc++-v3/testsuite/20_util/constant_wrapper/adl.cc new file mode 100644 index 0000000..3cdc09c --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/adl.cc @@ -0,0 +1,86 @@ +// { dg-do compile { target c++26 } } +#include <type_traits> +#include <concepts> + +#include <testsuite_hooks.h> + +namespace adl { + +struct Friend +{}; + +constexpr +int operator+(Friend, int x) +{ return x; }; + +template<typename T> + struct TemplFriend + { }; + +template<typename T> + constexpr + // templated, we cannot deduce T from cw<Friend<int>> + int operator+(TemplFriend<T>, int x) + { return x; }; + + +struct HiddenFriend +{ + constexpr friend + int operator+(HiddenFriend, int x) + { return x; } +}; + +template<typename T> + struct TemplHiddenFriend + { + constexpr friend + // note that this not not template itself + int operator+(TemplHiddenFriend, int x) + { return x; } + }; +} + +template<typename T> + concept supportMixedObj = requires + { + { std::cw<T{}> + 1 } -> std::same_as<int>; + }; + +template<typename T> + concept supportMixedInt = requires(T t) + { + { t + std::cw<1> } -> std::same_as<int>; + }; + +static_assert(supportMixedObj<adl::Friend>); +static_assert(supportMixedInt<adl::Friend>); +static_assert(!supportMixedObj<adl::TemplFriend<int>>); +static_assert(supportMixedInt<adl::TemplFriend<int>>); + +static_assert(supportMixedObj<adl::HiddenFriend>); +static_assert(supportMixedInt<adl::HiddenFriend>); +static_assert(supportMixedObj<adl::TemplHiddenFriend<int>>); +static_assert(supportMixedInt<adl::TemplHiddenFriend<int>>); + +struct Member +{ + constexpr + // conversion for the first argument is not allowed + int operator+(int x) const + { return x; } +}; + +static_assert(!supportMixedObj<Member>); +static_assert(supportMixedInt<Member>); + +struct ExplicitThisMember +{ + constexpr + // conversion for the first argument is not allowed + int operator+(this ExplicitThisMember, int x) + { return x; } +}; + +static_assert(!supportMixedObj<ExplicitThisMember>); +static_assert(supportMixedInt<ExplicitThisMember>); diff --git a/libstdc++-v3/testsuite/20_util/constant_wrapper/ex.cc b/libstdc++-v3/testsuite/20_util/constant_wrapper/ex.cc new file mode 100644 index 0000000..a4d967b --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/ex.cc @@ -0,0 +1,39 @@ +// { dg-do compile { target c++26 } } +#include <type_traits> +#include <iostream> + +#include <testsuite_hooks.h> + +constexpr auto +initial_phase(auto quantity_1, auto quantity_2) +{ return quantity_1 + quantity_2; } + +constexpr auto +middle_phase(auto tbd) +{ return tbd; } + +constexpr void +final_phase(auto gathered, auto available) +{ + if constexpr (gathered == available) + std::cout << "Profit!\n"; +} + +void +impeccable_underground_planning() +{ + auto gathered_quantity = middle_phase(initial_phase(std::cw<42>, std::cw<13>)); + static_assert(gathered_quantity == 55); + auto all_available = std::cw<55>; + final_phase(gathered_quantity, all_available); +} + +void +deeply_flawed_underground_planning() +{ + constexpr auto gathered_quantity = middle_phase(initial_phase(42, 13)); + constexpr auto all_available = 55; + final_phase(gathered_quantity, all_available); // { dg-error "required from here" } +} + +// { dg-prune-output "'gathered' is not a constant expression" } diff --git a/libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc b/libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc new file mode 100644 index 0000000..f632f8e --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc @@ -0,0 +1,391 @@ +// { dg-do run { target c++26 } } +#include <type_traits> +#include <utility> +#include <string_view> + +#include <testsuite_hooks.h> + +constexpr void +check_same(auto actual, auto expected) +{ + VERIFY(actual == expected); + static_assert(std::same_as<decltype(actual), decltype(expected)>); +}; + + +constexpr void +test_c_arrays() +{ + constexpr double x[] = {1.1, 2.2, 3.3}; + auto cx = std::cw<x>; + auto access = [](auto x, size_t i) + { return x[i]; }; + + check_same(access(std::cw<x>, 0), x[0]); + check_same(access(std::cw<x>, 1), x[1]); + check_same(access(std::cw<x>, 2), x[2]); + + check_same(cx[std::cw<0>], std::cw<x[0]>); + check_same(cx[std::cw<1>], std::cw<x[1]>); + check_same(cx[std::cw<2>], std::cw<x[2]>); +} + +constexpr size_t +deduce_cstr_size(auto str) +{ + size_t sz = 0; + while(str[sz++] != '\0') { } + return sz; +} + +constexpr void +test_string_literals() +{ + auto foo = std::cw<"foo">; + constexpr const typename decltype(foo)::value_type & cstr = foo; + constexpr size_t N = std::size(cstr); + constexpr auto foo_view = std::string_view(cstr, N-1); + + constexpr const char (&cstr1)[deduce_cstr_size(foo)] = foo; + constexpr size_t N1 = std::size(cstr); + + static_assert(static_cast<char const*>(cstr) == + static_cast<char const*>(cstr1)); + static_assert(N1 == N); + + static_assert(foo[0] == 'f'); + static_assert(foo[1] == 'o'); + static_assert(foo[2] == 'o'); + static_assert(foo[3] == '\0'); + static_assert(static_cast<char const *>(foo) == foo_view); +} + +constexpr bool +convert_constexpr(auto c) +{ + if constexpr (int(c) > 0) + return true; + return false; +} + +constexpr void +test_converted_constexpr() +{ + VERIFY(!convert_constexpr(std::cw<-1>)); + VERIFY(convert_constexpr(std::cw<1>)); +} + +constexpr void +test_ints() +{ + std::constant_wrapper<2> two; + std::constant_wrapper<3> three; + std::constant_wrapper<5> five; + + VERIFY(two + 3 == 5); + static_assert(std::same_as<decltype(two + 3), int>); + + VERIFY(two + three == 5); + VERIFY(two + three == five); + static_assert(std::same_as<decltype(two + three), std::constant_wrapper<5>>); + + VERIFY(two == std::cw<2>); + VERIFY(two + 3 == std::cw<5>); +} + +constexpr int +add(int i, int j) +{ return i + j; } + +struct Add +{ + constexpr int + operator()(int i, int j) const noexcept + { return i + j; } +}; + +constexpr void +test_function_object() +{ + auto check = [](auto cfo) + { + auto ci = std::cw<2>; + auto cj = std::cw<3>; + + VERIFY(cfo(ci, cj) == 5); + static_assert(std::same_as<decltype(cfo(ci, cj)), std::constant_wrapper<5>>); + + static_assert(std::invocable<decltype(cfo), decltype(ci), decltype(cj)>); + static_assert(!std::invocable<decltype(cfo), int, decltype(cj)>); + static_assert(!std::invocable<decltype(cfo), int, int>); + }; + + check(std::cw<Add{}>); + check(std::cw<[](int i, int j){ return i + j; }>); + check(std::cw<[](auto i, auto j){ return i + j; }>); +} + +constexpr void +test_function_pointer() +{ + auto cptr = std::cw<add>; + auto ci = std::cw<2>; + auto cj = std::cw<3>; + + VERIFY(cptr(ci, cj) == 5); + static_assert(std::same_as<decltype(cptr(ci, cj)), std::constant_wrapper<5>>); + + VERIFY(cptr(2, cj) == 5); + static_assert(std::same_as<decltype(cptr(2, cj)), int>); + + VERIFY(cptr(2, 3) == 5); + static_assert(std::same_as<decltype(cptr(2, 3)), int>); +} + +struct Indexable1 +{ + constexpr int + operator[](int i, int j) const noexcept + { return i*j; } +}; + +template<typename Obj, typename... Args> + concept indexable = requires (Obj obj, Args... args) + { + obj[args...]; + }; + +constexpr void +test_indexable1() +{ + auto cind = std::cw<Indexable1{}>; + auto ci = std::cw<2>; + auto cj = std::cw<3>; + VERIFY(cind[ci, cj] == ci*cj); + static_assert(std::same_as<decltype(cind[ci, cj]), std::constant_wrapper<6>>); + + static_assert(indexable<decltype(cind), decltype(ci), decltype(cj)>); + static_assert(!indexable<decltype(cind), int, decltype(cj)>); + static_assert(!indexable<decltype(cind), int, int>); +} + +struct Indexable2 +{ + template<typename I, typename J> + constexpr int + operator[](I i, J j) const noexcept + { return i*j; } +}; + +constexpr void +test_indexable2() +{ + auto cind = std::cw<Indexable2{}>; + auto ci = std::cw<2>; + auto cj = std::cw<3>; + VERIFY(cind[ci, cj] == ci*cj); + static_assert(std::same_as<decltype(cind[ci, cj]), std::constant_wrapper<6>>); + + static_assert(indexable<decltype(cind), decltype(ci), decltype(cj)>); + static_assert(!indexable<decltype(cind), int, decltype(cj)>); + static_assert(!indexable<decltype(cind), int, int>); +} + +struct Indexable3 +{ + template<typename... Is> + constexpr int + operator[](Is... i) const noexcept + { return (1 * ... * i); } +}; + +constexpr void +test_indexable3() +{ + auto cind = std::cw<Indexable3{}>; + auto ci = std::cw<2>; + auto cj = std::cw<3>; + + check_same(cind[], std::cw<1>); + check_same(cind[ci], std::cw<2>); + check_same(cind[ci, cj], std::cw<2*3>); +} + +struct Divide +{ + int value; + + constexpr int + divide(int div) const + { return value / div; } + +}; + +constexpr void +test_member_pointer() +{ + constexpr int nom = 42; + constexpr int denom = 3; + + auto cvalue = std::cw<&Divide::value>; + auto cdiv = std::cw<&Divide::divide>; + auto co = std::cw<Divide{nom}>; + + check_same((&co)->*cvalue, std::cw<nom>); + check_same((&co)->*(&Divide::value), nom); + check_same(&(co.value)->*cvalue, nom); + + auto expect_unwrapped = nom / denom; + check_same(((&co)->*(&Divide::divide))(denom), expect_unwrapped); + check_same((&(co.value)->*cdiv)(denom), expect_unwrapped); + check_same(((&decltype(co)::value)->*cdiv)(denom), expect_unwrapped); +} + +constexpr void +test_pseudo_mutator() +{ + auto ci = std::cw<3>; + auto cmmi = --ci; + VERIFY(ci.value == 3); + VERIFY(cmmi.value == 2); + + auto cimm = ci--; + VERIFY(ci.value == 3); + VERIFY(cimm.value == 3); +} + +struct Truthy +{ + constexpr operator bool() const + { return true; } +}; + +template<typename Lhs, typename Rhs> + concept has_op_and = requires (Lhs lhs, Rhs rhs) + { + lhs && rhs; + }; + +constexpr void +test_logic() +{ + auto ctrue = std::cw<true>; + auto cfalse = std::cw<false>; + auto truthy = Truthy{}; + + auto check_and = [](auto lhs, auto rhs) + { + static_assert(lhs && rhs); + static_assert(std::same_as<decltype(lhs && rhs), bool>); + }; + + auto check_or = [](auto lhs, auto rhs) + { + static_assert(lhs || rhs); + static_assert(std::same_as<decltype(lhs || rhs), bool>); + }; + + check_and(ctrue, ctrue); + check_or(ctrue, cfalse); + check_and((std::cw<0> < std::cw<1>), (std::cw<1> < std::cw<5>)); + check_or((std::cw<0> < std::cw<1>), (std::cw<1> < std::cw<5>)); + + auto ctruthy = std::cw<Truthy{}>; + static_assert(has_op_and<decltype(truthy), bool>); + static_assert(!has_op_and<decltype(ctruthy), decltype(ctrue)>); + static_assert(!has_op_and<decltype(ctruthy), bool>); +} + +struct ThreeWayComp +{ + friend + constexpr std::strong_ordering + operator<=>(ThreeWayComp lhs, ThreeWayComp rhs) + { return lhs.value <=> rhs.value; } + + int value; +}; + +constexpr void +test_three_way() +{ + auto ctrue = std::cw<true>; + auto cfalse = std::cw<false>; + + check_same(std::cw<ThreeWayComp{0}> < std::cw<ThreeWayComp{1}>, ctrue); + check_same(std::cw<ThreeWayComp{2}> > std::cw<ThreeWayComp{1}>, ctrue); + check_same(std::cw<ThreeWayComp{2}> >= std::cw<ThreeWayComp{1}>, ctrue); + check_same(std::cw<ThreeWayComp{0}> <= std::cw<ThreeWayComp{1}>, ctrue); + check_same(std::cw<ThreeWayComp{0}> >= std::cw<ThreeWayComp{1}>, cfalse); + + check_same(std::cw<ThreeWayComp{0}> < ThreeWayComp{1}, true); + check_same(ThreeWayComp{2} > std::cw<ThreeWayComp{1}>, true); +} + +struct EqualityComp +{ + friend + constexpr bool + operator==(EqualityComp lhs, EqualityComp rhs) + { return lhs.value == rhs.value; } + + int value; +}; + +constexpr void +test_equality() +{ + auto ctrue = std::cw<true>; + check_same(std::cw<EqualityComp{1}> == std::cw<EqualityComp{1}>, ctrue); + check_same(std::cw<EqualityComp{0}> != std::cw<EqualityComp{1}>, ctrue); + + check_same(std::cw<EqualityComp{1}> == EqualityComp{1}, true); + check_same(EqualityComp{0} != std::cw<EqualityComp{1}>, true); +} + +struct ConstAssignable +{ + int value; + + constexpr ConstAssignable + operator=(int rhs) const + { return ConstAssignable{rhs}; } + + friend constexpr bool + operator==(const ConstAssignable& lhs, const ConstAssignable& rhs) + { return lhs.value == rhs.value; } +}; + +constexpr void +test_assignment() +{ + check_same(std::cw<ConstAssignable{3}> = std::cw<2>, + std::cw<ConstAssignable{2}>); +} + + +constexpr bool +test_all() +{ + test_c_arrays(); + test_ints(); + test_function_object(); + test_function_pointer(); + test_indexable1(); + test_indexable2(); + test_indexable3(); + test_member_pointer(); + test_pseudo_mutator(); + test_logic(); + test_three_way(); + test_equality(); + return true; +} + +int +main() +{ + test_all(); + static_assert(test_all()); + return 0; +} diff --git a/libstdc++-v3/testsuite/20_util/constant_wrapper/instantiate.cc b/libstdc++-v3/testsuite/20_util/constant_wrapper/instantiate.cc new file mode 100644 index 0000000..4f12325 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/instantiate.cc @@ -0,0 +1,575 @@ +// { dg-do run { target c++26 } } +#include <type_traits> +#include <utility> + +#include <testsuite_hooks.h> + +namespace free_ops +{ + template<int OpId> + struct UnaryOps + { + friend constexpr int + operator+(UnaryOps) noexcept requires (OpId == 0) + { return OpId; } + + friend constexpr int + operator-(UnaryOps) noexcept requires (OpId == 1) + { return OpId; } + + friend constexpr int + operator~(UnaryOps) noexcept requires (OpId == 2) + { return OpId; } + + friend constexpr int + operator!(UnaryOps) noexcept requires (OpId == 3) + { return OpId; } + + friend constexpr int + operator&(UnaryOps) noexcept requires (OpId == 4) + { return OpId; } + + friend constexpr int + operator*(UnaryOps) noexcept requires (OpId == 5) + { return OpId; } + + friend constexpr int + operator++(UnaryOps) noexcept requires (OpId == 6) + { return OpId; } + + friend constexpr int + operator++(UnaryOps, int) noexcept requires (OpId == 7) + { return OpId; } + + friend constexpr int + operator--(UnaryOps) noexcept requires (OpId == 8) + { return OpId; } + + friend constexpr int + operator--(UnaryOps, int) noexcept requires (OpId == 9) + { return OpId; } + }; +} + +namespace member_ops +{ + template<int OpId> + struct UnaryOps + { + constexpr int + operator+() const noexcept requires (OpId == 0) + { return OpId; } + + constexpr int + operator-() const noexcept requires (OpId == 1) + { return OpId; } + + constexpr int + operator~() const noexcept requires (OpId == 2) + { return OpId; } + + constexpr int + operator!() const noexcept requires (OpId == 3) + { return OpId; } + + constexpr int + operator&() const noexcept requires (OpId == 4) + { return OpId; } + + constexpr int + operator*() const noexcept requires (OpId == 5) + { return OpId; } + + constexpr int + operator++() const noexcept requires (OpId == 6) + { return OpId; } + + constexpr int + operator++(int) const noexcept requires (OpId == 7) + { return OpId; } + + constexpr int + operator--() const noexcept requires (OpId == 8) + { return OpId; } + + constexpr int + operator--(int) const noexcept requires (OpId == 9) + { return OpId; } + }; +} + +constexpr size_t n_unary_ops = 10; + +template<template<int> typename Ops, int OpId> + constexpr void + test_unary_operator() + { + auto x = std::cw<Ops<OpId>{}>; + + auto check = [](auto c) + { + VERIFY(c == OpId); + static_assert(std::same_as<decltype(c), std::constant_wrapper<OpId>>); + }; + + if constexpr (OpId == 0) + check(+x); + if constexpr (OpId == 1) + check(-x); + if constexpr (OpId == 2) + check(~x); + if constexpr (OpId == 3) + check(!x); + if constexpr (OpId == 4) + check(&x); + if constexpr (OpId == 5) + check(*x); + if constexpr (OpId == 6) + check(++x); + if constexpr (OpId == 7) + check(x++); + if constexpr (OpId == 8) + check(--x); + if constexpr (OpId == 9) + check(x--); + + static_assert(n_unary_ops == 10); + } + +constexpr void +test_unary_operators_all() +{ + auto run = []<size_t... Idx>(std::integer_sequence<size_t, Idx...>) + { + (test_unary_operator<free_ops::UnaryOps, Idx>(), ...); + (test_unary_operator<member_ops::UnaryOps, Idx>(), ...); + }; + run(std::make_index_sequence<n_unary_ops>()); +} + +namespace free_ops +{ + template<int OpId> + struct BinaryOps + { + friend constexpr int + operator+(BinaryOps, BinaryOps) noexcept requires (OpId == 0) + { return OpId; } + + friend constexpr int + operator-(BinaryOps, BinaryOps) noexcept requires (OpId == 1) + { return OpId; } + + friend constexpr int + operator*(BinaryOps, BinaryOps) noexcept requires (OpId == 2) + { return OpId; } + + friend constexpr int + operator/(BinaryOps, BinaryOps) noexcept requires (OpId == 3) + { return OpId; } + + friend constexpr int + operator%(BinaryOps, BinaryOps) noexcept requires (OpId == 4) + { return OpId; } + + friend constexpr int + operator<<(BinaryOps, BinaryOps) noexcept requires (OpId == 5) + { return OpId; } + + friend constexpr int + operator>>(BinaryOps, BinaryOps) noexcept requires (OpId == 6) + { return OpId; } + + friend constexpr int + operator&(BinaryOps, BinaryOps) noexcept requires (OpId == 7) + { return OpId; } + + friend constexpr int + operator|(BinaryOps, BinaryOps) noexcept requires (OpId == 8) + { return OpId; } + + friend constexpr int + operator^(BinaryOps, BinaryOps) noexcept requires (OpId == 9) + { return OpId; } + + friend constexpr int + operator&&(BinaryOps, BinaryOps) noexcept requires (OpId == 10) + { return OpId; } + + friend constexpr int + operator||(BinaryOps, BinaryOps) noexcept requires (OpId == 11) + { return OpId; } + + friend constexpr int + operator<=>(BinaryOps, BinaryOps) noexcept requires (OpId == 12) + { return OpId; } + + friend constexpr int + operator<(BinaryOps, BinaryOps) noexcept requires (OpId == 13) + { return OpId; } + + friend constexpr int + operator<=(BinaryOps, BinaryOps) noexcept requires (OpId == 14) + { return OpId; } + + friend constexpr int + operator==(BinaryOps, BinaryOps) noexcept requires (OpId == 15) + { return OpId; } + + friend constexpr int + operator!=(BinaryOps, BinaryOps) noexcept requires (OpId == 16) + { return OpId; } + + friend constexpr int + operator>(BinaryOps, BinaryOps) noexcept requires (OpId == 17) + { return OpId; } + + friend constexpr int + operator>=(BinaryOps, BinaryOps) noexcept requires (OpId == 18) + { return OpId; } + + friend constexpr int + operator+=(BinaryOps, BinaryOps) noexcept requires (OpId == 19) + { return OpId; } + + friend constexpr int + operator-=(BinaryOps, BinaryOps) noexcept requires (OpId == 20) + { return OpId; } + + friend constexpr int + operator*=(BinaryOps, BinaryOps) noexcept requires (OpId == 21) + { return OpId; } + + friend constexpr int + operator/=(BinaryOps, BinaryOps) noexcept requires (OpId == 22) + { return OpId; } + + friend constexpr int + operator%=(BinaryOps, BinaryOps) noexcept requires (OpId == 23) + { return OpId; } + + friend constexpr int + operator&=(BinaryOps, BinaryOps) noexcept requires (OpId == 24) + { return OpId; } + + friend constexpr int + operator|=(BinaryOps, BinaryOps) noexcept requires (OpId == 25) + { return OpId; } + friend constexpr int + + operator^=(BinaryOps, BinaryOps) noexcept requires (OpId == 26) + { return OpId; } + + friend constexpr int + operator<<=(BinaryOps, BinaryOps) noexcept requires (OpId == 27) + { return OpId; } + + friend constexpr int + operator>>=(BinaryOps, BinaryOps) noexcept requires (OpId == 28) + { return OpId; } + }; +} + +namespace member_ops +{ + template<int OpId> + struct BinaryOps + { + constexpr int + operator+(BinaryOps) const noexcept requires (OpId == 0) + { return OpId; } + + constexpr int + operator-(BinaryOps) const noexcept requires (OpId == 1) + { return OpId; } + + constexpr int + operator*(BinaryOps) const noexcept requires (OpId == 2) + { return OpId; } + + constexpr int + operator/(BinaryOps) const noexcept requires (OpId == 3) + { return OpId; } + + constexpr int + operator%(BinaryOps) const noexcept requires (OpId == 4) + { return OpId; } + + constexpr int + operator<<(BinaryOps) const noexcept requires (OpId == 5) + { return OpId; } + + constexpr int + operator>>(BinaryOps) const noexcept requires (OpId == 6) + { return OpId; } + + constexpr int + operator&(BinaryOps) const noexcept requires (OpId == 7) + { return OpId; } + + constexpr int + operator|(BinaryOps) const noexcept requires (OpId == 8) + { return OpId; } + + constexpr int + operator^(BinaryOps) const noexcept requires (OpId == 9) + { return OpId; } + + constexpr int + operator&&(BinaryOps) const noexcept requires (OpId == 10) + { return OpId; } + + constexpr int + operator||(BinaryOps) const noexcept requires (OpId == 11) + { return OpId; } + + constexpr int + operator<=>(BinaryOps) const noexcept requires (OpId == 12) + { return OpId; } + + constexpr int + operator<(BinaryOps) const noexcept requires (OpId == 13) + { return OpId; } + + constexpr int + operator<=(BinaryOps) const noexcept requires (OpId == 14) + { return OpId; } + + constexpr int + operator==(BinaryOps) const noexcept requires (OpId == 15) + { return OpId; } + + constexpr int + operator!=(BinaryOps) const noexcept requires (OpId == 16) + { return OpId; } + + constexpr int + operator>(BinaryOps) const noexcept requires (OpId == 17) + { return OpId; } + + constexpr int + operator>=(BinaryOps) const noexcept requires (OpId == 18) + { return OpId; } + + constexpr int + operator+=(BinaryOps) const noexcept requires (OpId == 19) + { return OpId; } + + constexpr int + operator-=(BinaryOps) const noexcept requires (OpId == 20) + { return OpId; } + + constexpr int + operator*=(BinaryOps) const noexcept requires (OpId == 21) + { return OpId; } + + constexpr int + operator/=(BinaryOps) const noexcept requires (OpId == 22) + { return OpId; } + + constexpr int + operator%=(BinaryOps) const noexcept requires (OpId == 23) + { return OpId; } + + constexpr int + operator&=(BinaryOps) const noexcept requires (OpId == 24) + { return OpId; } + + constexpr int + operator|=(BinaryOps) const noexcept requires (OpId == 25) + { return OpId; } + + constexpr int + operator^=(BinaryOps) const noexcept requires (OpId == 26) + { return OpId; } + + constexpr int + operator<<=(BinaryOps) const noexcept requires (OpId == 27) + { return OpId; } + + constexpr int + operator>>=(BinaryOps) const noexcept requires (OpId == 28) + { return OpId; } + }; +} + +constexpr size_t n_binary_ops = 29; + +template<template<int> typename Ops, int OpId> + constexpr void + test_binary_operator() + { + auto cx = std::cw<Ops<OpId>{}>; + auto cy = std::cw<Ops<OpId>{}>; + + auto check = [](auto c) + { + VERIFY(c == OpId); + static_assert(std::same_as<decltype(c), std::constant_wrapper<OpId>>); + }; + + if constexpr (OpId == 0) + check(cx + cy); + if constexpr (OpId == 1) + check(cx - cy); + if constexpr (OpId == 2) + check(cx * cy); + if constexpr (OpId == 3) + check(cx / cy); + if constexpr (OpId == 4) + check(cx % cy); + if constexpr (OpId == 5) + check(cx << cy); + if constexpr (OpId == 6) + check(cx >> cy); + if constexpr (OpId == 7) + check(cx & cy); + if constexpr (OpId == 8) + check(cx | cy); + if constexpr (OpId == 10) + check(cx && cy); + if constexpr (OpId == 11) + check(cx || cy); + if constexpr (OpId == 12) + check(cx <=> cy); + if constexpr (OpId == 13) + check(cx < cy); + if constexpr (OpId == 14) + check(cx <= cy); + if constexpr (OpId == 15) + check(cx == cy); + if constexpr (OpId == 16) + check(cx != cy); + if constexpr (OpId == 17) + check(cx > cy); + if constexpr (OpId == 18) + check(cx >= cy); + if constexpr (OpId == 19) + check(cx += cy); + if constexpr (OpId == 20) + check(cx -= cy); + if constexpr (OpId == 21) + check(cx *= cy); + if constexpr (OpId == 22) + check(cx /= cy); + if constexpr (OpId == 23) + check(cx %= cy); + if constexpr (OpId == 24) + check(cx &= cy); + if constexpr (OpId == 25) + check(cx |= cy); + if constexpr (OpId == 26) + check(cx ^= cy); + if constexpr (OpId == 27) + check(cx <<= cy); + if constexpr (OpId == 28) + check(cx >>= cy); + static_assert(n_binary_ops == 29); + } + +template<template<int> typename Ops, int OpId> + constexpr void + test_mixed_binary_operators() + { + constexpr auto x = Ops<OpId>{}; + auto cx = std::cw<x>; + constexpr auto y = Ops<OpId>{}; + auto cy = std::cw<y>; + + auto check = [](auto vc, auto cv) + { + auto impl = [](auto c) + { + VERIFY(c == OpId); + static_assert(std::same_as<decltype(c), int>); + }; + + impl(vc); + impl(cv); + }; + + if constexpr (OpId == 0) + check(x + cy, cx + y); + if constexpr (OpId == 1) + check(x - cy, cx - y); + if constexpr (OpId == 2) + check(x * cy, cx * y); + if constexpr (OpId == 3) + check(x / cy, cx / y); + if constexpr (OpId == 4) + check(x % cy, cx % y); + if constexpr (OpId == 5) + check(x << cy, cx << y); + if constexpr (OpId == 6) + check(x >> cy, cx >> y); + if constexpr (OpId == 7) + check(x & cy, cx & y); + if constexpr (OpId == 8) + check(x | cy, cx | y); + if constexpr (OpId == 10) + check(x && cy, cx && y); + if constexpr (OpId == 11) + check(x || cy, cx || y); + if constexpr (OpId == 12) + check(x <=> cy, cx <=> y); + if constexpr (OpId == 13) + check(x < cy, cx < y); + if constexpr (OpId == 14) + check(x <= cy, cx <= y); + if constexpr (OpId == 15) + check(x == cy, cx == y); + if constexpr (OpId == 16) + check(x != cy, cx != y); + if constexpr (OpId == 17) + check(x > cy, cx > y); + if constexpr (OpId == 18) + check(x >= cy, cx >= y); + if constexpr (OpId == 19) + check(x += cy, cx += y); + if constexpr (OpId == 20) + check(x -= cy, cx -= y); + if constexpr (OpId == 21) + check(x *= cy, cx *= y); + if constexpr (OpId == 22) + check(x /= cy, cx /= y); + if constexpr (OpId == 23) + check(x %= cy, cx %= y); + if constexpr (OpId == 24) + check(x &= cy, cx &= y); + if constexpr (OpId == 25) + check(x |= cy, cx |= y); + if constexpr (OpId == 26) + check(x ^= cy, cx ^= y); + if constexpr (OpId == 27) + check(x <<= cy, cx <<= y); + if constexpr (OpId == 28) + check(x >>= cy, cx >>= y); + static_assert(n_binary_ops == 29); + } + +constexpr void +test_binary_operators_all() +{ + auto run = []<size_t... Idx>(std::integer_sequence<size_t, Idx...>) + { + (test_binary_operator<free_ops::BinaryOps, Idx>(), ...); + (test_mixed_binary_operators<free_ops::BinaryOps, Idx>(), ...); + (test_binary_operator<member_ops::BinaryOps, Idx>(), ...); + }; + run(std::make_index_sequence<n_binary_ops>()); +} + +constexpr bool +test_all() +{ + test_unary_operators_all(); + test_binary_operators_all(); + return true; +} + +int +main() +{ + test_all(); + return 0; +} diff --git a/libstdc++-v3/testsuite/20_util/constant_wrapper/op_comma_neg.cc b/libstdc++-v3/testsuite/20_util/constant_wrapper/op_comma_neg.cc new file mode 100644 index 0000000..4384e92 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/op_comma_neg.cc @@ -0,0 +1,14 @@ +// { dg-do compile { target c++26 } } +#include <type_traits> + +constexpr void +test_comma_same_types() +{ + (std::cw<1>, std::cw<2>); // { dg-error "use of deleted function" } +} + +constexpr void +test_comma_different_types() +{ + (std::cw<1>, std::cw<2.0>); // { dg-error "use of deleted function" } +} diff --git a/libstdc++-v3/testsuite/20_util/constant_wrapper/other_wrappers.cc b/libstdc++-v3/testsuite/20_util/constant_wrapper/other_wrappers.cc new file mode 100644 index 0000000..3c3cfaf --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/other_wrappers.cc @@ -0,0 +1,75 @@ +// { dg-do run { target c++26 } } +#include <type_traits> +#include <concepts> + +#include <testsuite_hooks.h> + +template<typename Type, Type Value> + struct ConstWrapper + { + constexpr static Type value = Value; + }; + +constexpr void +check_same(auto actual, auto expected) +{ + VERIFY(actual == expected); + static_assert(std::same_as<decltype(actual), decltype(expected)>); +} + +constexpr void +test_mix_integer_constant() +{ + auto i4 = std::integral_constant<int, 4>{}; + auto c3 = std::cw<3>; + auto w2 = ConstWrapper<int, 2>{}; + + check_same(i4 + c3, std::cw<7>); + check_same(c3 + i4, std::cw<7>); + check_same(c3 + w2, std::cw<5>); + check_same(w2 + c3, std::cw<5>); +} + +constexpr void +test_array() +{ + constexpr double x[] = {1.1, 2.2, 3.3}; + auto cx = std::cw<x>; + auto i2 = std::integral_constant<int, 2>{}; + auto w2 = ConstWrapper<int, 2>{}; + + check_same(x[i2], x[2]); + check_same(cx[i2], std::cw<x[2]>); + check_same(cx[w2], std::cw<x[2]>); +} + +constexpr void +test_function_object() +{ + auto cadd = std::cw<[](int i, int j) { return i + j; }>; + auto i4 = std::integral_constant<int, 4>{}; + auto c3 = std::cw<3>; + auto w2 = ConstWrapper<int, 2>{}; + + check_same(cadd(i4, c3), std::cw<7>); + check_same(cadd(c3, i4), std::cw<7>); + check_same(cadd(w2, c3), std::cw<5>); + check_same(cadd(c3, w2), std::cw<5>); +} + +constexpr bool +test_all() +{ + test_mix_integer_constant(); + test_array(); + test_function_object(); + return true; +} + +int +main() +{ + test_all(); + static_assert(test_all()); + return 0; +} diff --git a/libstdc++-v3/testsuite/20_util/constant_wrapper/version.cc b/libstdc++-v3/testsuite/20_util/constant_wrapper/version.cc new file mode 100644 index 0000000..4fee615 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/constant_wrapper/version.cc @@ -0,0 +1,11 @@ +// { dg-do preprocess { target c++26 } } +// { dg-add-options no_pch } + +#include <type_traits> + +#ifndef __cpp_lib_constant_wrapper +#error "Feature test macro __cpp_lib_constant_wrapper is missing for <type_traits>" +#if __cpp_lib_constant_wrapper < 202506L +#error "Feature test macro __cpp_lib_constant_wrapper has the wrong value" +#endif +#endif diff --git a/libstdc++-v3/testsuite/20_util/copyable_function/call.cc b/libstdc++-v3/testsuite/20_util/copyable_function/call.cc new file mode 100644 index 0000000..605422d --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/copyable_function/call.cc @@ -0,0 +1,248 @@ +// { dg-do run { target c++26 } } +// { dg-require-effective-target hosted } + +#include <functional> +#include <utility> +#include <testsuite_hooks.h> + +using std::copyable_function; + +using std::is_same_v; +using std::is_invocable_v; +using std::is_nothrow_invocable_v; +using std::invoke_result_t; + +// Check return types +static_assert( is_same_v<void, invoke_result_t<copyable_function<void()>>> ); +static_assert( is_same_v<int, invoke_result_t<copyable_function<int()>>> ); +static_assert( is_same_v<int&, invoke_result_t<copyable_function<int&()>>> ); + +// With const qualifier +static_assert( ! is_invocable_v< copyable_function<void()> const > ); +static_assert( ! is_invocable_v< copyable_function<void()> const &> ); +static_assert( is_invocable_v< copyable_function<void() const> > ); +static_assert( is_invocable_v< copyable_function<void() const> &> ); +static_assert( is_invocable_v< copyable_function<void() const> const > ); +static_assert( is_invocable_v< copyable_function<void() const> const &> ); + +// With no ref-qualifier +static_assert( is_invocable_v< copyable_function<void()> > ); +static_assert( is_invocable_v< copyable_function<void()> &> ); +static_assert( is_invocable_v< copyable_function<void() const> > ); +static_assert( is_invocable_v< copyable_function<void() const> &> ); +static_assert( is_invocable_v< copyable_function<void() const> const > ); +static_assert( is_invocable_v< copyable_function<void() const> const &> ); + +// With & ref-qualifier +static_assert( ! is_invocable_v< copyable_function<void()&> > ); +static_assert( is_invocable_v< copyable_function<void()&> &> ); +static_assert( is_invocable_v< copyable_function<void() const&> > ); +static_assert( is_invocable_v< copyable_function<void() const&> &> ); +static_assert( is_invocable_v< copyable_function<void() const&> const > ); +static_assert( is_invocable_v< copyable_function<void() const&> const &> ); + +// With && ref-qualifier +static_assert( is_invocable_v< copyable_function<void()&&> > ); +static_assert( ! is_invocable_v< copyable_function<void()&&> &> ); +static_assert( is_invocable_v< copyable_function<void() const&&> > ); +static_assert( ! is_invocable_v< copyable_function<void() const&&> &> ); +static_assert( is_invocable_v< copyable_function<void() const&&> const > ); +static_assert( ! is_invocable_v< copyable_function<void() const&&> const &> ); + +// With noexcept-specifier +static_assert( ! is_nothrow_invocable_v< copyable_function<void()> > ); +static_assert( ! is_nothrow_invocable_v< copyable_function<void() noexcept(false)> > ); +static_assert( is_nothrow_invocable_v< copyable_function<void() noexcept> > ); +static_assert( is_nothrow_invocable_v< copyable_function<void()& noexcept>& > ); + +void +test01() +{ + struct F + { + int operator()() { return 0; } + int operator()() const { return 1; } + }; + + copyable_function<int()> f0{F{}}; + VERIFY( f0() == 0 ); + VERIFY( std::move(f0)() == 0 ); + + copyable_function<int() const> f1{F{}}; + VERIFY( f1() == 1 ); + VERIFY( std::as_const(f1)() == 1 ); + VERIFY( std::move(f1)() == 1 ); + VERIFY( std::move(std::as_const(f1))() == 1 ); + + copyable_function<int()&> f2{F{}}; + VERIFY( f2() == 0 ); + // Not rvalue-callable: std::move(f2)() + + copyable_function<int() const&> f3{F{}}; + VERIFY( f3() == 1 ); + VERIFY( std::as_const(f3)() == 1 ); + VERIFY( std::move(f3)() == 1 ); + VERIFY( std::move(std::as_const(f3))() == 1 ); + + copyable_function<int()&&> f4{F{}}; + // Not lvalue-callable: f4() + VERIFY( std::move(f4)() == 0 ); + + copyable_function<int() const&&> f5{F{}}; + // Not lvalue-callable: f5() + VERIFY( std::move(f5)() == 1 ); + VERIFY( std::move(std::as_const(f5))() == 1 ); +} + +void +test02() +{ + struct F + { + int operator()() & { return 0; } + int operator()() && { return 1; } + }; + + copyable_function<int()> f0{F{}}; + VERIFY( f0() == 0 ); + VERIFY( std::move(f0)() == 0 ); + + copyable_function<int()&&> f1{F{}}; + // Not lvalue callable: f1() + VERIFY( std::move(f1)() == 1 ); + + copyable_function<int()&> f2{F{}}; + VERIFY( f2() == 0 ); + // Not rvalue-callable: std::move(f2)() +} + +void +test03() +{ + struct F + { + int operator()() const & { return 0; } + int operator()() && { return 1; } + }; + + copyable_function<int()> f0{F{}}; + VERIFY( f0() == 0 ); + VERIFY( std::move(f0)() == 0 ); + + copyable_function<int()&&> f1{F{}}; + // Not lvalue callable: f1() + VERIFY( std::move(f1)() == 1 ); + + copyable_function<int() const> f2{F{}}; + VERIFY( f2() == 0 ); + VERIFY( std::as_const(f2)() == 0 ); + VERIFY( std::move(f2)() == 0 ); + VERIFY( std::move(std::as_const(f2))() == 0 ); + + copyable_function<int() const &&> f3{F{}}; + // Not lvalue callable: f3() + VERIFY( std::move(f3)() == 0 ); + VERIFY( std::move(std::as_const(f3))() == 0 ); + + copyable_function<int() const &> f4{F{}}; + VERIFY( f4() == 0 ); + VERIFY( std::as_const(f4)() == 0 ); + // Not rvalue-callable: std::move(f4)() +} + +void +test04() +{ + struct F + { + int operator()() & { return 0; } + int operator()() && { return 1; } + int operator()() const & { return 2; } + int operator()() const && { return 3; } + }; + + copyable_function<int()> f0{F{}}; + VERIFY( f0() == 0 ); + VERIFY( std::move(f0)() == 0 ); + + copyable_function<int()&> f1{F{}}; + VERIFY( f1() == 0 ); + // Not rvalue-callable: std::move(f1)() + + copyable_function<int()&&> f2{F{}}; + // Not lvalue callable: f2() + VERIFY( std::move(f2)() == 1 ); + + copyable_function<int() const> f3{F{}}; + VERIFY( f3() == 2 ); + VERIFY( std::as_const(f3)() == 2 ); + VERIFY( std::move(f3)() == 2 ); + VERIFY( std::move(std::as_const(f3))() == 2 ); + + copyable_function<int() const &> f4{F{}}; + VERIFY( f4() == 2 ); + VERIFY( std::as_const(f4)() == 2 ); + // Not rvalue-callable: std::move(f4)() + + copyable_function<int() const &&> f5{F{}}; + // Not lvalue callable: f5() + VERIFY( std::move(f5)() == 3 ); + VERIFY( std::move(std::as_const(f5))() == 3 ); +} + +void +test05() +{ + int (*fp)() = [] { return 0; }; + copyable_function<int()> f0{fp}; + VERIFY( f0() == 0 ); + VERIFY( std::move(f0)() == 0 ); + + const copyable_function<int() const> f1{fp}; + VERIFY( f1() == 0 ); + VERIFY( std::move(f1)() == 0 ); +} + +struct Incomplete; +enum CompleteEnum : int; + +void +test_params() +{ + std::copyable_function<void(Incomplete&)> f1; + std::copyable_function<void(Incomplete&&)> f2; + std::copyable_function<void(CompleteEnum)> f4; +} + +struct EmptyIdFunc +{ + EmptyIdFunc* operator()() + { return this; } +}; + +struct Composed : EmptyIdFunc +{ + std::move_only_function<EmptyIdFunc*()> nested; +}; + +void +test_aliasing() +{ + Composed c; + c.nested = EmptyIdFunc{}; + + EmptyIdFunc* baseAddr = c(); + EmptyIdFunc* nestedAddr = c.nested(); + VERIFY( baseAddr != nestedAddr ); +}; + +int main() +{ + test01(); + test02(); + test03(); + test04(); + test05(); + test_params(); + test_aliasing(); +} diff --git a/libstdc++-v3/testsuite/20_util/copyable_function/cons.cc b/libstdc++-v3/testsuite/20_util/copyable_function/cons.cc new file mode 100644 index 0000000..8d422dc --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/copyable_function/cons.cc @@ -0,0 +1,126 @@ +// { dg-do compile { target c++26 } } +// { dg-require-effective-target hosted } +// { dg-add-options no_pch } + +#include <functional> + +#ifndef __cpp_lib_copyable_function +# error "Feature-test macro for copyable_function missing in <functional>" +#elif __cpp_lib_copyable_function != 202306L +# error "Feature-test macro for copyable_function has wrong value in <functional>" +#endif + +using std::copyable_function; + +using std::is_constructible_v; +using std::is_copy_constructible_v; +using std::is_nothrow_default_constructible_v; +using std::is_nothrow_move_constructible_v; +using std::is_nothrow_constructible_v; +using std::nullptr_t; +using std::in_place_type_t; + +static_assert( is_nothrow_default_constructible_v<copyable_function<void()>> ); +static_assert( is_nothrow_constructible_v<copyable_function<void()>, nullptr_t> ); +static_assert( is_nothrow_move_constructible_v<copyable_function<void()>> ); +static_assert( is_copy_constructible_v<copyable_function<void()>> ); + +static_assert( is_constructible_v<copyable_function<void()>, void()> ); +static_assert( is_constructible_v<copyable_function<void()>, void(&)()> ); +static_assert( is_constructible_v<copyable_function<void()>, void(*)()> ); +static_assert( is_constructible_v<copyable_function<void()>, int()> ); +static_assert( is_constructible_v<copyable_function<void()>, int(&)()> ); +static_assert( is_constructible_v<copyable_function<void()>, int(*)()> ); +static_assert( ! is_constructible_v<copyable_function<void()>, void(int)> ); +static_assert( is_constructible_v<copyable_function<void(int)>, void(int)> ); + +static_assert( is_constructible_v<copyable_function<void(int)>, + in_place_type_t<void(*)(int)>, void(int)> ); + +static_assert( is_constructible_v<copyable_function<void()>, + void() noexcept> ); +static_assert( is_constructible_v<copyable_function<void() noexcept>, + void() noexcept> ); +static_assert( ! is_constructible_v<copyable_function<void() noexcept>, + void() > ); + +struct Q +{ + void operator()() const &; + void operator()() &&; +}; + +static_assert( is_constructible_v<copyable_function<void()>, Q> ); +static_assert( is_constructible_v<copyable_function<void() const>, Q> ); +static_assert( is_constructible_v<copyable_function<void() &>, Q> ); +static_assert( is_constructible_v<copyable_function<void() const &>, Q> ); +static_assert( is_constructible_v<copyable_function<void() &&>, Q> ); +static_assert( is_constructible_v<copyable_function<void() const &&>, Q> ); + +struct R +{ + void operator()() &; + void operator()() &&; +}; + +static_assert( is_constructible_v<copyable_function<void()>, R> ); +static_assert( is_constructible_v<copyable_function<void()&>, R> ); +static_assert( is_constructible_v<copyable_function<void()&&>, R> ); +static_assert( ! is_constructible_v<copyable_function<void() const>, R> ); +static_assert( ! is_constructible_v<copyable_function<void() const&>, R> ); +static_assert( ! is_constructible_v<copyable_function<void() const&&>, R> ); + +// The following nothrow-constructible guarantees are a GCC extension, +// not required by the standard. + +static_assert( is_nothrow_constructible_v<copyable_function<void()>, void()> ); +static_assert( is_nothrow_constructible_v<copyable_function<void(int)>, + in_place_type_t<void(*)(int)>, + void(int)> ); + +// These types are all small and nothrow move constructible +struct F { void operator()(); }; +struct G { void operator()() const; }; +static_assert( is_nothrow_constructible_v<copyable_function<void()>, F> ); +static_assert( is_nothrow_constructible_v<copyable_function<void()>, G> ); +static_assert( is_nothrow_constructible_v<copyable_function<void() const>, G> ); + +struct H { + H(int); + H(int, int) noexcept; + void operator()() noexcept; +}; +static_assert( is_nothrow_constructible_v<copyable_function<void()>, H> ); +static_assert( is_nothrow_constructible_v<copyable_function<void() noexcept>, + H> ); +static_assert( ! is_nothrow_constructible_v<copyable_function<void() noexcept>, + in_place_type_t<H>, int> ); +static_assert( is_nothrow_constructible_v<copyable_function<void() noexcept>, + in_place_type_t<H>, int, int> ); + +struct I { + I(int, const char*); + I(std::initializer_list<char>); + int operator()() const noexcept; +}; + +static_assert( is_constructible_v<copyable_function<void()>, + std::in_place_type_t<I>, + int, const char*> ); +static_assert( is_constructible_v<copyable_function<void()>, + std::in_place_type_t<I>, + std::initializer_list<char>> ); + +void +test_instantiation() +{ + // Instantiate the constructor bodies + copyable_function<void()> f0; + copyable_function<void()> f1(nullptr); + copyable_function<void()> f2( I(1, "two") ); + copyable_function<void()> f3(std::in_place_type<I>, 3, "four"); + copyable_function<void()> f4(std::in_place_type<I>, // PR libstdc++/102825 + { 'P', 'R', '1', '0', '2', '8', '2', '5'}); + auto f5 = std::move(f4); + f4 = std::move(f5); +} diff --git a/libstdc++-v3/testsuite/20_util/copyable_function/conv.cc b/libstdc++-v3/testsuite/20_util/copyable_function/conv.cc new file mode 100644 index 0000000..11c839b --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/copyable_function/conv.cc @@ -0,0 +1,286 @@ +// { dg-do run { target c++26 } } +// { dg-require-effective-target hosted } + +#include <functional> +#include <string_view> +#include <testsuite_hooks.h> + +using std::copyable_function; + +static_assert( !std::is_constructible_v<std::copyable_function<void()>, + std::copyable_function<void()&>> ); +static_assert( !std::is_constructible_v<std::copyable_function<void()>, + std::copyable_function<void()&&>> ); +static_assert( !std::is_constructible_v<std::copyable_function<void()&>, + std::copyable_function<void()&&>> ); +static_assert( !std::is_constructible_v<std::copyable_function<void() const>, + std::copyable_function<void()>> ); + +using FuncType = int(int); + +// Top level const qualifiers are ignored and decay is performed in parameters +// of function_types. +static_assert( std::is_same_v<std::copyable_function<void(int const)>, + std::copyable_function<void(int)>> ); +static_assert( std::is_same_v<std::copyable_function<void(int[2])>, + std::copyable_function<void(int*)>>); +static_assert( std::is_same_v<std::copyable_function<void(int[])>, + std::copyable_function<void(int*)>>); +static_assert( std::is_same_v<std::copyable_function<void(int const[5])>, + std::copyable_function<void(int const*)>>); +static_assert( std::is_same_v<std::copyable_function<void(FuncType)>, + std::copyable_function<void(FuncType*)>>); + +// Non-trivial args, guarantess that type is not passed by copy +struct CountedArg +{ + CountedArg() = default; + CountedArg(const CountedArg& f) noexcept : counter(f.counter) { ++counter; } + CountedArg& operator=(CountedArg&&) = delete; + + int counter = 0; +}; +CountedArg const c; + +// When copyable_function or move_only_function is constructed from other copyable_function, +// the compiler can avoid double indirection per C++26 [func.wrap.general] p2. + +void +test01() +{ + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::copyable_function<int(CountedArg) const noexcept> c1(f); + using CF = std::copyable_function<int(CountedArg) const noexcept>; + VERIFY( c1(c) == 1 ); + + std::copyable_function<int(CountedArg) const> c2a(c1); + VERIFY( c2a(c) == 1 ); + + std::copyable_function<int(CountedArg) const> c2b(static_cast<CF>(c1)); + VERIFY( c2b(c) == 1 ); + + std::move_only_function<int(CountedArg) const> m2a(c1); + VERIFY( m2a(c) == 1 ); + + std::move_only_function<int(CountedArg) const> m2b(static_cast<CF>(c1)); + VERIFY( m2b(c) == 1 ); + + std::copyable_function<int(CountedArg)> c3a(c1); + VERIFY( c3a(c) == 1 ); + + std::copyable_function<int(CountedArg)> c3b(static_cast<CF>(c1)); + VERIFY( c3b(c) == 1 ); + + std::move_only_function<int(CountedArg)> m3a(c1); + VERIFY( m3a(c) == 1 ); + + std::move_only_function<int(CountedArg)> m3b(static_cast<CF>(c1)); + VERIFY( m3b(c) == 1 ); + + // Invokers internally uses Counted&& for non-trivial types, + // sinature remain compatible. + std::copyable_function<int(CountedArg&&)> c4a(c1); + VERIFY( c4a({}) == 0 ); + + std::copyable_function<int(CountedArg&&)> c4b(static_cast<CF>(c1)); + VERIFY( c4b({}) == 0 ); + + std::move_only_function<int(CountedArg&&)> m4a(c1); + VERIFY( m4a({}) == 0 ); + + std::move_only_function<int(CountedArg&&)> m4b(static_cast<CF>(c1)); + VERIFY( m4b({}) == 0 ); + + std::copyable_function<int(CountedArg&&)&> c5a(c1); + VERIFY( c5a({}) == 0 ); + + std::copyable_function<int(CountedArg&&)&&> c5b(static_cast<CF>(c1)); + VERIFY( std::move(c5b)({}) == 0 ); + + std::move_only_function<int(CountedArg&&)&> m5a(c1); + VERIFY( m5a({}) == 0 ); + + std::move_only_function<int(CountedArg&&)&&> m5b(static_cast<CF>(c1)); + VERIFY( std::move(m5b)({}) == 0 ); + + // Incompatible signatures + std::copyable_function<long(CountedArg) const noexcept> c6a(c1); + VERIFY( c6a(c) == 2 ); + + std::copyable_function<long(CountedArg) const noexcept> c6b(static_cast<CF>(c1)); + VERIFY( c6b(c) == 2 ); + + std::move_only_function<long(CountedArg) const noexcept> m6a(c1); + VERIFY( m6a(c) == 2 ); + + std::move_only_function<long(CountedArg) const noexcept> m6b(static_cast<CF>(c1)); + VERIFY( m6b(c) == 2 ); +} + +void +test02() +{ + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::copyable_function<int(CountedArg) const noexcept> c1(f); + using CF = std::copyable_function<int(CountedArg) const noexcept>; + VERIFY( c1(c) == 1 ); + + std::copyable_function<int(CountedArg) const> c2; + c2 = c1; + VERIFY( c2(c) == 1 ); + c2 = static_cast<CF>(c1); + VERIFY( c2(c) == 1 ); + + std::move_only_function<int(CountedArg) const> m2; + m2 = c1; + VERIFY( m2(c) == 1 ); + m2 = static_cast<CF>(c1); + VERIFY( m2(c) == 1 ); + + // Incompatible signatures + std::copyable_function<long(CountedArg) const noexcept> c3; + c3 = c1; + VERIFY( c3(c) == 2 ); + c3 = static_cast<CF>(c1); + VERIFY( c3(c) == 2 ); + + std::move_only_function<long(CountedArg) const noexcept> m3; + m3 = c1; + VERIFY( m3(c) == 2 ); + m3 = static_cast<CF>(c1); + VERIFY( m3(c) == 2 ); +} + +void +test03() +{ + std::copyable_function<int(long) const noexcept> c1; + VERIFY( c1 == nullptr ); + + std::copyable_function<int(long) const> c2(c1); + VERIFY( c2 == nullptr ); + c2 = c1; + VERIFY( c2 == nullptr ); + c2 = std::move(c1); + VERIFY( c2 == nullptr ); + + std::copyable_function<bool(int) const> c3(std::move(c1)); + VERIFY( c3 == nullptr ); + c3 = c1; + VERIFY( c3 == nullptr ); + c3 = std::move(c1); + VERIFY( c3 == nullptr ); + + // LWG4255 move_only_function constructor should recognize empty + // copyable_functions + std::move_only_function<int(long) const noexcept> m1(c1); + VERIFY( m1 == nullptr ); + m1 = c1; + VERIFY( m1 == nullptr ); + m1 = std::move(c1); + VERIFY( m1 == nullptr ); + + std::move_only_function<int(long) const> m2(c1); + VERIFY( m2 == nullptr ); + m2 = c1; + VERIFY( m2 == nullptr ); + m2 = std::move(c1); + VERIFY( m2 == nullptr ); + + std::move_only_function<bool(int) const> m3(std::move(c1)); + VERIFY( m3 == nullptr ); + m3 = c1; + VERIFY( m3 == nullptr ); + m3 = std::move(c1); + VERIFY( m3 == nullptr ); +} + +void +test04() +{ + struct F + { + int operator()(CountedArg const& arg) noexcept + { return arg.counter; } + + int operator()(CountedArg const& arg) const noexcept + { return arg.counter + 1000; } + }; + + F f; + std::copyable_function<int(CountedArg) const> c1(f); + VERIFY( c1(c) == 1001 ); + + // Call const overload as std::copyable_function<int(CountedArg) const> + // inside td::copyable_function<int(CountedArg)> would do. + std::copyable_function<int(CountedArg)> c2(c1); + VERIFY( c2(c) == 1001 ); + std::move_only_function<int(CountedArg)> m2(c1); + VERIFY( m2(c) == 1001 ); + + std::copyable_function<int(CountedArg)> m3(f); + VERIFY( m3(c) == 1 ); +} + +void +test05() +{ + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::copyable_function<int(CountedArg)> w1(f); + // copyable_function stores copyable_function due incompatibile signatures + std::copyable_function<int(CountedArg const&)> w2(std::move(w1)); + // copy is made when passing to int(CountedArg) + VERIFY( w2(c) == 1 ); + // wrapped 3 times + w1 = std::move(w2); + VERIFY( w1(c) == 2 ); + // wrapped 4 times + w2 = std::move(w1); + VERIFY( w2(c) == 2 ); + // wrapped 5 times + w1 = std::move(w2); + VERIFY( w1(c) == 3 ); +} + +void +test06() +{ + // No special interoperability with std::function + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::function<int(CountedArg)> f1(f); + std::copyable_function<int(CountedArg) const> c1(std::move(f1)); + VERIFY( c1(c) == 2 ); + + std::copyable_function<int(CountedArg) const> c2(f); + std::function<int(CountedArg)> f2(c2); + VERIFY( f2(c) == 2 ); +} + +void +test07() +{ + // Scalar types and small trivially move constructible types are passed + // by value to invoker. So int&& signature is not compatible for such types. + auto fi = [](CountedArg const& arg, int) noexcept { return arg.counter; }; + std::copyable_function<int(CountedArg, int) const noexcept> ci1(fi); + VERIFY( ci1(c, 0) == 1 ); + std::copyable_function<int(CountedArg, int&&) const noexcept> ci2(ci1); + VERIFY( ci2(c, 0) == 2 ); + + auto fs = [](CountedArg const& arg, std::string_view) noexcept { return arg.counter; }; + std::copyable_function<int(CountedArg, std::string_view) const noexcept> cs1(fs); + VERIFY( cs1(c, "") == 1 ); + std::copyable_function<int(CountedArg, std::string_view&&) const noexcept> cs2(cs1); + VERIFY( cs2(c, "") == 2 ); +} + +int main() +{ + test01(); + test02(); + test03(); + test04(); + test05(); + test06(); + test07(); +} diff --git a/libstdc++-v3/testsuite/20_util/copyable_function/copy.cc b/libstdc++-v3/testsuite/20_util/copyable_function/copy.cc new file mode 100644 index 0000000..6445a27 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/copyable_function/copy.cc @@ -0,0 +1,154 @@ +// { dg-do run { target c++26 } } +// { dg-require-effective-target hosted } + +#include <functional> +#include <testsuite_hooks.h> + +using std::copyable_function; + +void +test01() +{ + // Small type with non-throwing move constructor. Not allocated on the heap. + struct F + { + F() = default; + F(const F& f) : counters(f.counters) { ++counters.copy; } + F(F&& f) noexcept : counters(f.counters) { ++counters.move; } + + F& operator=(F&&) = delete; + + struct Counters + { + int copy = 0; + int move = 0; + } counters; + + const Counters& operator()() const { return counters; } + }; + + F f; + std::copyable_function<const F::Counters&() const> m1(f); + VERIFY( m1().copy == 1 ); + VERIFY( m1().move == 0 ); + + // This will copy construct a new target object + auto m2 = m1; + VERIFY( m1 != nullptr && m2 != nullptr ); + VERIFY( m2().copy == 2 ); + VERIFY( m2().move == 0 ); + + m1 = m2; + VERIFY( m1 != nullptr && m2 != nullptr ); + VERIFY( m1().copy == 3 ); + VERIFY( m1().move == 1 ); // Copies object first and then swaps + + m1 = m1; + VERIFY( m1 != nullptr && m2 != nullptr ); + VERIFY( m1().copy == 4 ); + VERIFY( m1().move == 2 ); // Copies object first and then swaps + + m2 = f; + VERIFY( m2().copy == 1 ); + VERIFY( m2().move == 1 ); // Copy construct target object, then swap into m2. +} + +void +test02() +{ + // Move constructor is potentially throwing. Allocated on the heap. + struct F + { + F() = default; + F(const F& f) noexcept : counters(f.counters) { ++counters.copy; } + F(F&& f) noexcept(false) : counters(f.counters) { ++counters.move; } + + F& operator=(F&&) = delete; + + struct Counters + { + int copy = 0; + int move = 0; + } counters; + + Counters operator()() const noexcept { return counters; } + }; + + F f; + std::copyable_function<F::Counters() const> m1(f); + VERIFY( m1().copy == 1 ); + VERIFY( m1().move == 0 ); + + // The target object is on the heap, but we need to allocate new one + auto m2 = m1; + VERIFY( m1 != nullptr && m2 != nullptr ); + VERIFY( m2().copy == 2 ); + VERIFY( m2().move == 0 ); + + m1 = m2; + VERIFY( m1 != nullptr && m2 != nullptr ); + VERIFY( m1().copy == 3 ); + VERIFY( m1().move == 0 ); + + m1 = m1; + VERIFY( m1 != nullptr && m2 != nullptr ); + VERIFY( m1().copy == 4 ); + VERIFY( m1().move == 0 ); + + m2 = f; + VERIFY( m2().copy == 1 ); + VERIFY( m2().move == 0 ); +} + +void +test03() +{ + // Small type with non-throwing, but not non-trivial move constructor. + struct F + { + F(int i) noexcept : id(i) {} + F(const F& f) : id(f.id) + { if (id == 3) throw id; } + F(F&& f) noexcept : id(f.id) { } + + int operator()() const + { return id; } + + int id; + }; + + std::copyable_function<int() const> m1(std::in_place_type<F>, 1); + const std::copyable_function<int() const> m2(std::in_place_type<F>, 2); + const std::copyable_function<int() const> m3(std::in_place_type<F>, 3); + + try + { + auto mc = m3; + VERIFY( false ); + } + catch(int i) + { + VERIFY( i == 3 ); + } + + m1 = m2; + VERIFY( m1() == 2 ); + + try + { + m1 = m3; + VERIFY( false ); + } + catch (int i) + { + VERIFY( i == 3 ); + } + VERIFY( m1() == 2 ); +} + +int main() +{ + test01(); + test02(); + test03(); +} diff --git a/libstdc++-v3/testsuite/20_util/copyable_function/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/copyable_function/incomplete_neg.cc new file mode 100644 index 0000000..21ddde0 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/copyable_function/incomplete_neg.cc @@ -0,0 +1,18 @@ +// { dg-do compile { target c++26 } } + +#include <functional> + +struct IncompleteClass; + +using T1 = std::copyable_function<int(IncompleteClass)>::result_type; // { dg-error "here" } +using T2 = std::copyable_function<int(int, IncompleteClass)>::result_type; // { dg-error "here" } + +enum Enum { + x = [] { + // Enum enumeration is incomplete here + using T3 = std::copyable_function<int(Enum)>::result_type; // { dg-error "here" } + return T3(1); + }() +}; + +// { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/20_util/copyable_function/move.cc b/libstdc++-v3/testsuite/20_util/copyable_function/move.cc new file mode 100644 index 0000000..ec9d0d1 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/copyable_function/move.cc @@ -0,0 +1,120 @@ +// { dg-do run { target c++26 } } +// { dg-require-effective-target hosted } + +#include <functional> +#include <testsuite_hooks.h> + +using std::copyable_function; + +void +test01() +{ + // Small type with non-throwing move constructor. Not allocated on the heap. + struct F + { + F() = default; + F(const F& f) : counters(f.counters) { ++counters.copy; } + F(F&& f) noexcept : counters(f.counters) { ++counters.move; } + + F& operator=(F&&) = delete; + + struct Counters + { + int copy = 0; + int move = 0; + } counters; + + const Counters& operator()() const { return counters; } + }; + + F f; + std::copyable_function<const F::Counters&() const> m1(f); + VERIFY( m1().copy == 1 ); + VERIFY( m1().move == 0 ); + + // Standard specifies move assigment as copy and swap + m1 = std::move(m1); + VERIFY( m1 != nullptr ); + VERIFY( m1().copy == 1 ); + VERIFY( m1().move == 0 ); + + // This will move construct a new target object and destroy the old one: + auto m2 = std::move(m1); + VERIFY( m1 == nullptr && m2 != nullptr ); + VERIFY( m2().copy == 1 ); + VERIFY( m2().move == 1 ); + + m1 = std::move(m2); + VERIFY( m1 != nullptr && m2 == nullptr ); + VERIFY( m1().copy == 1 ); + VERIFY( m1().move == 2 ); + + m2 = std::move(f); + VERIFY( m2().copy == 0 ); + VERIFY( m2().move == 2 ); // Move construct target object, then swap into m2. + const int moves = m1().move + m2().move; + // This will do three moves: + swap(m1, m2); + VERIFY( m1().copy == 0 ); + VERIFY( m2().copy == 1 ); + VERIFY( (m1().move + m2().move) == (moves + 3) ); +} + +void +test02() +{ + // Move constructor is potentially throwing. Allocated on the heap. + struct F + { + F() = default; + F(const F& f) noexcept : counters(f.counters) { ++counters.copy; } + F(F&& f) noexcept(false) : counters(f.counters) { ++counters.move; } + + F& operator=(F&&) = delete; + + struct Counters + { + int copy = 0; + int move = 0; + } counters; + + Counters operator()() const noexcept { return counters; } + }; + + F f; + std::copyable_function<F::Counters() const> m1(f); + VERIFY( m1().copy == 1 ); + VERIFY( m1().move == 0 ); + + m1 = std::move(m1); + VERIFY( m1 != nullptr ); + VERIFY( m1().copy == 1 ); + VERIFY( m1().move == 0 ); + + // The target object is on the heap so this just moves a pointer: + auto m2 = std::move(m1); + VERIFY( m1 == nullptr && m2 != nullptr ); + VERIFY( m2().copy == 1 ); + VERIFY( m2().move == 0 ); + + m1 = std::move(m2); + VERIFY( m1 != nullptr && m2 == nullptr ); + VERIFY( m1().copy == 1 ); + VERIFY( m1().move == 0 ); + + m2 = std::move(f); + VERIFY( m2().copy == 0 ); + VERIFY( m2().move == 1 ); + const int moves = m1().move + m2().move; + // This just swaps the pointers, so no moves: + swap(m1, m2); + VERIFY( m1().copy == 0 ); + VERIFY( m2().copy == 1 ); + VERIFY( (m1().move + m2().move) == moves ); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/20_util/duration/114244.cc b/libstdc++-v3/testsuite/20_util/duration/114244.cc index 55a7670..12701bf 100644 --- a/libstdc++-v3/testsuite/20_util/duration/114244.cc +++ b/libstdc++-v3/testsuite/20_util/duration/114244.cc @@ -13,8 +13,8 @@ test_pr114244() { using namespace std::chrono; seconds s; - milliseconds ms; - microseconds us; + milliseconds ms{}; + microseconds us{}; std::istringstream is; diff --git a/libstdc++-v3/testsuite/20_util/duration/io.cc b/libstdc++-v3/testsuite/20_util/duration/io.cc index 0117673..6ada4fc 100644 --- a/libstdc++-v3/testsuite/20_util/duration/io.cc +++ b/libstdc++-v3/testsuite/20_util/duration/io.cc @@ -193,8 +193,8 @@ test_parse() { using namespace std::chrono; seconds s; - milliseconds ms; - microseconds us; + milliseconds ms{}; + microseconds us{}; std::istringstream is(" 2023-07-24 13:05"); VERIFY( is >> parse(" %Y-%m-%d %H:%M", s) ); @@ -289,13 +289,13 @@ test_parse() is.clear(); is.str("0.5"); - std::chrono::duration<double> ds; + std::chrono::duration<double> ds{}; VERIFY( is >> parse("%S", ds) ); VERIFY( ds == 0.5s ); is.clear(); is.str("0.125"); - std::chrono::duration<double, std::milli> dms; + std::chrono::duration<double, std::milli> dms{}; VERIFY( is >> parse("%S", dms) ); VERIFY( dms == 0.125s ); } diff --git a/libstdc++-v3/testsuite/20_util/expected/119714.cc b/libstdc++-v3/testsuite/20_util/expected/119714.cc new file mode 100644 index 0000000..a8dc6e8 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/expected/119714.cc @@ -0,0 +1,9 @@ +// { dg-do compile { target c++23 } } + +// PR libstdc++/119714 - constraint recursion with std::expected::operator== + +#include <expected> +#include <vector> + +using I = std::vector<std::expected<int,int>>::iterator; +static_assert(std::totally_ordered<I>); diff --git a/libstdc++-v3/testsuite/20_util/expected/equality.cc b/libstdc++-v3/testsuite/20_util/expected/equality.cc index db19b15..cc122f4 100644 --- a/libstdc++-v3/testsuite/20_util/expected/equality.cc +++ b/libstdc++-v3/testsuite/20_util/expected/equality.cc @@ -28,19 +28,45 @@ test_eq() std::expected<int, int> e2; VERIFY(e2 == e2); VERIFY(e1 == e2); + VERIFY(e2 == e1); VERIFY(e1 != std::unexpected<int>(1)); + e1 = std::unexpected<int>(1); VERIFY(e1 == std::unexpected<int>(1)); VERIFY(e1 != std::unexpected<int>(2)); VERIFY(e1 != e2); + VERIFY(e2 != e1); + VERIFY(e1 != 1); + + e2 = std::unexpected<int>(1); + VERIFY(e1 == e2); + VERIFY(e2 == e1); + + e2 = std::unexpected<int>(2); + VERIFY(e1 != e2); + VERIFY(e2 != e1); std::expected<void, int> e3; VERIFY(e3 == e3); VERIFY(e3 != std::unexpected<int>(1)); + std::expected<const void, long> e4; + VERIFY(e3 == e4); + VERIFY(e4 == e3); + e3 = std::unexpected<int>(1); VERIFY(e3 == e3); VERIFY(e3 == std::unexpected<int>(1)); VERIFY(e3 != std::unexpected<int>(2)); + VERIFY(e3 != e4); + VERIFY(e4 != e3); + + e4 = e3; + VERIFY(e3 == e4); + VERIFY(e4 == e3); + + e4 = std::unexpected<int>(4); + VERIFY(e3 != e4); + VERIFY(e4 != e3); return true; } diff --git a/libstdc++-v3/testsuite/20_util/expected/illformed_neg.cc b/libstdc++-v3/testsuite/20_util/expected/illformed_neg.cc index 69c13b4..69aa4a1 100644 --- a/libstdc++-v3/testsuite/20_util/expected/illformed_neg.cc +++ b/libstdc++-v3/testsuite/20_util/expected/illformed_neg.cc @@ -13,6 +13,7 @@ test_unexpected() std::unexpected<void()> func(test_unexpected); // { dg-error "here" } // { dg-error "no matching function for call to" "" { target *-*-* } 0 } // { dg-error "invalidly declared function type" "" { target *-*-* } 0 } + // { dg-error "could not convert" "" { target *-*-* } 0 } // an array type, std::unexpected<int[2]> array(i); // { dg-error "here" } diff --git a/libstdc++-v3/testsuite/20_util/expected/lwg4222.cc b/libstdc++-v3/testsuite/20_util/expected/lwg4222.cc new file mode 100644 index 0000000..5c10779 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/expected/lwg4222.cc @@ -0,0 +1,39 @@ +// { dg-do run { target c++23 } } + +// LWG 4222. 'expected' constructor from a single value missing a constraint + +#include <expected> +#include <type_traits> +#include <testsuite_hooks.h> + +struct T { + explicit T(auto) {} +}; +struct E { + E(int) {} +}; + +struct V { + explicit constexpr V(std::unexpect_t) {} +}; + +static_assert(!std::is_constructible_v<std::expected<T, E>, std::unexpect_t>); +static_assert(!std::is_constructible_v<std::expected<T, E>, std::unexpect_t &>); +static_assert(!std::is_constructible_v<std::expected<T, E>, std::unexpect_t &&>); +static_assert(!std::is_constructible_v<std::expected<T, E>, const std::unexpect_t>); +static_assert(!std::is_constructible_v<std::expected<T, E>, const std::unexpect_t &>); +static_assert(!std::is_constructible_v<std::expected<T, E>, const std::unexpect_t &&>); + +constexpr bool test() { + std::expected<V, int> e1(std::in_place, std::unexpect); + VERIFY( e1.has_value() ); + std::expected<int, V> e2(std::unexpect, std::unexpect); + VERIFY( !e2.has_value() ); + return true; +} + +int main() { + test(); + static_assert(test()); + return 0; +} diff --git a/libstdc++-v3/testsuite/20_util/expected/lwg4366.cc b/libstdc++-v3/testsuite/20_util/expected/lwg4366.cc new file mode 100644 index 0000000..35d5371 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/expected/lwg4366.cc @@ -0,0 +1,33 @@ +// { dg-do run { target c++23 } } + +// LWG 4366. Heterogeneous comparison of expected may be ill-formed + +#include <expected> +#include <testsuite_hooks.h> + +struct Bool +{ + operator bool() const { return true; } + explicit operator bool() { throw; } +}; + +struct E1 { + friend Bool operator==(E1, E1) { return {}; } +} e1; + +struct E2 { + friend Bool operator==(E1, E2) { return {}; } +} e2; + +int main() +{ + std::expected<int, E1> u1(std::unexpect, e1); + VERIFY(u1 == u1); + + std::unexpected<E2> u2(e2); + VERIFY(u1 == u2); + + std::expected<void, E1> u3(std::unexpect, e1); + VERIFY(u3 == u3); + VERIFY(u3 == u2); +} diff --git a/libstdc++-v3/testsuite/20_util/function/cons/70692.cc b/libstdc++-v3/testsuite/20_util/function/cons/70692.cc index b15208a..f9e8fe3 100644 --- a/libstdc++-v3/testsuite/20_util/function/cons/70692.cc +++ b/libstdc++-v3/testsuite/20_util/function/cons/70692.cc @@ -11,4 +11,4 @@ int main() std::function<const int&()> ff(f); // { dg-error "no matching function" } std::function<long&&()> f2(f); // { dg-error "no matching function" } } -// { dg-error "std::(__8::)?enable_if" "" { target *-*-* } 0 } +// { dg-error "std::enable_if" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/20_util/function_objects/121782.cc b/libstdc++-v3/testsuite/20_util/function_objects/121782.cc new file mode 100644 index 0000000..f18fb1e --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_objects/121782.cc @@ -0,0 +1,30 @@ +// { dg-do compile { target c++17 } } +// libstdc++/121782 +// Missing Mandates for operator() of std::boyer_moore_[horspool]_searcher + +// N.B. we only enforce this for C++20 and later. +// { dg-error "static assertion failed" "" { target c++20 } 0 } + +#include <algorithm> +#include <functional> +#include <testsuite_iterators.h> + +template<typename T> +using Range = __gnu_test::random_access_container<T>; + +void +test_bm(Range<char> needle, Range<unsigned char> haystack) +{ + std::boyer_moore_searcher s(needle.begin(), needle.end()); + (void) std::search(haystack.begin(), haystack.end(), s); // { dg-error "here" "" { target c++20 } } + // { dg-error "'char' is not the same as 'unsigned char'" "" { target c++20 } 0 } +} + +void +test_bmh(Range<char> needle, Range<signed char> haystack) +{ + std::boyer_moore_horspool_searcher s(needle.begin(), needle.end()); + (void) std::search(haystack.begin(), haystack.end(), s); // { dg-error "here" "" { target c++20 } } + // { dg-error "'char' is not the same as 'signed char'" "" { target c++20 } 0 } +} + diff --git a/libstdc++-v3/testsuite/20_util/function_objects/bind_back/1.cc b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/1.cc index c31d322..743b7b0 100644 --- a/libstdc++-v3/testsuite/20_util/function_objects/bind_back/1.cc +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/1.cc @@ -48,6 +48,15 @@ test01() decltype(bind_back(std::declval<const F&>(), std::declval<const int&>())) >); + static_assert(std::is_same_v< + decltype(bind_back(std::declval<F>(), std::declval<int>(), std::declval<float>())), + decltype(bind_back(std::declval<F&>(), std::declval<int&>(), std::declval<float&>())) + >); + static_assert(std::is_same_v< + decltype(bind_back(std::declval<F>(), std::declval<int>(), std::declval<float>())), + decltype(bind_back(std::declval<const F&>(), std::declval<const int&>(), std::declval<const float&>())) + >); + // Reference wrappers should be handled: static_assert(!std::is_same_v< decltype(bind_back(std::declval<F>(), std::declval<int&>())), @@ -63,29 +72,58 @@ test01() >); } +struct quals +{ + bool as_const; + bool as_lvalue; +}; + +template<typename... Args> void -test02() +testTarget(Args... args) { - struct quals + struct F { - bool as_const; - bool as_lvalue; + quals operator()(Args...) & { return { false, true }; } + quals operator()(Args...) const & { return { true, true }; } + quals operator()(Args...) && { return { false, false }; } + quals operator()(Args...) const && { return { true, false }; } }; + F f; + auto g = bind_back(f, args...); + const auto& cg = g; + quals q; + + // constness and value category should be forwarded to the target object: + q = g(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = std::move(g)(); + VERIFY( ! q.as_const && ! q.as_lvalue ); + q = cg(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(cg)(); + VERIFY( q.as_const && ! q.as_lvalue ); +} + +template<typename... Args> +void +testBoundArgs(Args... args) +{ struct F { - quals operator()() & { return { false, true }; } - quals operator()() const & { return { true, true }; } - quals operator()() && { return { false, false }; } - quals operator()() const && { return { true, false }; } + quals operator()(Args..., int&) const { return { false, true }; } + quals operator()(Args..., int const&) const { return { true, true }; } + quals operator()(Args..., int&&) const { return { false, false }; } + quals operator()(Args..., int const&&) const { return { true, false }; } }; F f; - auto g = bind_back(f); + auto g = bind_back(f, args..., 10); const auto& cg = g; quals q; - // constness and value category should be forwarded to the target object: + // constness and value category should be forwarded to the bound objects: q = g(); VERIFY( ! q.as_const && q.as_lvalue ); q = std::move(g)(); @@ -94,6 +132,85 @@ test02() VERIFY( q.as_const && q.as_lvalue ); q = std::move(cg)(); VERIFY( q.as_const && ! q.as_lvalue ); + + int i = 0; + auto gr = bind_back(f, args..., std::ref(i)); + const auto& cgr = gr; + + // bound object is reference wrapper + q = gr(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = std::move(gr)(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = cgr(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = std::move(cgr)(); + VERIFY( ! q.as_const && q.as_lvalue ); + + auto gcr = bind_back(f, args..., std::cref(i)); + const auto& cgcr = gcr; + + q = gcr(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(gcr)(); + VERIFY( q.as_const && q.as_lvalue ); + q = cgcr(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(cgcr)(); + VERIFY( q.as_const && q.as_lvalue ); +} + +template<typename... Args> +void +testCallArgs(Args... args) +{ + struct F + { + quals operator()(int&, Args...) const { return { false, true }; } + quals operator()(int const&, Args...) const { return { true, true }; } + quals operator()(int&&, Args...) const { return { false, false }; } + quals operator()(int const&&, Args...) const { return { true, false }; } + }; + + F f; + auto g = bind_back(f, args...); + const auto& cg = g; + quals q; + int i = 10; + const int ci = i; + + q = g(i); + VERIFY( ! q.as_const && q.as_lvalue ); + q = g(std::move(i)); + VERIFY( ! q.as_const && ! q.as_lvalue ); + q = g(ci); + VERIFY( q.as_const && q.as_lvalue ); + q = g(std::move(ci)); + VERIFY( q.as_const && ! q.as_lvalue ); + + q = cg(i); + VERIFY( ! q.as_const && q.as_lvalue ); + q = cg(std::move(i)); + VERIFY( ! q.as_const && ! q.as_lvalue ); + q = cg(ci); + VERIFY( q.as_const && q.as_lvalue ); + q = cg(std::move(ci)); + VERIFY( q.as_const && ! q.as_lvalue ); + + struct S + { + int operator()(long, long, Args...) const { return 1; } + int operator()(int, void*, Args...) const { return 2; } + }; + + S s; + // literal zero can be converted to any pointer, so (int, void*) + // is best candidate + VERIFY( s(0, 0, args...) == 2 ); + // both arguments are bound to int&&, and no longer can be + // converted to pointer, (long, long) is only candidate + VERIFY( bind_back(s)(0, 0, args...) == 1 ); + VERIFY( bind_back(s, args...)(0, 0) == 1 ); } void @@ -149,30 +266,71 @@ test03() static_assert(is_invocable_r_v<void*, const G4&&>); } -constexpr int f(int i, int j, int k) { return i + 2*(j + k); } +constexpr int f(int i, int j, int k) { return i + 2*j + 3*k; } constexpr bool test04() { auto g = bind_back(f); - VERIFY( g(1, 2, 3) == 1 + 2*(2 + 3) ); + VERIFY( g(1, 2, 3) == 1 + 2*2 + 3*3 ); auto g1 = bind_back(f, 1); - VERIFY( g1(2, 3) == 2 + 2*(3 + 1) ); - VERIFY( bind_back(g, 1)(2, 3) == 2 + 2*(3 + 1) ); + VERIFY( g1(2, 3) == 3*1 + 2 + 3*2); + VERIFY( bind_back(g, 1)(2, 3) == 3*1 + 2 + 2*3 ); auto g2 = bind_back(f, 1, 2); - VERIFY( g2(3) == 3 + 2*(1 + 2) ); - VERIFY( bind_back(g1, 2)(3) == 3 + 2*(2 + 1) ); + VERIFY( g2(3) == 3 + 2*1 + 3*2); + VERIFY( bind_back(g1, 2)(3) == 3*1 + 2*2 + 3 ); auto g3 = bind_back(f, 1, 2, 3); - VERIFY( g3() == 1 + 2*(2 + 3) ); - VERIFY( bind_back(g2, 3)() == 3 + 2*(1 + 2) ); + VERIFY( g3() == 1 + 2*2 + 3*3 ); + VERIFY( bind_back(g2, 3)() == 3*1 + 1*2 + 2*3); return true; } +struct CountedArg +{ + CountedArg() = default; + CountedArg(CountedArg&& f) noexcept : counter(f.counter) { ++counter; } + CountedArg& operator=(CountedArg&&) = delete; + + int counter = 0; +}; +CountedArg const c; + +void +testMaterialization() +{ + struct F + { + int operator()(CountedArg arg, int) const + { return arg.counter; }; + }; + + // CountedArg is bound to rvalue-reference thus moved + auto f0 = std::bind_back(F{}); + VERIFY( f0(CountedArg(), 10) == 1 ); + + auto f1 = std::bind_back(F{}, 10); + VERIFY( f1(CountedArg()) == 1 ); +} + int main() { test01(); - test02(); test03(); + + testTarget(); + testTarget(10); + testTarget(10, 20, 30); + + testBoundArgs(); + testBoundArgs(10); + testBoundArgs(10, 20, 30); + + testCallArgs(); + testCallArgs(10); + testCallArgs(10, 20, 30); + + testMaterialization(); + static_assert(test04()); } diff --git a/libstdc++-v3/testsuite/20_util/function_objects/bind_back/111327.cc b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/111327.cc index d634db9..8e7dacf 100644 --- a/libstdc++-v3/testsuite/20_util/function_objects/bind_back/111327.cc +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/111327.cc @@ -37,6 +37,18 @@ int main() { g1(); // { dg-error "deleted|no match" } std::move(g1)(); // { dg-error "deleted|no match" } std::move(std::as_const(g1))(); + + auto f2 = std::bind_back(F{}, 42, 10); + f2(); // { dg-error "deleted|no match" } + std::move(f2)(); + std::as_const(f2)(); + std::move(std::as_const(f2))(); + + auto g2 = std::bind_back(G{}, 42, 10); + g2(); // { dg-error "deleted|no match" } + std::move(g2)(); // { dg-error "deleted|no match" } + std::move(std::as_const(g2))(); } -// { dg-error "no type named 'type' in 'struct std::invoke_result" "" { target c++23 } 0 } +// { dg-error "no type named 'type' in 'std::__conditional_t<false, std::invoke_result<" "" { target c++23 } 0 } +// { dg-error "no type named 'type' in 'std::__conditional_t<true, std::invoke_result<" "" { target c++23 } 0 } diff --git a/libstdc++-v3/testsuite/20_util/function_objects/bind_back/nttp.cc b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/nttp.cc new file mode 100644 index 0000000..24bf046 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/nttp.cc @@ -0,0 +1,345 @@ +// { dg-do run { target c++26 } } +// { dg-add-options no_pch } + +// Test NTTP bind_back<f>(Args...), P2714 + +#include <functional> + +#ifndef __cpp_lib_bind_back +# error "Feature test macro for bind_back is missing in <functional>" +#elif __cpp_lib_bind_back < 202306L +# error "Feature test macro for bind_back has wrong value in <functional>" +#endif + +#include <testsuite_hooks.h> + +using std::bind_back; +using std::is_same_v; +using std::is_invocable_v; +using std::is_invocable_r_v; + +void +test01() +{ + struct F { void operator()(int) {} }; + constexpr F f{}; + + // Arguments should be decayed: + static_assert(std::is_same_v< + decltype(bind_back<f>(std::declval<int>())), + decltype(bind_back<f>(std::declval<int&>())) + >); + static_assert(std::is_same_v< + decltype(bind_back<f>(std::declval<int>())), + decltype(bind_back<f>(std::declval<const int&>())) + >); + + static_assert(std::is_same_v< + decltype(bind_back<f>(std::declval<int>(), std::declval<float>())), + decltype(bind_back<f>(std::declval<int&>(), std::declval<float&>())) + >); + static_assert(std::is_same_v< + decltype(bind_back<f>(std::declval<int>(), std::declval<float>())), + decltype(bind_back<f>(std::declval<const int&>(), std::declval<const float&>())) + >); + + // Reference wrappers should be handled: + static_assert(!std::is_same_v< + decltype(bind_back<f>(std::declval<int&>())), + decltype(bind_back<f>(std::ref(std::declval<int&>()))) + >); + static_assert(!std::is_same_v< + decltype(bind_back<f>(std::declval<const int&>())), + decltype(bind_back<f>(std::cref(std::declval<int&>()))) + >); + static_assert(!std::is_same_v< + decltype(bind_back<f>(std::ref(std::declval<int&>()))), + decltype(bind_back<f>(std::cref(std::declval<int&>()))) + >); +} + +struct quals +{ + bool as_const; + bool as_lvalue; +}; + +template<typename... Args> +void +testTarget(Args... args) +{ + struct F + { + quals operator()(Args...) & { return { false, true }; } + quals operator()(Args...) const & { return { true, true }; } + quals operator()(Args...) && { return { false, false }; } + quals operator()(Args...) const && { return { true, false }; } + }; + + constexpr F f; + auto g = bind_back<f>(args...); + const auto& cg = g; + quals q; + + // template parameter object is always constant lvalue + q = g(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(g)(); + VERIFY( q.as_const && q.as_lvalue ); + q = cg(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(cg)(); + VERIFY( q.as_const && q.as_lvalue ); +} + +template<typename... Args> +void +testBoundArgs(Args... args) +{ + struct F + { + quals operator()(Args..., int&) const { return { false, true }; } + quals operator()(Args..., int const&) const { return { true, true }; } + quals operator()(Args..., int&&) const { return { false, false }; } + quals operator()(Args..., int const&&) const { return { true, false }; } + }; + + constexpr F f; + auto g = bind_back<f>(args..., 10); + const auto& cg = g; + quals q; + + // constness and value category should be forwarded to the bound objects: + q = g(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = std::move(g)(); + VERIFY( ! q.as_const && ! q.as_lvalue ); + q = cg(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(cg)(); + VERIFY( q.as_const && ! q.as_lvalue ); + + int i = 0; + auto gr = bind_back<f>(args..., std::ref(i)); + const auto& cgr = gr; + + // bound object is reference wrapper, converts to same type of reference + q = gr(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = std::move(gr)(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = cgr(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = std::move(cgr)(); + VERIFY( ! q.as_const && q.as_lvalue ); + + auto gcr = bind_back<f>(args..., std::cref(i)); + const auto& cgcr = gcr; + + q = gcr(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(gcr)(); + VERIFY( q.as_const && q.as_lvalue ); + q = cgcr(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(cgcr)(); + VERIFY( q.as_const && q.as_lvalue ); +} + +template<typename... Args> +void +testCallArgs(Args... args) +{ + struct F + { + quals operator()(int&, Args...) const { return { false, true }; } + quals operator()(int const&, Args...) const { return { true, true }; } + quals operator()(int&&, Args...) const { return { false, false }; } + quals operator()(int const&&, Args...) const { return { true, false }; } + }; + + constexpr F f; + auto g = bind_back<f>(args...); + const auto& cg = g; + quals q; + int i = 10; + const int ci = i; + + q = g(i); + VERIFY( ! q.as_const && q.as_lvalue ); + q = g(std::move(i)); + VERIFY( ! q.as_const && ! q.as_lvalue ); + q = g(ci); + VERIFY( q.as_const && q.as_lvalue ); + q = g(std::move(ci)); + VERIFY( q.as_const && ! q.as_lvalue ); + + q = cg(i); + VERIFY( ! q.as_const && q.as_lvalue ); + q = cg(std::move(i)); + VERIFY( ! q.as_const && ! q.as_lvalue ); + q = cg(ci); + VERIFY( q.as_const && q.as_lvalue ); + q = cg(std::move(ci)); + VERIFY( q.as_const && ! q.as_lvalue ); + + struct S + { + int operator()(long, long, Args...) const { return 1; } + int operator()(int, void*, Args...) const { return 2; } + }; + + constexpr S s; + // literal zero can be converted to any pointer, so (int, void*) + // is best candidate + VERIFY( s(0, 0, args...) == 2 ); + // both arguments are bound to int&&, and no longer can be + // converted to pointer, (long, long) is only candidate + VERIFY( bind_back<s>()(0, 0, args...) == 1 ); + VERIFY( bind_back<s>(args...)(0, 0) == 1 ); +} + +void +test03() +{ + struct F + { + int& operator()(void*, int& i) { return i; } + void* operator()(void* p, int) const { return p; } + }; + + int i = 5; + void* vp = &vp; // arbitrary void* value + constexpr F f; + + // Bound arg always forwarded as const int& so can only call second overload: + + auto g0 = bind_back<f>(); // call wrapper has no bound arg + using G0 = decltype(g0); + static_assert(is_invocable_r_v<void*, G0&, void*, int>); + static_assert(is_invocable_r_v<void*, G0&, void*, int&>); + static_assert(is_invocable_r_v<void*, G0&, void*, const int&>); + static_assert(is_invocable_r_v<void*, const G0&, void*, int>); + static_assert(is_invocable_r_v<void*, G0&&, void*, int>); + void* p0 = static_cast<G0&&>(g0)(vp, i); + VERIFY( p0 == vp ); + + auto g1 = bind_back<f>(i); // call wrapper has bound arg of type int + using G1 = decltype(g1); + static_assert(!is_invocable_r_v<int&, G1&, void*>); + static_assert(is_invocable_r_v<void*, const G1&, void*>); + static_assert(is_invocable_r_v<void*, G1&&, void*>); + void* p1 = static_cast<G1&&>(g1)(vp); + VERIFY( p1 == vp ); + + auto g2 = bind_back<f>(std::ref(i)); // bound arg of type int& + using G2 = decltype(g2); + static_assert(is_invocable_r_v<void*, G2&, void*>); + static_assert(is_invocable_r_v<void*, G2&&, void*>); + static_assert(is_invocable_r_v<void*, const G2&, void*>); + static_assert(is_invocable_r_v<void*, const G2&&, void*>); + void* p2 = g2(vp); + VERIFY( p2 == vp ); + p2 = static_cast<G2&&>(g2)(vp); + VERIFY( p2 == vp ); + p2 = const_cast<const G2&>(g2)(vp); + VERIFY( p2 == vp ); + + auto g3 = bind_back<f>(std::cref(i)); // bound arg of type const int& + using G3 = decltype(g3); + static_assert(is_invocable_r_v<void*, G3&, void*>); + static_assert(is_invocable_r_v<void*, G3&&, void*>); + static_assert(is_invocable_r_v<void*, const G3&, void*>); + static_assert(is_invocable_r_v<void*, const G3&&, void*>); +} + +void test03a() +{ + struct F + { + int& operator()(void*, int& i) { return i; } + void* operator()(void* p, long) const { return p; } + }; + + int i = 5; + void* vp = &vp; // arbitrary void* value + constexpr F f; + + // Bound arg always forwarded as const int& so can only call second overload: + auto g1 = bind_back<f>(i); // call wrapper has bound arg of type int + using G1 = decltype(g1); + static_assert(is_invocable_r_v<void*, G1&, void*>); + static_assert(is_invocable_r_v<void*, const G1&, void*>); + static_assert(is_invocable_r_v<void*, G1&&, void*>); + void* p1 = static_cast<G1&&>(g1)(vp); + VERIFY( p1 == vp ); +} + + +constexpr int f(int i, int j, int k) { return i + 2*j + 3*k; } + +consteval bool +test04() +{ + constexpr auto g = bind_back<f>(); + VERIFY( std::is_empty_v<decltype(g)> ); + VERIFY(g(1, 2, 3) == 1 + 2*2 + 3*3 ); + constexpr auto g1 = bind_back<f>(1); + VERIFY(g1(2, 3) == 3*1 + 1*2 + 2*3 ); + VERIFY(bind_back<g>(1)(2, 3) == 3*1 + 1*2 + 2*3 ); + constexpr auto g2 = bind_back<f>(1, 2); + VERIFY(g2(3) == 2*1 + 3*2 + 1*3 ); + constexpr auto g3 = bind_back<f>(1, 2, 3); + VERIFY(g3() == 1 + 2*2 + 3*3); + return true; +} + +struct CountedArg +{ + CountedArg() = default; + CountedArg(CountedArg&& f) noexcept : counter(f.counter) { ++counter; } + CountedArg& operator=(CountedArg&&) = delete; + + int counter = 0; +}; +CountedArg const c; + +void +testMaterialization() +{ + struct F + { + int operator()(CountedArg arg, int) const + { return arg.counter; }; + }; + + // CountedArg is bound to rvalue-reference thus moved + auto f0 = std::bind_back<F{}>(); + VERIFY( f0(CountedArg(), 10) == 1 ); + + auto f1 = std::bind_back<F{}>(10); + VERIFY( f1(CountedArg()) == 1 ); +} + +int +main() +{ + test01(); + test03(); + test03a(); + static_assert(test04()); + + testTarget(); + testTarget(10); + testTarget(10, 20, 30); + + testBoundArgs(); + testBoundArgs(10); + testBoundArgs(10, 20, 30); + + testCallArgs(); + testCallArgs(10); + testCallArgs(10, 20, 30); + + testMaterialization(); +} diff --git a/libstdc++-v3/testsuite/20_util/function_objects/bind_back/nttp_neg.cc b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/nttp_neg.cc new file mode 100644 index 0000000..d64d49e --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_back/nttp_neg.cc @@ -0,0 +1,38 @@ +// { dg-do compile { target c++26 } } + +#include <functional> + +void f() {}; +using fp = decltype(&f); +constexpr fp nfp = nullptr; + +struct A { void mf() const {} }; +using mfp = decltype(&A::mf); +constexpr mfp nnmfp = &A::mf; +constexpr mfp nmfp = nullptr; + +struct B { B() = default; B(B const&) = delete; }; +void bf(B const&) {}; + +struct C { C() = default; C(C&&) = delete; }; +void cf(C&&) {}; + +int main() +{ + std::bind_back<f>()(); + // Verify bind_back<fn> with fn a null pointer fails. + std::bind_back<nfp>()(); // { dg-error "here" } + + std::bind_back<nnmfp>(A{})(); + // Verify bind_back<mfn> with mfn a null member pointer fails. + std::bind_back<nmfp>(A{})(); // { dg-error "here" } + + // Verify passing uncopyable type fails. + std::bind_back<bf>(B{}); // { dg-error "here" } + // + // Verify passing unmovable type fails. + std::bind_back<cf>(C{}); // { dg-error "here" } +} + +// { dg-error "static assertion failed" "" { target *-*-* } 0 } +// { dg-error "use of deleted function" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/1.cc b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/1.cc index 57482c5..56bdaaa 100644 --- a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/1.cc +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/1.cc @@ -48,6 +48,15 @@ test01() decltype(bind_front(std::declval<const F&>(), std::declval<const int&>())) >); + static_assert(std::is_same_v< + decltype(bind_front(std::declval<F>(), std::declval<int>(), std::declval<float>())), + decltype(bind_front(std::declval<F&>(), std::declval<int&>(), std::declval<float&>())) + >); + static_assert(std::is_same_v< + decltype(bind_front(std::declval<F>(), std::declval<int>(), std::declval<float>())), + decltype(bind_front(std::declval<const F&>(), std::declval<const int&>(), std::declval<const float&>())) + >); + // Reference wrappers should be handled: static_assert(!std::is_same_v< decltype(bind_front(std::declval<F>(), std::declval<int&>())), @@ -63,29 +72,58 @@ test01() >); } +struct quals +{ + bool as_const; + bool as_lvalue; +}; + +template<typename... Args> void -test02() +testTarget(Args... args) { - struct quals + struct F { - bool as_const; - bool as_lvalue; + quals operator()(Args...) & { return { false, true }; } + quals operator()(Args...) const & { return { true, true }; } + quals operator()(Args...) && { return { false, false }; } + quals operator()(Args...) const && { return { true, false }; } }; + F f; + auto g = bind_front(f, args...); + const auto& cg = g; + quals q; + + // constness and value category should be forwarded to the target object: + q = g(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = std::move(g)(); + VERIFY( ! q.as_const && ! q.as_lvalue ); + q = cg(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(cg)(); + VERIFY( q.as_const && ! q.as_lvalue ); +} + +template<typename... Args> +void +testBoundArgs(Args... args) +{ struct F { - quals operator()() & { return { false, true }; } - quals operator()() const & { return { true, true }; } - quals operator()() && { return { false, false }; } - quals operator()() const && { return { true, false }; } + quals operator()(Args..., int&) const { return { false, true }; } + quals operator()(Args..., int const&) const { return { true, true }; } + quals operator()(Args..., int&&) const { return { false, false }; } + quals operator()(Args..., int const&&) const { return { true, false }; } }; F f; - auto g = bind_front(f); + auto g = bind_front(f, args..., 10); const auto& cg = g; quals q; - // constness and value category should be forwarded to the target object: + // constness and value category should be forwarded to the bound objects: q = g(); VERIFY( ! q.as_const && q.as_lvalue ); q = std::move(g)(); @@ -94,6 +132,85 @@ test02() VERIFY( q.as_const && q.as_lvalue ); q = std::move(cg)(); VERIFY( q.as_const && ! q.as_lvalue ); + + int i = 0; + auto gr = bind_front(f, args..., std::ref(i)); + const auto& cgr = gr; + + // bound object is reference wrapper, converts to same type of reference + q = gr(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = std::move(gr)(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = cgr(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = std::move(cgr)(); + VERIFY( ! q.as_const && q.as_lvalue ); + + auto gcr = bind_front(f, args..., std::cref(i)); + const auto& cgcr = gcr; + + q = gcr(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(gcr)(); + VERIFY( q.as_const && q.as_lvalue ); + q = cgcr(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(cgcr)(); + VERIFY( q.as_const && q.as_lvalue ); +} + +template<typename... Args> +void +testCallArgs(Args... args) +{ + struct F + { + quals operator()(Args..., int&) const { return { false, true }; } + quals operator()(Args..., int const&) const { return { true, true }; } + quals operator()(Args..., int&&) const { return { false, false }; } + quals operator()(Args..., int const&&) const { return { true, false }; } + }; + + F f; + auto g = bind_front(f, args...); + const auto& cg = g; + quals q; + int i = 10; + const int ci = i; + + q = g(i); + VERIFY( ! q.as_const && q.as_lvalue ); + q = g(std::move(i)); + VERIFY( ! q.as_const && ! q.as_lvalue ); + q = g(ci); + VERIFY( q.as_const && q.as_lvalue ); + q = g(std::move(ci)); + VERIFY( q.as_const && ! q.as_lvalue ); + + q = cg(i); + VERIFY( ! q.as_const && q.as_lvalue ); + q = cg(std::move(i)); + VERIFY( ! q.as_const && ! q.as_lvalue ); + q = cg(ci); + VERIFY( q.as_const && q.as_lvalue ); + q = cg(std::move(ci)); + VERIFY( q.as_const && ! q.as_lvalue ); + + struct S + { + int operator()(Args..., long, long) const { return 1; } + int operator()(Args..., int, void*) const { return 2; } + }; + + S s; + // literal zero can be converted to any pointer, so (int, void*) + // is best candidate + VERIFY( s(args..., 0, 0) == 2 ); + // both arguments are bound to int&&, and no longer can be + // converted to pointer, (long, long) is only candidate + VERIFY( bind_front(s)(args..., 0, 0) == 1 ); + VERIFY( bind_front(s, args...)(0, 0) == 1 ); } void @@ -149,29 +266,69 @@ test03() static_assert(is_invocable_r_v<void*, const G4&&>); } -int f(int i, int j, int k) { return i + j + k; } +int f(int i, int j, int k) { return i + 2*j + 3*k; } void test04() { auto g = bind_front(f); - VERIFY( g(1, 2, 3) == 6 ); + VERIFY( g(1, 2, 3) == 14 ); auto g1 = bind_front(f, 1); - VERIFY( g1(2, 3) == 6 ); - VERIFY( bind_front(g, 1)(2, 3) == 6 ); + VERIFY( g1(2, 3) == 14 ); + VERIFY( bind_front(g, 1)(2, 3) == 14 ); auto g2 = bind_front(f, 1, 2); - VERIFY( g2(3) == 6 ); - VERIFY( bind_front(g1, 2)(3) == 6 ); + VERIFY( g2(3) == 14 ); + VERIFY( bind_front(g1, 2)(3) == 14 ); auto g3 = bind_front(f, 1, 2, 3); - VERIFY( g3() == 6 ); - VERIFY( bind_front(g2, 3)() == 6 ); + VERIFY( g3() == 14 ); + VERIFY( bind_front(g2, 3)() == 14 ); +} + +struct CountedArg +{ + CountedArg() = default; + CountedArg(CountedArg&& f) noexcept : counter(f.counter) { ++counter; } + CountedArg& operator=(CountedArg&&) = delete; + + int counter = 0; +}; +CountedArg const c; + +void +testMaterialization() +{ + struct F + { + int operator()(int, CountedArg arg) const + { return arg.counter; }; + }; + + // CountedArg is bound to rvalue-reference thus moved + auto f0 = std::bind_front(F{}); + VERIFY( f0(10, CountedArg()) == 1 ); + + auto f1 = std::bind_front(F{}, 10); + VERIFY( f1(CountedArg()) == 1 ); } int main() { test01(); - test02(); test03(); test04(); + + testTarget(); + testTarget(10); + testTarget(10, 20, 30); + + testBoundArgs(); + testBoundArgs(10); + testBoundArgs(10, 20, 30); + + testCallArgs(); + testCallArgs(10); + testCallArgs(10, 20, 30); + + testMaterialization(); } diff --git a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/111327.cc b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/111327.cc index 5fe0a83..493ef08 100644 --- a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/111327.cc +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/111327.cc @@ -37,6 +37,17 @@ int main() { g1(); // { dg-error "deleted|no match" } std::move(g1)(); // { dg-error "deleted|no match" } std::move(std::as_const(g1))(); + + auto f2 = std::bind_front(F{}, 42, 10); + f2(); // { dg-error "deleted|no match" } + std::move(f2)(); + std::as_const(f2)(); + std::move(std::as_const(f2))(); + + auto g2 = std::bind_front(G{}, 42, 10); + g2(); // { dg-error "deleted|no match" } + std::move(g2)(); // { dg-error "deleted|no match" } + std::move(std::as_const(g2))(); } -// { dg-error "no type named 'type' in 'struct std::invoke_result" "" { target c++23 } 0 } +// { dg-error "no type named 'type' in 'std::__conditional_t<false, std::invoke_result<" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/nttp.cc b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/nttp.cc new file mode 100644 index 0000000..cf84353 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/nttp.cc @@ -0,0 +1,345 @@ +// { dg-do run { target c++26 } } +// { dg-add-options no_pch } + +// Test NTTP bind_front<f>(Args...), P2714 + +#include <functional> + +#ifndef __cpp_lib_bind_front +# error "Feature test macro for bind_front is missing in <functional>" +#elif __cpp_lib_bind_front < 202306L +# error "Feature test macro for bind_front has wrong value in <functional>" +#endif + +#include <testsuite_hooks.h> + +using std::bind_front; +using std::is_same_v; +using std::is_invocable_v; +using std::is_invocable_r_v; + +void +test01() +{ + struct F { void operator()(int) {} }; + constexpr F f{}; + + // Arguments should be decayed: + static_assert(std::is_same_v< + decltype(bind_front<f>(std::declval<int>())), + decltype(bind_front<f>(std::declval<int&>())) + >); + static_assert(std::is_same_v< + decltype(bind_front<f>(std::declval<int>())), + decltype(bind_front<f>(std::declval<const int&>())) + >); + + static_assert(std::is_same_v< + decltype(bind_front<f>(std::declval<int>(), std::declval<float>())), + decltype(bind_front<f>(std::declval<int&>(), std::declval<float&>())) + >); + static_assert(std::is_same_v< + decltype(bind_front<f>(std::declval<int>(), std::declval<float>())), + decltype(bind_front<f>(std::declval<const int&>(), std::declval<const float&>())) + >); + + // Reference wrappers should be handled: + static_assert(!std::is_same_v< + decltype(bind_front<f>(std::declval<int&>())), + decltype(bind_front<f>(std::ref(std::declval<int&>()))) + >); + static_assert(!std::is_same_v< + decltype(bind_front<f>(std::declval<const int&>())), + decltype(bind_front<f>(std::cref(std::declval<int&>()))) + >); + static_assert(!std::is_same_v< + decltype(bind_front<f>(std::ref(std::declval<int&>()))), + decltype(bind_front<f>(std::cref(std::declval<int&>()))) + >); +} + +struct quals +{ + bool as_const; + bool as_lvalue; +}; + +template<typename... Args> +void +testTarget(Args... args) +{ + struct F + { + quals operator()(Args...) & { return { false, true }; } + quals operator()(Args...) const & { return { true, true }; } + quals operator()(Args...) && { return { false, false }; } + quals operator()(Args...) const && { return { true, false }; } + }; + + constexpr F f; + auto g = bind_front<f>(args...); + const auto& cg = g; + quals q; + + // template parameter object is always constant lvalue + q = g(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(g)(); + VERIFY( q.as_const && q.as_lvalue ); + q = cg(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(cg)(); + VERIFY( q.as_const && q.as_lvalue ); +} + +template<typename... Args> +void +testBoundArgs(Args... args) +{ + struct F + { + quals operator()(Args..., int&) const { return { false, true }; } + quals operator()(Args..., int const&) const { return { true, true }; } + quals operator()(Args..., int&&) const { return { false, false }; } + quals operator()(Args..., int const&&) const { return { true, false }; } + }; + + constexpr F f; + auto g = bind_front<f>(args..., 10); + const auto& cg = g; + quals q; + + // constness and value category should be forwarded to the bound objects: + q = g(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = std::move(g)(); + VERIFY( ! q.as_const && ! q.as_lvalue ); + q = cg(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(cg)(); + VERIFY( q.as_const && ! q.as_lvalue ); + + int i = 0; + auto gr = bind_front<f>(args..., std::ref(i)); + const auto& cgr = gr; + + // bound object is reference wrapper, converts to same type of reference + q = gr(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = std::move(gr)(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = cgr(); + VERIFY( ! q.as_const && q.as_lvalue ); + q = std::move(cgr)(); + VERIFY( ! q.as_const && q.as_lvalue ); + + auto gcr = bind_front<f>(args..., std::cref(i)); + const auto& cgcr = gcr; + + q = gcr(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(gcr)(); + VERIFY( q.as_const && q.as_lvalue ); + q = cgcr(); + VERIFY( q.as_const && q.as_lvalue ); + q = std::move(cgcr)(); + VERIFY( q.as_const && q.as_lvalue ); +} + +template<typename... Args> +void +testCallArgs(Args... args) +{ + struct F + { + quals operator()(Args..., int&) const { return { false, true }; } + quals operator()(Args..., int const&) const { return { true, true }; } + quals operator()(Args..., int&&) const { return { false, false }; } + quals operator()(Args..., int const&&) const { return { true, false }; } + }; + + constexpr F f; + auto g = bind_front<f>(args...); + const auto& cg = g; + quals q; + int i = 10; + const int ci = i; + + q = g(i); + VERIFY( ! q.as_const && q.as_lvalue ); + q = g(std::move(i)); + VERIFY( ! q.as_const && ! q.as_lvalue ); + q = g(ci); + VERIFY( q.as_const && q.as_lvalue ); + q = g(std::move(ci)); + VERIFY( q.as_const && ! q.as_lvalue ); + + q = cg(i); + VERIFY( ! q.as_const && q.as_lvalue ); + q = cg(std::move(i)); + VERIFY( ! q.as_const && ! q.as_lvalue ); + q = cg(ci); + VERIFY( q.as_const && q.as_lvalue ); + q = cg(std::move(ci)); + VERIFY( q.as_const && ! q.as_lvalue ); + + struct S + { + int operator()(Args..., long, long) const { return 1; } + int operator()(Args..., int, void*) const { return 2; } + }; + + constexpr S s; + // literal zero can be converted to any pointer, so (int, void*) + // is best candidate + VERIFY( s(args..., 0, 0) == 2 ); + // both arguments are bound to int&&, and no longer can be + // converted to pointer, (long, long) is only candidate + VERIFY( bind_front<s>()(args..., 0, 0) == 1 ); + VERIFY( bind_front<s>(args...)(0, 0) == 1 ); +} + +void +test03() +{ + struct F + { + int& operator()(int& i, void*) { return i; } + void* operator()(long, void* p) const { return p; } + }; + + int i = 5; + void* vp = &vp; // arbitrary void* value + constexpr F f; + + // Bound arg always forwarded as const int& so can only call second overload: + + auto g0 = bind_front<f>(); // call wrapper has no bound arg + using G0 = decltype(g0); + static_assert(is_invocable_r_v<void*, G0&, int, void*>); + static_assert(is_invocable_r_v<void*, G0&, int&, void*>); + static_assert(is_invocable_r_v<void*, G0&, const int&, void*>); + static_assert(is_invocable_r_v<void*, const G0&, int, void*>); + static_assert(is_invocable_r_v<void*, G0&&, int, void*>); + void* p0 = static_cast<G0&&>(g0)(i, vp); + VERIFY( p0 == vp ); + + auto g1 = bind_front<f>(i); // call wrapper has bound arg of type int + using G1 = decltype(g1); + static_assert(!is_invocable_r_v<int&, G1&, void*>); + static_assert(is_invocable_r_v<void*, const G1&, void*>); + static_assert(is_invocable_r_v<void*, G1&&, void*>); + void* p1 = static_cast<G1&&>(g1)(vp); + VERIFY( p1 == vp ); + + auto g2 = bind_front<f>(std::ref(i)); // bound arg of type int& + using G2 = decltype(g2); + static_assert(is_invocable_r_v<void*, G2&, void*>); + static_assert(is_invocable_r_v<void*, G2&&, void*>); + static_assert(is_invocable_r_v<void*, const G2&, void*>); + static_assert(is_invocable_r_v<void*, const G2&&, void*>); + void* p2 = g2(vp); + VERIFY( p2 == vp ); + p2 = static_cast<G2&&>(g2)(vp); + VERIFY( p2 == vp ); + p2 = const_cast<const G2&>(g2)(vp); + VERIFY( p2 == vp ); + + auto g3 = bind_front<f>(std::cref(i)); // bound arg of type const int& + using G3 = decltype(g3); + static_assert(is_invocable_r_v<void*, G3&, void*>); + static_assert(is_invocable_r_v<void*, G3&&, void*>); + static_assert(is_invocable_r_v<void*, const G3&, void*>); + static_assert(is_invocable_r_v<void*, const G3&&, void*>); +} + +void test03a() +{ + struct F + { + int& operator()(int& i, void*) { return i; } + void* operator()(long, void* p) const { return p; } + }; + + int i = 5; + void* vp = &vp; // arbitrary void* value + constexpr F f; + + // Bound arg always forwarded as const int& so can only call second overload: + auto g1 = bind_front<f>(i); // call wrapper has bound arg of type int + using G1 = decltype(g1); + static_assert(is_invocable_r_v<void*, G1&, void*>); + static_assert(is_invocable_r_v<void*, const G1&, void*>); + static_assert(is_invocable_r_v<void*, G1&&, void*>); + void* p1 = static_cast<G1&&>(g1)(vp); + VERIFY( p1 == vp ); +} + +constexpr int f(int i, int j, int k) { return i + 2*j + 3*k; } + +consteval bool +test04() +{ + constexpr auto g = bind_front<f>(); + VERIFY( std::is_empty_v<decltype(g)> ); + VERIFY( g(1, 2, 3) == 1 + 2*2 + 3*3 ); + constexpr auto g1 = bind_front<f>(1); + VERIFY( g1(2, 3) == 1 + 2*2 + 3*3 ); + VERIFY( bind_front<g>(1)(2, 3) == 1 + 2*2 + 3*3 ); + constexpr auto g2 = bind_front<f>(1, 2); + VERIFY( g2(3) == 1 + 2*2 + 3*3 ); + constexpr auto g3 = bind_front<f>(1, 2, 3); + VERIFY( g3() == 1 + 2*2 + 3*3 ); + return true; +} + +struct CountedArg +{ + CountedArg() = default; + CountedArg(CountedArg&& f) noexcept : counter(f.counter) { ++counter; } + CountedArg& operator=(CountedArg&&) = delete; + + int counter = 0; +}; +CountedArg const c; + +void +testMaterialization() +{ + struct F + { + int operator()(int, CountedArg arg) const + { return arg.counter; }; + }; + + // CountedArg is bound to rvalue-reference thus moved + auto f0 = std::bind_front<F{}>(); + VERIFY( f0(10, CountedArg()) == 1 ); + + auto f1 = std::bind_front<F{}>(10); + VERIFY( f1(CountedArg()) == 1 ); +} + +int +main() +{ + test01(); + test03(); + test03a(); + static_assert(test04()); + + testTarget(); + testTarget(10); + testTarget(10, 20, 30); + + testBoundArgs(); + testBoundArgs(10); + testBoundArgs(10, 20, 30); + + testCallArgs(); + testCallArgs(10); + testCallArgs(10, 20, 30); + + testMaterialization(); + +} diff --git a/libstdc++-v3/testsuite/20_util/function_objects/bind_front/nttp_neg.cc b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/nttp_neg.cc new file mode 100644 index 0000000..d274c19 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_objects/bind_front/nttp_neg.cc @@ -0,0 +1,38 @@ +// { dg-do compile { target c++26 } } + +#include <functional> + +void f() {} +using fp = decltype(&f); +constexpr fp nfp = nullptr; + +struct A { void mf() const {} }; +using mfp = decltype(&A::mf); +constexpr mfp nnmfp = &A::mf; +constexpr mfp nmfp = nullptr; + +struct B { B() = default; B(B const&) = delete; }; +void bf(B const&) {}; + +struct C { C() = default; C(C&&) = delete; }; +void cf(C&&) {}; + +int main() +{ + std::bind_front<f>()(); + // Verify bind_front<fn> with fn a null pointer fails: + std::bind_front<nfp>()(); // { dg-error "here" } + + std::bind_front<nnmfp>(A{})(); + // Verify bind_front<mfn> with mfn a null member pointer fails: + std::bind_front<nmfp>(A{})(); // { dg-error "here" } + + // Verify passing uncopyable type fails: + std::bind_front<bf>(B{}); // { dg-error "here" } + // + // Verify passing unmovable type fails: + std::bind_front<cf>(C{}); // { dg-error "here" } +} + +// { dg-error "static assertion failed" "" { target *-*-* } 0 } +// { dg-error "use of deleted function" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/20_util/function_objects/not_fn/111327.cc b/libstdc++-v3/testsuite/20_util/function_objects/not_fn/111327.cc index 725a842..be58b0e 100644 --- a/libstdc++-v3/testsuite/20_util/function_objects/not_fn/111327.cc +++ b/libstdc++-v3/testsuite/20_util/function_objects/not_fn/111327.cc @@ -15,15 +15,28 @@ struct G { bool operator()(...) const &&; }; +struct Weird { + void operator()(); + bool operator()() const { return true; } +}; + int main() { auto f = std::not_fn(F{}); - f(); // { dg-error "deleted" } + f(); // { dg-error "no match" } std::move(f)(); std::as_const(f)(); std::move(std::as_const(f))(); auto g = std::not_fn(G{}); - g(); // { dg-error "deleted" } - std::move(g)(); // { dg-error "deleted" } + g(); // { dg-error "no match" } + std::move(g)(); // { dg-error "no match" } std::move(std::as_const(g))(); + + auto h = std::not_fn(Weird{}); + h(); // { dg-error "no match" } } + +// { dg-error "no type named 'type' in 'struct std::__invoke_result<" "" { target *-*-* } 0 } +// { dg-error "no matching function for call to 'std::_Not_fn<Weird>" "" { target *-*-* } 0 } +// { dg-error "could not convert 'std::declval<void>\\(\\)' from 'void' to 'bool'" "" { target *-*-* } 0 } +// { dg-error "in argument to unary !" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/20_util/function_objects/not_fn/nttp.cc b/libstdc++-v3/testsuite/20_util/function_objects/not_fn/nttp.cc new file mode 100644 index 0000000..3567d67 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_objects/not_fn/nttp.cc @@ -0,0 +1,141 @@ +// Test NTTP version of not_fn, from P2714 + +// { dg-do run { target c++26 } } + +#include <functional> +#include <testsuite_hooks.h> + +using std::not_fn; + +int func(int, char) { return 0; } + +struct F +{ + bool operator()() { return false; } + bool operator()() const { return true; } + bool operator()(int) const { return false; } +}; + +void +test01() +{ + auto f1 = not_fn<func>(); + VERIFY( std::is_empty_v<decltype(f1)> ); + VERIFY( f1(1, '2') == true ); + + auto f2 = not_fn<[] { return true; }>(); + VERIFY( std::is_empty_v<decltype(f2)> ); + VERIFY( f2() == false ); + + auto f3 = not_fn<F{}>(); + VERIFY( f3() == false ); // Prefer the const member. + VERIFY( f3(1) == true ); + const auto f4 = f3; + VERIFY( f4() == false ); +} + +void +test04() +{ + struct abstract { virtual void f() = 0; }; + struct derived : abstract { void f() { } }; + struct F { bool operator()(const abstract&) const { return false; } }; + constexpr F f; + constexpr derived d; + VERIFY( not_fn<f>()(d) ); +} + +void +test05() +{ + auto nf = std::not_fn<[] { return false; }>(); + auto copy(nf); // PR libstdc++/70564 +} + +void +test06() +{ + struct Boolean { + Boolean operator!() const noexcept(false) { return *this; } + }; + struct F { + Boolean operator()() const { return {}; } + }; + const F f; + const auto notf = std::not_fn<f>(); + using NotF = decltype(notf); + static_assert( std::is_invocable<NotF>::value, "cannot negate" ); + static_assert( !noexcept(notf()), "conversion to bool affects noexcept" ); +} + +void +test07() +{ + struct NonNegatable { }; // there is no operator!(NonNegatable) + struct F { + NonNegatable operator()() const { return {}; } + }; + F f; + constexpr auto notf = std::not_fn<f>(); + using NotF = decltype(notf); + static_assert( !std::is_invocable<NotF>::value, "cannot negate" ); +} + +void +test08() +{ + struct quals + { + bool as_const; + bool as_lvalue; + + quals operator!() const + { return *this; }; + }; + + struct F + { + quals operator()(int&) const { return { false, true }; } + quals operator()(int const&) const { return { true, true }; } + quals operator()(int&&) const { return { false, false }; } + quals operator()(int const&&) const { return { true, false }; } + }; + + constexpr F f; + auto g = not_fn<f>(); + const auto& cg = g; + quals q; + int i = 10; + const int ci = i; + + q = g(i); + VERIFY( ! q.as_const && q.as_lvalue ); + q = g(std::move(i)); + VERIFY( ! q.as_const && ! q.as_lvalue ); + q = g(ci); + VERIFY( q.as_const && q.as_lvalue ); + q = g(std::move(ci)); + VERIFY( q.as_const && ! q.as_lvalue ); + + q = cg(i); + VERIFY( ! q.as_const && q.as_lvalue ); + q = cg(std::move(i)); + VERIFY( ! q.as_const && ! q.as_lvalue ); + q = cg(ci); + VERIFY( q.as_const && q.as_lvalue ); + q = cg(std::move(ci)); + VERIFY( q.as_const && ! q.as_lvalue ); +} + +int +main() +{ + test01(); + test04(); + test05(); + test06(); + test07(); + test08(); + constexpr auto f = []{ return false; }; + static_assert(std::not_fn<f>()()); +} diff --git a/libstdc++-v3/testsuite/20_util/function_objects/not_fn/nttp_neg.cc b/libstdc++-v3/testsuite/20_util/function_objects/not_fn/nttp_neg.cc new file mode 100644 index 0000000..7875273 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_objects/not_fn/nttp_neg.cc @@ -0,0 +1,28 @@ +// { dg-do compile { target c++26 } } + +#include <functional> + +bool f() { return {}; } +using fp = decltype(&f); +constexpr fp nfp = nullptr; + +struct A { bool mf() const { return {}; } }; +using mfp = decltype(&A::mf); +constexpr mfp nnmfp = &A::mf; +constexpr mfp nmfp = nullptr; +constexpr A a; + +int main() +{ + (void) std::not_fn<f>()(); + + // Verify not_fn<fn> with fn a null pointer fails. + (void) std::not_fn<nfp>()(); // { dg-error "here" } + // + (void) std::not_fn<nnmfp>()(a); + + // Verify not_fn<mfn> with mfn a null member pointer fails. + return std::not_fn<nmfp>()(a); // { dg-error "here" } +} + +// { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/20_util/function_objects/not_fn/version.cc b/libstdc++-v3/testsuite/20_util/function_objects/not_fn/version.cc new file mode 100644 index 0000000..fc8a3c9 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_objects/not_fn/version.cc @@ -0,0 +1,12 @@ +// { dg-do preprocess { target c++17 } } +// { dg-add-options no_pch } + +#include <functional> + +#ifndef __cpp_lib_not_fn +# error "Feature test macro for not_fn is missing in <functional>" +#elif __cpp_lib_not_fn < 201603L +# error "Feature test macro __cpp_lib_not_fn has the wrong value for C++17 or later" +#elif __cplusplus > 202302L && __cpp_lib_not_fn < 202306L +# error "Feature test macro __cpp_lib_not_fn has the wrong value for C++26 or later" +#endif diff --git a/libstdc++-v3/testsuite/20_util/function_ref/assign.cc b/libstdc++-v3/testsuite/20_util/function_ref/assign.cc new file mode 100644 index 0000000..9b02dc4 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_ref/assign.cc @@ -0,0 +1,108 @@ +// { dg-do compile { target c++26 } } + +#include <functional> + +#ifndef __cpp_lib_function_ref +# error "Feature-test macro for function_ref missing in <functional>" +#elif __cpp_lib_function_ref != 202306L +# error "Feature-test macro for function_ref has wrong value in <functional>" +#endif + +using std::nontype; +using std::nontype_t; +using std::function_ref; + +using std::is_nothrow_move_assignable_v; +using std::is_nothrow_copy_assignable_v; +using std::is_nothrow_assignable_v; +using std::is_assignable_v; +using std::is_nothrow_swappable_v; +using std::is_trivially_copyable_v; + +static_assert( is_nothrow_move_assignable_v<function_ref<void()>> ); +static_assert( is_nothrow_copy_assignable_v<function_ref<void()>> ); +static_assert( is_nothrow_swappable_v<function_ref<void()>> ); + +static_assert( ! is_assignable_v<function_ref<void()>, std::nullptr_t> ); + +static_assert( is_nothrow_assignable_v<function_ref<void()>, void()> ); +static_assert( is_nothrow_assignable_v<function_ref<void()>, void(&)()> ); +static_assert( is_nothrow_assignable_v<function_ref<void()>, void(*)()> ); +static_assert( is_nothrow_assignable_v<function_ref<void()>, int()> ); +static_assert( is_nothrow_assignable_v<function_ref<void()>, int(&)()> ); +static_assert( is_nothrow_assignable_v<function_ref<void()>, int(*)()> ); +static_assert( ! is_nothrow_assignable_v<function_ref<void()>, void(int)> ); +static_assert( is_nothrow_assignable_v<function_ref<void(int)>, void(int)> ); + +static_assert( is_nothrow_assignable_v<function_ref<void()>, + void() noexcept> ); +static_assert( is_nothrow_assignable_v<function_ref<void() noexcept>, + void() noexcept> ); +static_assert( ! is_assignable_v<function_ref<void() noexcept>, void() > ); + +struct S +{ + int x; + int f(); +}; +int funS(S); + +static_assert( is_nothrow_assignable_v<function_ref<int(S)>, + decltype(funS)> ); +static_assert( is_nothrow_assignable_v<function_ref<int(S)>, + decltype(&funS)> ); +static_assert( ! is_assignable_v<function_ref<int(S)>, decltype(&S::x)> ); +static_assert( ! is_assignable_v<function_ref<int(S)>, decltype(&S::f)> ); + +static_assert( is_nothrow_assignable_v<function_ref<int(S)>, + nontype_t<funS>> ); +static_assert( is_nothrow_assignable_v<function_ref<int(S)>, + nontype_t<&funS>> ); +static_assert( is_nothrow_assignable_v<function_ref<int(S)>, + nontype_t<&S::x>> ); +static_assert( is_nothrow_assignable_v<function_ref<int(S)>, + nontype_t<&S::f>> ); +struct Q +{ + void operator()() const; +}; + +static_assert( ! is_assignable_v<function_ref<void()>, Q> ); +static_assert( ! is_assignable_v<function_ref<void()>, Q&> ); +static_assert( ! is_assignable_v<function_ref<void()>, const Q&> ); +static_assert( ! is_assignable_v<function_ref<void() const>, Q> ); +static_assert( ! is_assignable_v<function_ref<void() const>, Q&> ); +static_assert( ! is_assignable_v<function_ref<void() const>, const Q&> ); + +static_assert( is_nothrow_assignable_v<function_ref<void()>, + nontype_t<Q{}>> ); +static_assert( is_nothrow_assignable_v<function_ref<void() const>, + nontype_t<Q{}>> ); + +constexpr bool +test_constexpr() +{ + function_ref<void(S)> fp(nontype<funS>); + fp = nontype<funS>; + fp = nontype<&funS>; + fp = nontype<&S::x>; + fp = nontype<&S::f>; + + constexpr Q cq; + function_ref<void() const> fq(cq); + fq = nontype<cq>; + return true; +} +static_assert( test_constexpr() ); + +void func(); + +void +test_instantiation() +{ + function_ref<void(S)> fp(funS); + fp = funS; + fp = &funS; + + test_constexpr(); +} diff --git a/libstdc++-v3/testsuite/20_util/function_ref/call.cc b/libstdc++-v3/testsuite/20_util/function_ref/call.cc new file mode 100644 index 0000000..49c6030 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_ref/call.cc @@ -0,0 +1,263 @@ +// { dg-do run { target c++26 } } + +#include <functional> +#include <utility> +#include <testsuite_hooks.h> + +using std::nontype; +using std::function_ref; + +using std::is_same_v; +using std::is_invocable_v; +using std::is_nothrow_invocable_v; +using std::invoke_result_t; + +// Check return types +static_assert( is_same_v<void, invoke_result_t<function_ref<void()>>> ); +static_assert( is_same_v<int, invoke_result_t<function_ref<int()>>> ); +static_assert( is_same_v<int&, invoke_result_t<function_ref<int&()>>> ); + +// Const qualier applies to target object +static_assert( is_invocable_v< function_ref<void()> const > ); +static_assert( is_invocable_v< function_ref<void()> const &> ); +static_assert( is_invocable_v< function_ref<void() const> > ); +static_assert( is_invocable_v< function_ref<void() const> &> ); +static_assert( is_invocable_v< function_ref<void() const> const > ); +static_assert( is_invocable_v< function_ref<void() const> const &> ); + +// With noexcept-specifier +static_assert( ! is_nothrow_invocable_v< function_ref<void()> > ); +static_assert( ! is_nothrow_invocable_v< function_ref<void() noexcept(false)> > ); +static_assert( is_nothrow_invocable_v< function_ref<void() noexcept> > ); + +void +test01() +{ + struct F + { + int operator()() { return 0; } + int operator()() const { return 1; } + }; + + function_ref<int()> f0{F{}}; + VERIFY( f0() == 0 ); + VERIFY( std::move(f0)() == 0 ); + + function_ref<int()> f1{nontype<F{}>}; + VERIFY( f1() == 1 ); + VERIFY( std::move(f1)() == 1 ); + + function_ref<int() const> f2{F{}}; + VERIFY( f2() == 1 ); + VERIFY( std::as_const(f2)() == 1 ); + VERIFY( std::move(f2)() == 1 ); + VERIFY( std::move(std::as_const(f2))() == 1 ); + + function_ref<int() const> f3{nontype<F{}>}; + VERIFY( f3() == 1 ); + VERIFY( std::as_const(f3)() == 1 ); + VERIFY( std::move(f3)() == 1 ); + VERIFY( std::move(std::as_const(f3))() == 1 ); +} + +void +test02() +{ + struct F + { + struct Arg {}; + int operator()(Arg& arg) const { return 0; } + int operator()(const Arg& arg) const { return 1; } + }; + F::Arg arg; + + function_ref<int()> f0{std::nontype<F{}>, arg}; + VERIFY( f0() == 0 ); + VERIFY( std::move(f0)() == 0 ); + + function_ref<int() const> f1{std::nontype<F{}>, arg}; + VERIFY( f1() == 1 ); + VERIFY( std::as_const(f1)() == 1 ); +} + +void +test03() +{ + struct F + { + struct Arg {}; + int operator()(Arg* arg) const { return 0; } + int operator()(const Arg* arg) const { return 1; } + }; + F::Arg arg; + + function_ref<int()> f0{std::nontype<F{}>, &arg}; + VERIFY( f0() == 0 ); + VERIFY( std::move(f0)() == 0 ); + + function_ref<int() const> f1{std::nontype<F{}>, &arg}; + VERIFY( f1() == 1 ); + VERIFY( std::as_const(f1)() == 1 ); +} + +void +test04() +{ + constexpr int (*fp)() = [] { return 0; }; + function_ref<int()> f0{fp}; + VERIFY( f0() == 0 ); + VERIFY( std::move(f0)() == 0 ); + + function_ref<int()> f1{nontype<fp>}; + VERIFY( f1() == 0 ); + VERIFY( std::move(f1)() == 0 ); + + const function_ref<int() const> f2{fp}; + VERIFY( f2() == 0 ); + VERIFY( std::move(f2)() == 0 ); + + const function_ref<int() const> f3{nontype<fp>}; + VERIFY( f2() == 0 ); + VERIFY( std::move(f2)() == 0 ); +} + +using ftype = int(int); +int twice(int x) { return x * 2; } +int cube(int x) { return x * x * x; } +int callback_ptr(ftype* f, int x) { return f(x); } +int callback_ref(ftype& f, int x) { return f(x); } + +void +test05() +{ + function_ref<int(int)> r1(nontype<&callback_ptr>, &twice); + VERIFY( r1(2) == 4 ); + function_ref<int(int)> r2(nontype<&callback_ptr>, cube); + VERIFY( r2(2) == 8 ); + + function_ref<int(int)> r3(nontype<&callback_ref>, twice); + VERIFY( r3(3) == 6 ); + function_ref<int(int)> r4(nontype<&callback_ref>, cube); + VERIFY( r4(3) == 27 ); +} + +void +test06() +{ + struct S { + int v; + int& m() { return v; } + const int& c() const { return v; } + + int& operator()(int) { return v; } + int const& operator()(int, int) const { return v; } + }; + S s{10}; + std::reference_wrapper<S> sr(s); + std::reference_wrapper<const S> csr(s); + + std::function_ref<int&(int)> e1(sr); + VERIFY( &e1(0) == &s.v ); + std::function_ref<int&(int) const> e2(sr); + VERIFY( &e2(0) == &s.v ); + std::function_ref<int&(int) const> e3(std::as_const(sr)); + VERIFY( &e3(0) == &s.v ); + + std::function_ref<const int&(int, int)> e4(sr); + VERIFY( &e4(0, 0) == &s.v ); + std::function_ref<const int&(int, int) const> e5(sr); + VERIFY( &e5(0, 0) == &s.v ); + std::function_ref<const int&(int, int)> e6(csr); + VERIFY( &e6(0, 0) == &s.v ); + std::function_ref<const int&(int, int) const> e7(csr); + VERIFY( &e7(0, 0) == &s.v ); + std::function_ref<const int&(int, int) const> e8(std::as_const(csr)); + VERIFY( &e8(0, 0) == &s.v ); + + std::function_ref<int&()> f1(std::nontype<&S::v>, sr); + VERIFY( &f1() == &s.v ); + std::function_ref<const int&()> f2(std::nontype<&S::v>, sr); + VERIFY( &f2() == &s.v ); + std::function_ref<int&()> f3(std::nontype<&S::m>, sr); + VERIFY( &f3() == &s.v ); + std::function_ref<const int&()> f4(std::nontype<&S::c>, sr); + VERIFY( &f4() == &s.v ); + + std::function_ref<const int&()> f5(std::nontype<&S::v>, csr); + VERIFY( &f5() == &s.v ); + std::function_ref<const int&()> f6(std::nontype<&S::c>, sr); + VERIFY( &f6() == &s.v ); + static_assert( !std::is_constructible_v< + std::function_ref<int&()>, + std::nontype_t<&S::c>, std::reference_wrapper<S>&> + ); + + std::function_ref<int&()> f7(std::nontype<&S::v>, std::as_const(sr)); + VERIFY( &f7() == &s.v ); + std::function_ref<const int&()> f8(std::nontype<&S::m>, std::as_const(sr)); + VERIFY( &f8() == &s.v ); + + // No rvalue reference_wrapper support + static_assert( !std::is_constructible_v< + std::function_ref<int&()>, + std::nontype_t<&S::v>, std::reference_wrapper<S>> + ); + static_assert( !std::is_constructible_v< + std::function_ref<int&()>, + std::nontype_t<&S::v>, std::reference_wrapper<const S>> + ); + + // reference to reference_wrapper are bound, so mutation are visible + S s2{20}; + sr = s2; + csr = s2; + VERIFY( &e1(0) == &s2.v ); + VERIFY( &e2(0) == &s2.v ); + VERIFY( &e3(0) == &s2.v ); + VERIFY( &e4(0, 0) == &s2.v ); + VERIFY( &e5(0, 0) == &s2.v ); + VERIFY( &e6(0, 0) == &s2.v ); + VERIFY( &e7(0, 0) == &s2.v ); + VERIFY( &e8(0, 0) == &s2.v ); + VERIFY( &f1() == &s2.v ); + VERIFY( &f2() == &s2.v ); + VERIFY( &f3() == &s2.v ); + VERIFY( &f4() == &s2.v ); + VERIFY( &f5() == &s2.v ); + VERIFY( &f6() == &s2.v ); + VERIFY( &f7() == &s2.v ); + VERIFY( &f8() == &s2.v ); + + constexpr auto id = []<typename T>(const std::reference_wrapper<T>& x) + { return &x; }; + + // identity of reference_wrapper is preserved + std::function_ref<const std::reference_wrapper<S>*()> g1(std::nontype<id>, sr); + VERIFY( g1() == &sr ); + std::function_ref<const std::reference_wrapper<const S>*()> g2(std::nontype<id>, csr); + VERIFY( g2() == &csr ); +} + +struct Incomplete; +enum CompleteEnum : int; + +void +test_params() +{ + auto f = [](auto&&) {}; + std::function_ref<void(Incomplete&)> f1(f); + // See PR libstdc++/120259, this should be supported. + // std::function_ref<void(Incomplete&&)> f2(f); + std::function_ref<void(CompleteEnum)> f3(f); +} + +int main() +{ + test01(); + test02(); + test03(); + test04(); + test05(); + test06(); + test_params(); +} diff --git a/libstdc++-v3/testsuite/20_util/function_ref/cons.cc b/libstdc++-v3/testsuite/20_util/function_ref/cons.cc new file mode 100644 index 0000000..a91f5ba --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_ref/cons.cc @@ -0,0 +1,218 @@ +// { dg-do compile { target c++26 } } +// { dg-add-options no_pch } + +#include <functional> + +#ifndef __cpp_lib_function_ref +# error "Feature-test macro for function_ref missing in <functional>" +#elif __cpp_lib_function_ref != 202306L +# error "Feature-test macro for function_ref has wrong value in <functional>" +#endif + +using std::nontype; +using std::nontype_t; +using std::function_ref; + +using std::is_default_constructible_v; +using std::is_nothrow_copy_constructible_v; +using std::is_nothrow_move_constructible_v; +using std::is_nothrow_constructible_v; +using std::is_constructible_v; +using std::is_trivially_copyable_v; + +static_assert( ! is_default_constructible_v<function_ref<void()>> ); +static_assert( is_nothrow_move_constructible_v<function_ref<void()>> ); +static_assert( is_nothrow_copy_constructible_v<function_ref<void()>> ); +static_assert( is_trivially_copyable_v<function_ref<void()>> ); + +static_assert( ! is_constructible_v<function_ref<void()>, std::nullptr_t> ); + +static_assert( is_nothrow_constructible_v<function_ref<void()>, void()> ); +static_assert( is_nothrow_constructible_v<function_ref<void()>, void(&)()> ); +static_assert( is_nothrow_constructible_v<function_ref<void()>, void(*)()> ); +static_assert( is_nothrow_constructible_v<function_ref<void()>, int()> ); +static_assert( is_nothrow_constructible_v<function_ref<void()>, int(&)()> ); +static_assert( is_nothrow_constructible_v<function_ref<void()>, int(*)()> ); +static_assert( ! is_constructible_v<function_ref<void()>, void(int)> ); +static_assert( is_nothrow_constructible_v<function_ref<void(int)>, void(int)> ); + +static_assert( is_nothrow_constructible_v<function_ref<void()>, + void() noexcept> ); +static_assert( is_nothrow_constructible_v<function_ref<void() noexcept>, + void() noexcept> ); +static_assert( ! is_constructible_v<function_ref<void() noexcept>, + void() > ); + +struct S +{ + int x; + int f(); +}; +int funS(S); + +static_assert( is_nothrow_constructible_v<function_ref<int(S)>, + decltype(funS)> ); +static_assert( is_nothrow_constructible_v<function_ref<int(S)>, + decltype(&funS)> ); +static_assert( ! is_constructible_v<function_ref<int(S)>, + decltype(&S::x)> ); +static_assert( ! is_constructible_v<function_ref<int(S)>, + decltype(&S::f)> ); + +static_assert( is_nothrow_constructible_v<function_ref<int(S)>, + nontype_t<funS>> ); +static_assert( is_nothrow_constructible_v<function_ref<int(S)>, + nontype_t<&funS>> ); +static_assert( is_nothrow_constructible_v<function_ref<int(S)>, + nontype_t<&S::x>> ); +static_assert( is_nothrow_constructible_v<function_ref<int(S)>, + nontype_t<&S::f>> ); + +static_assert( is_nothrow_constructible_v<function_ref<int()>, + nontype_t<funS>, S&> ); +static_assert( is_nothrow_constructible_v<function_ref<int()>, + nontype_t<&funS>, S&> ); +static_assert( is_nothrow_constructible_v<function_ref<int()>, + nontype_t<&S::x>, S&> ); +static_assert( is_nothrow_constructible_v<function_ref<int()>, + nontype_t<&S::f>, S&> ); + +static_assert( ! is_constructible_v<function_ref<int()>, + nontype_t<funS>, S*> ); +static_assert( ! is_constructible_v<function_ref<int()>, + nontype_t<&funS>, S*> ); +static_assert( is_nothrow_constructible_v<function_ref<int()>, + nontype_t<&S::x>, S*> ); +static_assert( is_nothrow_constructible_v<function_ref<int()>, + nontype_t<&S::f>, S*> ); + +struct M +{ + void operator()(); +}; + + +static_assert( is_nothrow_constructible_v<function_ref<void()>, M> ); +static_assert( is_nothrow_constructible_v<function_ref<void()>, M&> ); +static_assert( ! is_constructible_v<function_ref<void()>, const M&> ); +static_assert( ! is_constructible_v<function_ref<void() const>, M> ); +static_assert( ! is_constructible_v<function_ref<void() const>, const M&> ); +static_assert( ! is_constructible_v<function_ref<void()>, + nontype_t<M{}>> ); +static_assert( ! is_constructible_v<function_ref<void() const>, + nontype_t<M{}>> ); +struct Q +{ + void operator()(int) const; + void operator()(int*) const; +}; + +static_assert( is_nothrow_constructible_v<function_ref<void(int)>, Q> ); +static_assert( is_nothrow_constructible_v<function_ref<void(int)>, Q&> ); +static_assert( is_nothrow_constructible_v<function_ref<void(int)>, const Q&> ); +static_assert( is_nothrow_constructible_v<function_ref<void(int) const>, Q> ); +static_assert( is_nothrow_constructible_v<function_ref<void(int) const>, Q&> ); +static_assert( is_nothrow_constructible_v<function_ref<void(int) const>, const Q&> ); + +static_assert( is_nothrow_constructible_v<function_ref<void(int)>, + nontype_t<Q{}>> ); +static_assert( is_nothrow_constructible_v<function_ref<void(int) const>, + nontype_t<Q{}>> ); +static_assert( is_nothrow_constructible_v<function_ref<void()>, + nontype_t<Q{}>, int&> ); +static_assert( is_nothrow_constructible_v<function_ref<void() const>, + nontype_t<Q{}>, int&> ); +static_assert( ! is_constructible_v<function_ref<void()>, + nontype_t<Q{}>, int> ); +static_assert( ! is_constructible_v<function_ref<void() const>, + nontype_t<Q{}>, int> ); + +static_assert( is_nothrow_constructible_v<function_ref<void()>, + nontype_t<Q{}>, int*> ); +static_assert( ! is_constructible_v<function_ref<void() const>, + nontype_t<Q{}>, int*> ); + +struct L +{ + void operator()() &; +}; + +static_assert( is_nothrow_constructible_v<function_ref<void()>, L> ); +static_assert( is_nothrow_constructible_v<function_ref<void()>, L&> ); +static_assert( ! is_constructible_v<function_ref<void()>, const L&> ); +static_assert( ! is_constructible_v<function_ref<void() const>, L> ); +static_assert( ! is_constructible_v<function_ref<void() const>, const L&> ); +static_assert( ! is_constructible_v<function_ref<void()>, + nontype_t<L{}>> ); +static_assert( ! is_constructible_v<function_ref<void() const>, + nontype_t<L{}>> ); + +struct R +{ + void operator()(float) const&&; +}; + +static_assert( ! is_constructible_v<function_ref<void(float)>, R> ); +static_assert( ! is_constructible_v<function_ref<void(float)>, R&> ); +static_assert( ! is_constructible_v<function_ref<void(float) const>, R> ); +static_assert( ! is_constructible_v<function_ref<void(float) const>, R&> ); +static_assert( ! is_constructible_v<function_ref<void(float) const>, const R&> ); + +static_assert( ! is_constructible_v<function_ref<void(float)>, + nontype_t<R{}>> ); +static_assert( ! is_constructible_v<function_ref<void(float) const>, + nontype_t<R{}>> ); + +constexpr bool +test_constexpr() +{ + function_ref<void(S)> fp1(nontype<funS>); + function_ref<void(S)> fp3(nontype<&funS>); + function_ref<void(S)> fp4(nontype<&S::x>); + function_ref<void(S)> fp5(nontype<&S::f>); + + S s; + function_ref<void()> fp6(nontype<&funS>, s); + function_ref<void()> fp7(nontype<&S::x>, s); + function_ref<void()> fp8(nontype<&S::x>, &s); + function_ref<void()> fp9(nontype<&S::f>, s); + function_ref<void()> fp10(nontype<&S::f>, &s); + + M m; + function_ref<void()> fm1(m); + function_ref<void()> fm2(std::move(m)); + + Q q; + constexpr Q cq; + function_ref<void(int)> fq1(q); + function_ref<void(int) const> fq2(q); + function_ref<void(int) const> fq3(std::move(q)); + + function_ref<void(int)> fcq1(cq); + function_ref<void(int) const> f(cq); + function_ref<void(int)> fcq3(nontype<cq>); + function_ref<void(int) const> fcq4(nontype<cq>); + + int i = 0; + function_ref<void()> fcq5(nontype<cq>, i); + function_ref<void() const> fcq6(nontype<cq>, i); + function_ref<void()> fcq7(nontype<cq>, &i); + + L l; + function_ref<void()> fl1(l); + function_ref<void()> fl2(std::move(l)); + + return true; +} +static_assert( test_constexpr() ); + +void func(); + +void +test_instantiation() +{ + function_ref<void(S)> fp1(funS); + function_ref<void(S)> fp2(&funS); + + test_constexpr(); +} diff --git a/libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc b/libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc new file mode 100644 index 0000000..050090d --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_ref/cons_neg.cc @@ -0,0 +1,30 @@ +// { dg-do compile { target c++26 } } + +#include <functional> + +using std::nontype; +using std::function_ref; + +struct S +{ + int x; + void foo(); +}; +S s; + +constexpr int(*fp)(S) = nullptr; +constexpr int S::*mdp = nullptr; +constexpr int (S::*mfp)() = nullptr; + +function_ref<int(S)> fd1(nontype<fp>); // { dg-error "from here" } +function_ref<int(S)> fd2(nontype<mdp>); // { dg-error "from here" } +function_ref<int(S)> fd3(nontype<mfp>); // { dg-error "from here" } + +function_ref<int()> br4(nontype<fp>, s); // { dg-error "from here" } +function_ref<int()> br5(nontype<mdp>, s); // { dg-error "from here" } +function_ref<int()> br6(nontype<mfp>, s); // { dg-error "from here" } + +function_ref<int()> bp7(nontype<mdp>, &s); // { dg-error "from here" } +function_ref<int()> bp8(nontype<mfp>, &s); // { dg-error "from here" } + +// { dg-prune-output "static assertion failed" } diff --git a/libstdc++-v3/testsuite/20_util/function_ref/conv.cc b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc new file mode 100644 index 0000000..7606d26 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_ref/conv.cc @@ -0,0 +1,293 @@ +// { dg-do run { target c++26 } } +// { dg-require-effective-target hosted } + +#include <functional> +#include <string_view> +#include <testsuite_hooks.h> + +using std::function_ref; + +static_assert( std::is_constructible_v<std::function_ref<void() const>, + std::function_ref<void()>> ); + +// Non-trivial args, guarantess that type is not passed by copy +struct CountedArg +{ + CountedArg() = default; + CountedArg(const CountedArg& f) noexcept : counter(f.counter) { ++counter; } + CountedArg& operator=(CountedArg&&) = delete; + + int counter = 0; +}; +CountedArg const c; + +using FuncType = int(int); + +// Top level const qualifiers are ignored in function types, and decay +// is performed. +static_assert( std::is_same_v<std::function_ref<void(int const)>, + std::function_ref<void(int)>> ); +static_assert( std::is_same_v<std::function_ref<void(int[2])>, + std::function_ref<void(int*)>>); +static_assert( std::is_same_v<std::function_ref<void(int[])>, + std::function_ref<void(int*)>>); +static_assert( std::is_same_v<std::function_ref<void(int const[5])>, + std::function_ref<void(int const*)>>); +static_assert( std::is_same_v<std::function_ref<void(FuncType)>, + std::function_ref<void(FuncType*)>>); + +// The C++26 [func.wrap.general] p2 does not currently cover funciton_ref, +// so we make extra copies of arguments. + +void +test01() +{ + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::function_ref<int(CountedArg) const noexcept> r1(f); + std::move_only_function<int(CountedArg) const noexcept> m1(f); + std::copyable_function<int(CountedArg) const noexcept> c1(f); + + // Complatible signatures + std::function_ref<int(CountedArg) const noexcept> r2m(m1); + VERIFY( r2m(c) == 2 ); + std::function_ref<int(CountedArg) const noexcept> r2c(c1); + VERIFY( r2c(c) == 2 ); + + std::function_ref<int(CountedArg) const> r3r(r1); + VERIFY( r3r(c) == 2 ); + std::function_ref<int(CountedArg) const> r3m(m1); + VERIFY( r3m(c) == 2 ); + std::function_ref<int(CountedArg) const> r3c(c1); + VERIFY( r3c(c) == 2 ); + + std::function_ref<int(CountedArg)> r4r(r1); + VERIFY( r4r(c) == 2 ); + std::function_ref<int(CountedArg)> r4m(m1); + VERIFY( r4m(c) == 2 ); + std::function_ref<int(CountedArg)> r4c(c1); + VERIFY( r4c(c) == 2 ); + + // Incompatible signatures + std::function_ref<long(CountedArg) const noexcept> r5r(r1); + VERIFY( r5r(c) == 2 ); + std::function_ref<long(CountedArg) const noexcept> r5m(m1); + VERIFY( r5r(c) == 2 ); + std::function_ref<long(CountedArg) const noexcept> r5c(c1); + VERIFY( r5r(c) == 2 ); +} + +void +test02() +{ + // Constructing move_only_function and copyable_function from function_ref, + // have not chance to restore manager, so we store function_ref inside. + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::function_ref<int(CountedArg) const noexcept> r1(f); + + std::move_only_function<int(CountedArg) const noexcept> m1(r1); + VERIFY( m1(c) == 2 ); + + std::copyable_function<int(CountedArg) const noexcept> c1(r1); + VERIFY( c1(c) == 2 ); +} + +void +test03() +{ + struct F + { + int operator()(CountedArg const& arg) noexcept + { return arg.counter; } + + int operator()(CountedArg const& arg) const noexcept + { return arg.counter + 1000; } + }; + + F f; + std::function_ref<int(CountedArg) const> r1(f); + VERIFY( r1(c) == 1001 ); + + // Call const overload as std::function_ref<int(CountedArg) const> + // inside std::function_ref<int(CountedArg)> would do. + std::function_ref<int(CountedArg)> r2(r1); + VERIFY( r2(c) == 1002 ); + std::move_only_function<int(CountedArg)> m2(r1); + VERIFY( m2(c) == 1002 ); + + // Call non-const overload as const-qualifed operator() for + // std::function_ref<int(CountedArg)> do. + std::function_ref<int(CountedArg)> r3(f); + VERIFY( r3(c) == 1 ); + std::function_ref<int(CountedArg) const> r4(r3); + VERIFY( r4(c) == 2 ); + std::move_only_function<int(CountedArg) const> m4(r3); + VERIFY( m4(c) == 2 ); +} + +void +test04() +{ + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::function_ref<int(CountedArg)> w1(f); + // function_ref stores function_ref due incompatibile signatures + std::function_ref<int(CountedArg const&)> w2(std::move(w1)); + // copy is made when passing to int(CountedArg) + VERIFY( w2(c) == 1 ); + // wrapped 3 times + std::function_ref<int(CountedArg)> w3(w2); + VERIFY( w3(c) == 2 ); + // wrapped 4 times + std::function_ref<int(CountedArg const&)> w4(w3); + VERIFY( w4(c) == 2 ); + // wrapped 5 times + std::function_ref<int(CountedArg)> w5(w4); + VERIFY( w5(c) == 3 ); +} + +void +test05() +{ + // No special interoperability with std::function + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::function<int(CountedArg)> f1(f); + std::function_ref<int(CountedArg) const> c1(std::move(f1)); + VERIFY( c1(c) == 2 ); + + std::function_ref<int(CountedArg) const> c2(f); + std::function<int(CountedArg)> f2(c2); + VERIFY( f2(c) == 2 ); +} + +void +test06() +{ + auto* func = +[]{ static int x; return &x; }; + std::move_only_function<const void*() const> m1(func); + std::function_ref<const void*() const> rm1(m1); + VERIFY( m1() == rm1() ); + std::copyable_function<const void*() const> c1(func); + std::function_ref<const void*() const> rc1(c1); + VERIFY( c1() == rc1() ); + + struct Trivial + { + void const* operator()() const + { return this; } + }; + std::move_only_function<const void*() const> m2(Trivial{}); + std::function_ref<const void*() const> rm2(m2); + VERIFY( m2() == rm2() ); + std::copyable_function<const void*() const> c2(Trivial{}); + std::function_ref<const void*() const> rc2(c2); + VERIFY( c2() == rc2() ); + + struct NonTrivial : Trivial + { + NonTrivial() {} + NonTrivial(NonTrivial&&) noexcept {} + NonTrivial(const NonTrivial&) {} + }; + std::move_only_function<const void*() const> m3(NonTrivial{}); + std::function_ref<const void*() const> rm3(m3); + VERIFY( m3() == rm3() ); + std::copyable_function<const void*() const> c3(NonTrivial{}); + std::function_ref<const void*() const> rc3(c3); + VERIFY( c3() == rc3() ); + + struct Large : Trivial + { + int tab[10]; + }; + std::move_only_function<const void*() const> m4(Large{}); + std::function_ref<const void*() const> rm4(m4); + VERIFY( m4() == rm4() ); + std::copyable_function<const void*() const> c4(Large{}); + std::function_ref<const void*() const> rc4(c4); + VERIFY( c4() == rc4() ); +} + +void +test07() +{ + int (*f1)() = [] { return 1; }; + int (*f2)() = [] { return 2; }; + + std::function_ref<int() const> r1(f1); + std::move_only_function<int() const> m1(f1); + std::copyable_function<int() const> c1(f1); + + std::function_ref<int() const> r2r(r1); + VERIFY( r2r() == 1 ); + r1 = f2; + VERIFY( r2r() == 1 ); // same-siganture, copy constructor is used + + std::function_ref<int() const> r2m(m1); + VERIFY( r2m() == 1 ); + m1 = f2; + VERIFY( r2m() == 2 ); + + std::function_ref<int() const> r2c(c1); + VERIFY( r2c() == 1 ); + c1 = f2; + VERIFY( r2c() == 2 ); + + std::function_ref<int()> r3r(r1); + VERIFY( r3r() == 2 ); + r1 = f1; + VERIFY( r3r() == 1 ); // converting-constructor + + std::function_ref<int()> r3m(m1); + VERIFY( r3m() == 2 ); + m1 = f1; + VERIFY( r3m() == 1 ); + + std::function_ref<int()> r3c(c1); + VERIFY( r3c() == 2 ); + c1 = f1; + VERIFY( r3c() == 1 ); + +} + +constexpr bool +test08() +{ + auto f = [](int x) noexcept { return x; }; + std::function_ref<int(int) const noexcept> rf(f); + + std::function_ref<int(int) const noexcept> rr1(rf); + std::function_ref<int(int)> rr2(rf); + std::function_ref<int(long)> rr3(rf); + return true; +}; + +void +test09() +{ + // Scalar types and small trivially move constructible types are passed + // by value to invoker. So int&& signature is not compatible for such types. + auto fi = [](CountedArg const& arg, int) noexcept { return arg.counter; }; + std::function_ref<int(CountedArg, int) const noexcept> ri1(fi); + VERIFY( ri1(c, 0) == 1 ); + std::function_ref<int(CountedArg, int&&) const noexcept> ri2(ri1); + VERIFY( ri2(c, 0) == 2 ); + + auto fs = [](CountedArg const& arg, std::string_view) noexcept { return arg.counter; }; + std::function_ref<int(CountedArg, std::string_view) const noexcept> rs1(fs); + VERIFY( rs1(c, "") == 1 ); + std::function_ref<int(CountedArg, std::string_view&&) const noexcept> rs2(rs1); + VERIFY( rs2(c, "") == 2 ); +} + +int main() +{ + test01(); + test02(); + test03(); + test04(); + test05(); + test06(); + test07(); + test09(); + + static_assert( test08() ); +} diff --git a/libstdc++-v3/testsuite/20_util/function_ref/deduction.cc b/libstdc++-v3/testsuite/20_util/function_ref/deduction.cc new file mode 100644 index 0000000..b034c7a --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_ref/deduction.cc @@ -0,0 +1,131 @@ +// { dg-do compile { target c++26 } } + +#include <functional> +#include <type_traits> + +using std::is_same_v; +using std::nontype; +using std::nontype_t; +using std::function_ref; + +int i = 0; + +template<auto f, class... Args> + concept deductible = requires (Args&... args) + { std::function_ref(std::nontype<f>, args...); }; + +static_assert( !deductible<1> ); +static_assert( !deductible<1, int> ); + +void f0(); +void f0n() noexcept; + +static_assert( is_same_v<decltype(function_ref(f0)), + function_ref<void()>> ); +static_assert( is_same_v<decltype(function_ref(f0n)), + function_ref<void() noexcept>> ); +static_assert( is_same_v<decltype(function_ref(nontype<f0>)), + function_ref<void()>> ); +static_assert( is_same_v<decltype(function_ref(nontype<f0n>)), + function_ref<void() noexcept>> ); +static_assert( !deductible<f0, char*> ); +static_assert( !deductible<f0n, char*> ); + +void f1(int); +void f1n(int) noexcept; + +static_assert( is_same_v<decltype(function_ref(f1)), + function_ref<void(int)>> ); +static_assert( is_same_v<decltype(function_ref(f1n)), + function_ref<void(int) noexcept>> ); +static_assert( is_same_v<decltype(function_ref(nontype<f1>)), + function_ref<void(int)>> ); +static_assert( is_same_v<decltype(function_ref(nontype<f1n>)), + function_ref<void(int) noexcept>> ); +static_assert( is_same_v<decltype(function_ref(nontype<f1>, i)), + function_ref<void()>> ); +static_assert( is_same_v<decltype(function_ref(nontype<f1n>, i)), + function_ref<void() noexcept>> ); +static_assert( !deductible<f1, char*> ); +static_assert( !deductible<f1n, char*> ); + +void f2(int*, int); +void f2n(int*, int) noexcept; + +static_assert( is_same_v<decltype(function_ref(f2)), + function_ref<void(int*, int)>> ); +static_assert( is_same_v<decltype(function_ref(f2n)), + function_ref<void(int*, int) noexcept>> ); +static_assert( is_same_v<decltype(function_ref(nontype<f2>)), + function_ref<void(int*, int)>> ); +static_assert( is_same_v<decltype(function_ref(nontype<f2n>)), + function_ref<void(int*, int) noexcept>> ); +static_assert( is_same_v<decltype(function_ref(nontype<f2>, &i)), + function_ref<void(int)>> ); +static_assert( is_same_v<decltype(function_ref(nontype<f2n>, &i)), + function_ref<void(int) noexcept>> ); +static_assert( !deductible<f2, char*> ); +static_assert( !deductible<f2n, char*> ); + +struct S +{ + int mem; + int f(); + int fn() noexcept; + + int fc(int) const; + int fcn(int) const noexcept; + + int fl(int) &; + int fln(int) & noexcept; + + int fcl(float) const&; + int fcln(float) const& noexcept; + + int fr(int) &&; + int frn(int) && noexcept; +}; +S s{}; +const S cs{}; + +static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, s)), + function_ref<int&() noexcept>> ); +static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, cs)), + function_ref<const int&() noexcept>> ); +static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, &s)), + function_ref<int&() noexcept>> ); +static_assert( is_same_v<decltype(function_ref(nontype<&S::mem>, &cs)), + function_ref<const int&() noexcept>> ); +static_assert( !deductible<&S::mem, int> ); + +static_assert( is_same_v<decltype(function_ref(nontype<&S::f>, s)), + function_ref<int()>> ); +static_assert( is_same_v<decltype(function_ref(nontype<&S::fn>, &s)), + function_ref<int() noexcept>> ); +static_assert( !deductible<&S::f, char*> ); +static_assert( !deductible<&S::fn, char*> ); +static_assert( !deductible<&S::f, const S> ); +static_assert( !deductible<&S::fn, const S> ); + +static_assert( is_same_v<decltype(function_ref(nontype<&S::fc>, &s)), + function_ref<int(int)>> ); +static_assert( is_same_v<decltype(function_ref(nontype<&S::fcn>, s)), + function_ref<int(int) noexcept>> ); +static_assert( !deductible<&S::fc, char*> ); +static_assert( !deductible<&S::fcn, char*> ); + +static_assert( is_same_v<decltype(function_ref(nontype<&S::fl>, &s)), + function_ref<int(int)>> ); +static_assert( is_same_v<decltype(function_ref(nontype<&S::fln>, s)), + function_ref<int(int) noexcept>> ); + +static_assert( is_same_v<decltype(function_ref(nontype<&S::fcl>, s)), + function_ref<int(float)>> ); +static_assert( is_same_v<decltype(function_ref(nontype<&S::fcln>, &s)), + function_ref<int(float) noexcept>> ); + +static_assert( !deductible<&S::fr, char*> ); +static_assert( !deductible<&S::frn, char*> ); +static_assert( !deductible<&S::fr, S> ); +static_assert( !deductible<&S::frn, S> ); + diff --git a/libstdc++-v3/testsuite/20_util/function_ref/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/function_ref/incomplete_neg.cc new file mode 100644 index 0000000..c8db1ee --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_ref/incomplete_neg.cc @@ -0,0 +1,18 @@ +// { dg-do compile { target c++26 } } + +#include <functional> + +struct IncompleteClass; + +int a1 = alignof(std::function_ref<int(IncompleteClass)>); // { dg-error "here" } +int a2 = alignof(std::function_ref<int(int, IncompleteClass)>); // { dg-error "here" } + +enum Enum { + x = [] { + // Enum enumeration is incomplete here + int a3 = alignof(std::function_ref<int(Enum)>); // { dg-error "here" } + return 1; + }() +}; + +// { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/20_util/function_ref/mutation.cc b/libstdc++-v3/testsuite/20_util/function_ref/mutation.cc new file mode 100644 index 0000000..32c6931 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_ref/mutation.cc @@ -0,0 +1,85 @@ +// { dg-do run { target c++26 } } + +#include <functional> +#include <testsuite_hooks.h> + +using std::nontype; +using std::function_ref; + +void +test01() +{ + struct F { + int v; + int operator()() { return v; } + }; + F f1{2}, f2{5}; + + function_ref<int()> r1(f1); + function_ref<long()> r2(f1); + + VERIFY( r1() == 2 ); + VERIFY( r2() == 2 ); + + f1.v = 10; + + VERIFY( r1() == 10 ); + VERIFY( r2() == 10 ); + + r1 = function_ref<int()>(f2); + r2 = function_ref<long()>(f2); + + VERIFY( r1() == 5 ); + VERIFY( r2() == 5 ); + + f2.v = 13; + + VERIFY( r1() == 13 ); + VERIFY( r2() == 13 ); + + r1 = function_ref<int()>(f1); + r2 = function_ref<long()>(f1); + + f1.v = 20; + VERIFY( r1() == 20 ); + VERIFY( r2() == 20 ); +} + +void +test02() +{ + struct S + { + int x; + int f() { return x; }; + }; + S s{10}; + + function_ref<int()> r1(nontype<&S::x>, s); + function_ref<long()> r2(nontype<&S::x>, &s); + + VERIFY( r1() == 10 ); + VERIFY( r2() == 10 ); + + s.x = 20; + + VERIFY( r1() == 20 ); + VERIFY( r2() == 20 ); + + r1 = function_ref<int()>(nontype<&S::f>, &s); + r2 = function_ref<long()>(nontype<&S::f>, s); + + VERIFY( r1() == 20 ); + VERIFY( r2() == 20 ); + + s.x = 30; + + VERIFY( r1() == 30 ); + VERIFY( r2() == 30 ); +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/20_util/hash/int128.cc b/libstdc++-v3/testsuite/20_util/hash/int128.cc new file mode 100644 index 0000000..a26d2e2 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/hash/int128.cc @@ -0,0 +1,20 @@ +// { dg-do run { target c++11 } } +// { dg-add-options strict_std } + +#include <functional> +#include <testsuite_hooks.h> + +int main() +{ +#ifdef __SIZEOF_INT128__ + std::hash<__int128> h; + __int128 i = (__int128)0x123456789; + VERIFY( h(i) == (std::size_t)i ); + VERIFY( h(-i) == (std::size_t)-i ); + VERIFY( h(~i) == (std::size_t)~i ); + std::hash<unsigned __int128> hu; + unsigned __int128 u = i; + VERIFY( hu(u) == (std::size_t)u ); + VERIFY( hu(~u) == (std::size_t)~u ); +#endif +} diff --git a/libstdc++-v3/testsuite/20_util/headers/functional/synopsis.cc b/libstdc++-v3/testsuite/20_util/headers/functional/synopsis.cc index e3e9207..d58353e 100644 --- a/libstdc++-v3/testsuite/20_util/headers/functional/synopsis.cc +++ b/libstdc++-v3/testsuite/20_util/headers/functional/synopsis.cc @@ -18,7 +18,7 @@ // with this library; see the file COPYING3. If not see // <http://www.gnu.org/licenses/>. -// { dg-options "-Wno-deprecated" } +// { dg-options "-Wno-deprecated-declarations" } #include <functional> @@ -57,6 +57,13 @@ namespace std { template <class Predicate> _GLIBCXX14_CONSTEXPR binary_negate<Predicate> not2(const Predicate&); +#ifdef __cpp_lib_not_fn + template <typename F> _GLIBCXX20_CONSTEXPR auto not_fn(F&&) + noexcept(std::is_nothrow_constructible<std::decay_t<F>, F&&>::value); +#if __cpp_lib_not_fn >= 202306L + template <auto f> constexpr decltype(auto) not_fn() noexcept; +#endif +#endif // lib.binders, binders: template <class Operation> class binder1st; @@ -65,6 +72,24 @@ namespace std { template <class Operation> class binder2nd; template <class Operation, class T> binder2nd<Operation> bind2nd(const Operation&, const T&); +#ifdef __cpp_lib_bind_front + template <typename F, typename... Args> + _GLIBCXX20_CONSTEXPR auto bind_front(F&&, Args&&...); +#if __cpp_lib_bind_front >= 202306L + template <auto f, typename... Args> + constexpr decltype(auto) bind_front(Args&&...) + noexcept(__and_v<is_nothrow_constructible<Args>...>); +#endif +#endif +#ifdef __cpp_lib_bind_back + template <typename F, typename... Args> + _GLIBCXX20_CONSTEXPR auto bind_back(F&&, Args&&...); +#if __cpp_lib_bind_back >= 202306L + template <auto f, typename... Args> + constexpr decltype(auto) bind_back(Args&&...) + noexcept(__and_v<is_nothrow_constructible<Args>...>); +#endif +#endif // lib.function.pointer.adaptors, adaptors: template <class Arg, class Result> class pointer_to_unary_function; diff --git a/libstdc++-v3/testsuite/20_util/headers/memory/version.cc b/libstdc++-v3/testsuite/20_util/headers/memory/version.cc index c82c9a0..5366a5d 100644 --- a/libstdc++-v3/testsuite/20_util/headers/memory/version.cc +++ b/libstdc++-v3/testsuite/20_util/headers/memory/version.cc @@ -6,3 +6,11 @@ #if __cpp_lib_allocator_traits_is_always_equal != 201411L # error "Feature-test macro __cpp_lib_allocator_traits_is_always_equal has wrong value in <version>" #endif + +#if __cpp_lib_addressof_constexpr != 201603L +# error "Feature-test macro __cpp_lib_addressof_constexpr has wrong value in <version>" +#endif + +#if __cplusplus > 202302L && __cpp_lib_is_sufficiently_aligned != 202411L +# error "Feature-test macro __cpp_lib_is_sufficiently_aligned has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/20_util/integer_comparisons/extended.cc b/libstdc++-v3/testsuite/20_util/integer_comparisons/extended.cc new file mode 100644 index 0000000..d862b16 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/integer_comparisons/extended.cc @@ -0,0 +1,60 @@ +// { dg-do compile { target c++20 } } + +#include <utility> + +template<typename T> +constexpr bool +test() +{ + using S = std::make_signed_t<T>; + using U = std::make_unsigned_t<T>; + + static_assert( std::cmp_less((S)-1, (U)1)); + static_assert( ! std::cmp_less((S)20, (U)10)); + static_assert( ! std::cmp_less((U)20, (S)10)); + + static_assert( std::cmp_greater((S)100, (U)1) ); + static_assert( std::cmp_greater((U)100, (S)1) ); + static_assert( ! std::cmp_greater((S)-100, (U)1) ); + + static_assert( std::cmp_less_equal((S)-1, (U)1)); + static_assert( std::cmp_less_equal((U)10, (S)10)); + static_assert( ! std::cmp_less_equal((U)-100, (S)-100)); + + static_assert( std::cmp_greater_equal((S)200, (U)2) ); + static_assert( std::cmp_greater_equal((U)2000, (S)2000) ); + static_assert( ! std::cmp_greater_equal((S)-100, (U)100) ); + + static_assert( std::cmp_equal((S)1, (U)1) ); + static_assert( ! std::cmp_equal((S)-2, (U)-2) ); + + static_assert( std::cmp_not_equal((S)-1, (U)-1) ); + static_assert( ! std::cmp_not_equal((S)100, (U)100) ); + + static_assert( std::in_range<S>((U)5) ); + static_assert( std::in_range<U>((S)5) ); + static_assert( ! std::in_range<S>((U)-5) ); + static_assert( ! std::in_range<U>((S)-5) ); + + return true; +} + +#ifdef __SIZEOF_INT128__ +static_assert(test<__int128>()); +#endif + +#ifdef __GLIBCXX_TYPE_INT_N_0 +static_assert(test<__GLIBCXX_TYPE_INT_N_0>()); +#endif + +#ifdef __GLIBCXX_TYPE_INT_N_1 +static_assert(test<__GLIBCXX_TYPE_INT_N_1>()); +#endif + +#ifdef __GLIBCXX_TYPE_INT_N_2 +static_assert(test<__GLIBCXX_TYPE_INT_N_2>()); +#endif + +#ifdef __GLIBCXX_TYPE_INT_N_3 +static_assert(test<__GLIBCXX_TYPE_INT_N_3>()); +#endif diff --git a/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/120717.cc b/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/120717.cc new file mode 100644 index 0000000..4c07683 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/120717.cc @@ -0,0 +1,20 @@ +// PR libstdc++/120717 +// { dg-do compile { target c++11 } } +// { dg-additional-options "-Wsfinae-incomplete" } + +#include <type_traits> + +// Verify __is_complete_or_unbounded doesn't try to instantiate the underlying +// type of a reference or array of unknown bound. +template<class T> struct A { static_assert(false, "do not instantiate"); }; +static_assert(std::__is_complete_or_unbounded(std::__type_identity<A<int>&>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<A<int>&&>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<A<int>[]>{}), ""); + +// Verify __is_complete_or_unbounded doesn't produce unexpected +// -Wsfinae-incomplete warnings. +struct B; +static_assert(std::__is_complete_or_unbounded(std::__type_identity<B&>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<B&&>{}), ""); +static_assert(std::__is_complete_or_unbounded(std::__type_identity<B[]>{}), ""); +struct B { }; // { dg-bogus "-Wsfinae-incomplete" } diff --git a/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/memoization.cc b/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/memoization.cc index 256b84d..59af024 100644 --- a/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/memoization.cc +++ b/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/memoization.cc @@ -23,7 +23,7 @@ struct X; static_assert( !std::__is_complete_or_unbounded(std::__type_identity<X>{}), "error"); -struct X{}; +struct X{}; // { dg-warning Wsfinae-incomplete } static_assert( std::__is_complete_or_unbounded(std::__type_identity<X>{}), "Result memoized. This leads to worse diagnostics"); diff --git a/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/memoization_neg.cc b/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/memoization_neg.cc index 8e207b5..264efa7 100644 --- a/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/memoization_neg.cc +++ b/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/memoization_neg.cc @@ -25,5 +25,5 @@ struct X; constexpr bool res_incomplete = std::is_move_constructible<X>::value; // { dg-error "required from here" } -struct X{}; +struct X{}; // { dg-warning Wsfinae-incomplete } constexpr bool res_complete = std::is_default_constructible<X>::value; // { dg-bogus "required from here" } diff --git a/libstdc++-v3/testsuite/20_util/is_implicit_lifetime/value.cc b/libstdc++-v3/testsuite/20_util/is_implicit_lifetime/value.cc new file mode 100644 index 0000000..d8cb181 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/is_implicit_lifetime/value.cc @@ -0,0 +1,129 @@ +// Copyright (C) 2025 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-do compile { target c++23 } } +// { dg-add-options no_pch } + +#include <type_traits> + +#ifndef __cpp_lib_is_implicit_lifetime +# error "Feature test macro for is_implicit_lifetime is missing in <type_traits>" +#elif __cpp_lib_is_implicit_lifetime < 202302L +# error "Feature test macro for is_implicit_lifetime has wrong value in <type_traits>" +#endif + +#include <testsuite_tr1.h> + +template<typename T> + concept Is_implicit_lifetime + = __gnu_test::test_category<std::is_implicit_lifetime, T>(true); + +static_assert( ! Is_implicit_lifetime<void> ); +static_assert( ! Is_implicit_lifetime<const void> ); +static_assert( ! Is_implicit_lifetime<volatile void> ); +static_assert( Is_implicit_lifetime<char> ); +static_assert( Is_implicit_lifetime<signed char> ); +static_assert( Is_implicit_lifetime<const unsigned char> ); +static_assert( Is_implicit_lifetime<short> ); +static_assert( Is_implicit_lifetime<volatile unsigned short> ); +static_assert( Is_implicit_lifetime<int> ); +static_assert( Is_implicit_lifetime<unsigned int> ); +static_assert( Is_implicit_lifetime<const volatile long> ); +static_assert( Is_implicit_lifetime<unsigned long> ); +static_assert( Is_implicit_lifetime<long long> ); +static_assert( Is_implicit_lifetime<unsigned long long> ); +static_assert( Is_implicit_lifetime<float> ); +static_assert( Is_implicit_lifetime<double> ); +static_assert( Is_implicit_lifetime<long double volatile> ); +enum W { W1 }; +static_assert( Is_implicit_lifetime<W> ); +enum class X : int { X1 }; +static_assert( Is_implicit_lifetime<const volatile X> ); +static_assert( Is_implicit_lifetime<int *> ); +static_assert( Is_implicit_lifetime<int (*) (int)> ); +struct Y { int g; int foo (int); }; +static_assert( Is_implicit_lifetime<int (Y::*)> ); +static_assert( Is_implicit_lifetime<int (Y::*) (int)> ); +static_assert( ! Is_implicit_lifetime<int &> ); +static_assert( ! Is_implicit_lifetime<char &&> ); +static_assert( Is_implicit_lifetime<int []> ); +static_assert( Is_implicit_lifetime<int [1]> ); +static_assert( Is_implicit_lifetime<const Y [42]> ); +static_assert( ! Is_implicit_lifetime<int ()> ); +static_assert( ! Is_implicit_lifetime<int () &> ); +static_assert( ! Is_implicit_lifetime<int () const> ); +static_assert( ! Is_implicit_lifetime<int (&) ()> ); +struct Z; +static_assert( Is_implicit_lifetime<Z []> ); +static_assert( Is_implicit_lifetime<Z [5]> ); +struct A { int a, b, c; }; +static_assert( Is_implicit_lifetime<A> ); +class B { static int a; private: static int b; public: int c; }; +static_assert( Is_implicit_lifetime<B> ); +struct C { C () {} int a, b, c; }; +static_assert( Is_implicit_lifetime<C> ); +struct D { explicit D (int) {} int a, b, c; }; +static_assert( Is_implicit_lifetime<D> ); +struct E : public A { int d, e, f; }; +static_assert( Is_implicit_lifetime<E> ); +struct F : public C { using C::C; int d, e, f; }; +static_assert( Is_implicit_lifetime<F> ); +class G { int a, b; }; +static_assert( Is_implicit_lifetime<G> ); +struct H { private: int a, b; }; +static_assert( Is_implicit_lifetime<H> ); +struct I { protected: int a, b; }; +static_assert( Is_implicit_lifetime<I> ); +struct J { int a, b; void foo (); }; +static_assert( Is_implicit_lifetime<J> ); +struct K { int a, b; virtual void foo (); }; +static_assert( ! Is_implicit_lifetime<K> ); +struct L : virtual public A { int d, e; }; +static_assert( ! Is_implicit_lifetime<L> ); +struct M : protected A { int d, e; }; +static_assert( Is_implicit_lifetime<M> ); +struct N : private A { int d, e; }; +static_assert( Is_implicit_lifetime<N> ); +struct O { O () = delete; int a, b, c; }; +static_assert( Is_implicit_lifetime<O> ); +struct P { P () = default; int a, b, c; }; +static_assert( Is_implicit_lifetime<P> ); +struct Q { Q (); Q (const Q &); int a, b, c; }; +static_assert( ! Is_implicit_lifetime<Q> ); +struct R { R (); R (const R &); R (R &&) = default; int a, b, c; }; +static_assert( Is_implicit_lifetime<R> ); +struct S { S (); ~S (); int a, b, c; }; +static_assert( ! Is_implicit_lifetime<S> ); +static_assert( Is_implicit_lifetime<S [3]> ); +struct T { T (); ~T () = default; int a, b, c; }; +static_assert( Is_implicit_lifetime<T> ); +struct U { U (); U (const U &) = default; int a, b, c; }; +static_assert( Is_implicit_lifetime<U> ); +struct V { V () = default; V (const V &); int a, b, c; }; +static_assert( Is_implicit_lifetime<V> ); +struct AA { Q a; Q b; }; +static_assert( Is_implicit_lifetime<AA> ); +struct AB { Q a; Q b; ~AB () = default; }; +static_assert( Is_implicit_lifetime<AB> ); +struct AC { Q a; Q b; ~AC () {} }; +static_assert( ! Is_implicit_lifetime<AC> ); +struct AD : public Q {}; +static_assert( Is_implicit_lifetime<AD> ); +struct AE : public Q { ~AE () = default; }; +static_assert( Is_implicit_lifetime<AE> ); +struct AF : public Q { ~AF () {} }; +static_assert( ! Is_implicit_lifetime<AF> ); diff --git a/libstdc++-v3/testsuite/20_util/is_implicit_lifetime/version.cc b/libstdc++-v3/testsuite/20_util/is_implicit_lifetime/version.cc new file mode 100644 index 0000000..ed90b47 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/is_implicit_lifetime/version.cc @@ -0,0 +1,27 @@ +// Copyright (C) 2025 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-do compile { target c++23 } } +// { dg-add-options no_pch } + +#include <version> + +#ifndef __cpp_lib_is_implicit_lifetime +# error "Feature test macro for is_implicit_lifetime is missing in <version>" +#elif __cpp_lib_is_implicit_lifetime < 202302L +# error "Feature test macro for is_implicit_lifetime has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/20_util/is_literal_type/requirements/explicit_instantiation.cc b/libstdc++-v3/testsuite/20_util/is_literal_type/requirements/explicit_instantiation.cc index ca3676a..71d0f46 100644 --- a/libstdc++-v3/testsuite/20_util/is_literal_type/requirements/explicit_instantiation.cc +++ b/libstdc++-v3/testsuite/20_util/is_literal_type/requirements/explicit_instantiation.cc @@ -1,4 +1,4 @@ -// { dg-additional-options "-Wno-deprecated" { target c++17 } } +// { dg-additional-options "-Wno-deprecated-declarations" { target c++17 } } // { dg-do compile { target c++11 } } // 2010-02-21 Paolo Carlini <paolo.carlini@oracle.com> diff --git a/libstdc++-v3/testsuite/20_util/is_literal_type/requirements/typedefs.cc b/libstdc++-v3/testsuite/20_util/is_literal_type/requirements/typedefs.cc index 353d89f..7cfce9e 100644 --- a/libstdc++-v3/testsuite/20_util/is_literal_type/requirements/typedefs.cc +++ b/libstdc++-v3/testsuite/20_util/is_literal_type/requirements/typedefs.cc @@ -1,4 +1,4 @@ -// { dg-additional-options "-Wno-deprecated" { target c++17 } } +// { dg-additional-options "-Wno-deprecated-declarations" { target c++17 } } // { dg-do compile { target c++11 } } // 2010-02-21 Paolo Carlini <paolo.carlini@oracle.com> diff --git a/libstdc++-v3/testsuite/20_util/is_literal_type/value.cc b/libstdc++-v3/testsuite/20_util/is_literal_type/value.cc index b65d301..f8b1b8d 100644 --- a/libstdc++-v3/testsuite/20_util/is_literal_type/value.cc +++ b/libstdc++-v3/testsuite/20_util/is_literal_type/value.cc @@ -1,4 +1,4 @@ -// { dg-additional-options "-Wno-deprecated" { target c++17 } } +// { dg-additional-options "-Wno-deprecated-declarations" { target c++17 } } // { dg-do compile { target c++11 } } // 2010-03-23 Paolo Carlini <paolo.carlini@oracle.com> diff --git a/libstdc++-v3/testsuite/20_util/is_pod/requirements/explicit_instantiation.cc b/libstdc++-v3/testsuite/20_util/is_pod/requirements/explicit_instantiation.cc index 0bffe21..bcf67e1 100644 --- a/libstdc++-v3/testsuite/20_util/is_pod/requirements/explicit_instantiation.cc +++ b/libstdc++-v3/testsuite/20_util/is_pod/requirements/explicit_instantiation.cc @@ -1,5 +1,5 @@ // { dg-do compile { target c++11 } } -// { dg-additional-options "-Wno-deprecated" { target { c++2a } } } +// { dg-additional-options "-Wno-deprecated-declarations" { target { c++2a } } } // 2010-02-21 Paolo Carlini <paolo.carlini@oracle.com> // Copyright (C) 2010-2025 Free Software Foundation, Inc. diff --git a/libstdc++-v3/testsuite/20_util/is_pod/requirements/typedefs.cc b/libstdc++-v3/testsuite/20_util/is_pod/requirements/typedefs.cc index 38c6e13..6a41ea0 100644 --- a/libstdc++-v3/testsuite/20_util/is_pod/requirements/typedefs.cc +++ b/libstdc++-v3/testsuite/20_util/is_pod/requirements/typedefs.cc @@ -1,5 +1,5 @@ // { dg-do compile { target c++11 } } -// { dg-additional-options "-Wno-deprecated" { target { c++2a } } } +// { dg-additional-options "-Wno-deprecated-declarations" { target { c++2a } } } // 2010-02-21 Paolo Carlini <paolo.carlini@oracle.com> // diff --git a/libstdc++-v3/testsuite/20_util/is_pod/value.cc b/libstdc++-v3/testsuite/20_util/is_pod/value.cc index ff53aed..3722309 100644 --- a/libstdc++-v3/testsuite/20_util/is_pod/value.cc +++ b/libstdc++-v3/testsuite/20_util/is_pod/value.cc @@ -1,5 +1,5 @@ // { dg-do compile { target c++11 } } -// { dg-additional-options "-Wno-deprecated" { target { c++2a } } } +// { dg-additional-options "-Wno-deprecated-declarations" { target { c++2a } } } // 2010-02-21 Paolo Carlini <pcarlini@suse.de> // diff --git a/libstdc++-v3/testsuite/20_util/is_sufficiently_aligned/1.cc b/libstdc++-v3/testsuite/20_util/is_sufficiently_aligned/1.cc new file mode 100644 index 0000000..4c2738b --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/is_sufficiently_aligned/1.cc @@ -0,0 +1,31 @@ +// { dg-do run { target c++26 } } + +#include <memory> +#include <array> +#include <testsuite_hooks.h> + +void +test01() +{ + constexpr size_t N = 4; + constexpr size_t M = 2*N + 1; + alignas(N) std::array<char, M> buffer{}; + + auto* ptr = buffer.data(); + VERIFY(std::is_sufficiently_aligned<1>(ptr+0)); + VERIFY(std::is_sufficiently_aligned<1>(ptr+1)); + + VERIFY(std::is_sufficiently_aligned<2>(ptr+0)); + VERIFY(!std::is_sufficiently_aligned<2>(ptr+1)); + VERIFY(std::is_sufficiently_aligned<2>(ptr+2)); + + for (size_t i = 0; i < M; ++i) + VERIFY(std::is_sufficiently_aligned<N>(ptr + i) == (i % N == 0)); +} + +int +main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/20_util/make_unsigned/int128.cc b/libstdc++-v3/testsuite/20_util/make_unsigned/int128.cc new file mode 100644 index 0000000..46c07b7 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/make_unsigned/int128.cc @@ -0,0 +1,14 @@ +// { dg-do compile { target c++11 } } +// { dg-add-options strict_std } + +#include <type_traits> + +#ifdef __SIZEOF_INT128__ +enum E : __int128 { }; +using U = std::make_unsigned<E>::type; +static_assert( std::is_integral<U>::value, "type is an integer" ); +static_assert( sizeof(U) == sizeof(E), "width of type is 128 bits" ); +using I = std::make_signed<E>::type; +static_assert( std::is_integral<I>::value, "type is an integer" ); +static_assert( sizeof(I) == sizeof(E), "width of type is 128 bits" ); +#endif diff --git a/libstdc++-v3/testsuite/20_util/move_only_function/call.cc b/libstdc++-v3/testsuite/20_util/move_only_function/call.cc index bfc609a..34ca73b 100644 --- a/libstdc++-v3/testsuite/20_util/move_only_function/call.cc +++ b/libstdc++-v3/testsuite/20_util/move_only_function/call.cc @@ -190,21 +190,59 @@ test04() VERIFY( std::move(std::as_const(f5))() == 3 ); } +void +test05() +{ + int (*fp)() = [] { return 0; }; + move_only_function<int()> f0{fp}; + VERIFY( f0() == 0 ); + VERIFY( std::move(f0)() == 0 ); + + const move_only_function<int() const> f1{fp}; + VERIFY( f1() == 0 ); + VERIFY( std::move(f1)() == 0 ); +} + struct Incomplete; +enum CompleteEnum : int; void test_params() { - std::move_only_function<void(Incomplete)> f1; - std::move_only_function<void(Incomplete&)> f2; - std::move_only_function<void(Incomplete&&)> f3; + std::move_only_function<void(Incomplete&)> f1; + std::move_only_function<void(Incomplete&&)> f2; + std::move_only_function<void(CompleteEnum)> f4; } +struct EmptyIdFunc +{ + EmptyIdFunc* operator()() + { return this; } +}; + +struct Composed : EmptyIdFunc +{ + std::move_only_function<EmptyIdFunc*()> nested; +}; + +void +test_aliasing() +{ + Composed c; + c.nested = EmptyIdFunc{}; + + EmptyIdFunc* baseAddr = c(); + EmptyIdFunc* nestedAddr = c.nested(); + VERIFY( baseAddr != nestedAddr ); +}; + int main() { test01(); test02(); test03(); test04(); + test05(); test_params(); + test_aliasing(); } diff --git a/libstdc++-v3/testsuite/20_util/move_only_function/conv.cc b/libstdc++-v3/testsuite/20_util/move_only_function/conv.cc new file mode 100644 index 0000000..ef8bb37b --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/move_only_function/conv.cc @@ -0,0 +1,223 @@ +// { dg-do run { target c++23 } } +// { dg-require-effective-target hosted } + +#include <functional> +#include <string_view> +#include <testsuite_hooks.h> + +using std::move_only_function; + +static_assert( !std::is_constructible_v<std::move_only_function<void()>, + std::move_only_function<void()&>> ); +static_assert( !std::is_constructible_v<std::move_only_function<void()>, + std::move_only_function<void()&&>> ); +static_assert( !std::is_constructible_v<std::move_only_function<void()&>, + std::move_only_function<void()&&>> ); +static_assert( !std::is_constructible_v<std::move_only_function<void() const>, + std::move_only_function<void()>> ); + +using FuncType = int(int); + +// Top level const qualifiers are ignored in function types, and decay +// is performed. +static_assert( std::is_same_v<std::move_only_function<void(int const)>, + std::move_only_function<void(int)>> ); +static_assert( std::is_same_v<std::move_only_function<void(int[2])>, + std::move_only_function<void(int*)>>); +static_assert( std::is_same_v<std::move_only_function<void(int[])>, + std::move_only_function<void(int*)>>); +static_assert( std::is_same_v<std::move_only_function<void(int const[5])>, + std::move_only_function<void(int const*)>>); +static_assert( std::is_same_v<std::move_only_function<void(FuncType)>, + std::move_only_function<void(FuncType*)>>); + +// Non-trivial args, guarantess that type is not passed by copy +struct CountedArg +{ + CountedArg() = default; + CountedArg(const CountedArg& f) noexcept : counter(f.counter) { ++counter; } + CountedArg& operator=(CountedArg&&) = delete; + + int counter = 0; +}; +CountedArg const c; + +// When move_only_functions is constructed from other move_only_function, +// the compiler can avoid double indirection per C++26 [func.wrap.general] p2. + +void +test01() +{ + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::move_only_function<int(CountedArg) const noexcept> m1(f); + VERIFY( m1(c) == 1 ); + + std::move_only_function<int(CountedArg) const> m2(std::move(m1)); + VERIFY( m2(c) == 1 ); + + std::move_only_function<int(CountedArg)> m3(std::move(m2)); + VERIFY( m3(c) == 1 ); + + // Invokers internally uses Counted&& for non-trivial types, + // sinature remain compatible. + std::move_only_function<int(CountedArg&&)> m4(std::move(m3)); + VERIFY( m4({}) == 0 ); + + std::move_only_function<int(CountedArg&&)&&> m5(std::move(m4)); + VERIFY( std::move(m5)({}) == 0 ); + + m4 = f; + std::move_only_function<int(CountedArg&&)&> m7(std::move(m4)); + VERIFY( m7({}) == 0 ); + + m4 = f; + std::move_only_function<int(CountedArg&&)&> m8(std::move(m4)); + VERIFY( m8({}) == 0 ); + + // Incompatible signatures + m1 = f; + std::move_only_function<long(CountedArg) const noexcept> m9(std::move(m1)); + VERIFY( m9(c) == 2 ); +} + +void +test02() +{ + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::move_only_function<int(CountedArg) const noexcept> m1(f); + VERIFY( m1(c) == 1 ); + + std::move_only_function<int(CountedArg) const> m2; + m2 = std::move(m1); + VERIFY( m2(c) == 1 ); + + std::move_only_function<int(CountedArg)> m3; + m3 = std::move(m2); + VERIFY( m3(c) == 1 ); + + // Invokers internally uses Counted&& for non-trivial types, + // sinature remain compatible. + std::move_only_function<int(CountedArg&&)> m4; + m4 = std::move(m3); + VERIFY( m4({}) == 0 ); + + std::move_only_function<int(CountedArg&&)&&> m5; + m5 = std::move(m4); + VERIFY( std::move(m5)({}) == 0 ); + + m4 = f; + std::move_only_function<int(CountedArg&&)&> m7; + m7 = std::move(m4); + VERIFY( m7({}) == 0 ); + + m4 = f; + std::move_only_function<int(CountedArg&&)&> m8; + m8 = std::move(m4); + VERIFY( m8({}) == 0 ); + + m1 = f; + std::move_only_function<long(CountedArg) const noexcept> m9; + m9 = std::move(m1); + VERIFY( m9(c) == 2 ); +} + +void +test03() +{ + std::move_only_function<int(long) const noexcept> e; + VERIFY( e == nullptr ); + + std::move_only_function<int(long) const> e2(std::move(e)); + VERIFY( e2 == nullptr ); + e2 = std::move(e); + VERIFY( e2 == nullptr ); + + std::move_only_function<bool(int) const> e3(std::move(e)); + VERIFY( e3 == nullptr ); + e3 = std::move(e); + VERIFY( e3 == nullptr ); +} + +void +test04() +{ + struct F + { + int operator()(CountedArg const& arg) noexcept + { return arg.counter; } + + int operator()(CountedArg const& arg) const noexcept + { return arg.counter + 1000; } + }; + + F f; + std::move_only_function<int(CountedArg) const> m1(f); + VERIFY( m1(c) == 1001 ); + + // Call const overload as std::move_only_function<int(CountedArg) const> + // inside std::move_only_function<int(CountedArg)> would do. + std::move_only_function<int(CountedArg)> m2(std::move(m1)); + VERIFY( m2(c) == 1001 ); + + std::move_only_function<int(CountedArg)> m3(f); + VERIFY( m3(c) == 1 ); +} + +void +test05() +{ + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::move_only_function<int(CountedArg)> w1(f); + // move_only_function stores move_only_function due incompatibile signatures + std::move_only_function<int(CountedArg const&)> w2(std::move(w1)); + // copy is made when passing to int(CountedArg) + VERIFY( w2(c) == 1 ); + // wrapped 3 times + w1 = std::move(w2); + VERIFY( w1(c) == 2 ); + // wrapped 4 times + w2 = std::move(w1); + VERIFY( w2(c) == 2 ); + // wrapped 5 times + w1 = std::move(w2); + VERIFY( w1(c) == 3 ); +} + +void +test06() +{ + // No special interoperability with std::function + auto f = [](CountedArg const& arg) noexcept { return arg.counter; }; + std::function<int(CountedArg)> f1(f); + std::move_only_function<int(CountedArg) const> m1(std::move(f1)); + VERIFY( m1(c) == 2 ); +} + +void +test07() +{ + // Scalar types and small trivially move constructible types are passed + // by value to invoker. So int&& signature is not compatible for such types. + auto fi = [](CountedArg const& arg, int) noexcept { return arg.counter; }; + std::move_only_function<int(CountedArg, int) const noexcept> mi1(fi); + VERIFY( mi1(c, 0) == 1 ); + std::move_only_function<int(CountedArg, int&&) const noexcept> mi2(std::move(mi1)); + VERIFY( mi2(c, 0) == 2 ); + + auto fs = [](CountedArg const& arg, std::string_view) noexcept { return arg.counter; }; + std::move_only_function<int(CountedArg, std::string_view) const noexcept> ms1(fs); + VERIFY( ms1(c, "") == 1 ); + std::move_only_function<int(CountedArg, std::string_view&&) const noexcept> ms2(std::move(ms1)); + VERIFY( ms2(c, "") == 2 ); +} + +int main() +{ + test01(); + test02(); + test03(); + test04(); + test05(); + test06(); + test07(); +} diff --git a/libstdc++-v3/testsuite/20_util/move_only_function/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/move_only_function/incomplete_neg.cc new file mode 100644 index 0000000..d025c47 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/move_only_function/incomplete_neg.cc @@ -0,0 +1,18 @@ +// { dg-do compile { target c++23 } } + +#include <functional> + +struct IncompleteClass; + +using T1 = std::move_only_function<int(IncompleteClass)>::result_type; // { dg-error "here" } +using T2 = std::move_only_function<int(int, IncompleteClass)>::result_type; // { dg-error "here" } + +enum Enum { + x = [] { + // Enum enumeration is incomplete here + using T3 = std::move_only_function<int(Enum)>::result_type; // { dg-error "here" } + return T3(1); + }() +}; + +// { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/20_util/move_only_function/move.cc b/libstdc++-v3/testsuite/20_util/move_only_function/move.cc index 51e31a6..6da02c9 100644 --- a/libstdc++-v3/testsuite/20_util/move_only_function/move.cc +++ b/libstdc++-v3/testsuite/20_util/move_only_function/move.cc @@ -32,6 +32,12 @@ test01() VERIFY( m1().copy == 1 ); VERIFY( m1().move == 0 ); + // Standard specifies move assigment as copy and swap + m1 = std::move(m1); + VERIFY( m1 != nullptr ); + VERIFY( m1().copy == 1 ); + VERIFY( m1().move == 0 ); + // This will move construct a new target object and destroy the old one: auto m2 = std::move(m1); VERIFY( m1 == nullptr && m2 != nullptr ); @@ -80,6 +86,11 @@ test02() VERIFY( m1().copy == 1 ); VERIFY( m1().move == 0 ); + m1 = std::move(m1); + VERIFY( m1 != nullptr ); + VERIFY( m1().copy == 1 ); + VERIFY( m1().move == 0 ); + // The target object is on the heap so this just moves a pointer: auto m2 = std::move(m1); VERIFY( m1 == nullptr && m2 != nullptr ); diff --git a/libstdc++-v3/testsuite/20_util/optional/cons/value_neg.cc b/libstdc++-v3/testsuite/20_util/optional/cons/value_neg.cc index d3abd03..f6b1886 100644 --- a/libstdc++-v3/testsuite/20_util/optional/cons/value_neg.cc +++ b/libstdc++-v3/testsuite/20_util/optional/cons/value_neg.cc @@ -37,3 +37,4 @@ int main() } } // { dg-prune-output "no type .*enable_if" } +// { dg-prune-output "no matching function for call to 'main..::U::U..'" } diff --git a/libstdc++-v3/testsuite/20_util/optional/make_optional-2.cc b/libstdc++-v3/testsuite/20_util/optional/make_optional-2.cc index f383b51..0aa2ac5e 100644 --- a/libstdc++-v3/testsuite/20_util/optional/make_optional-2.cc +++ b/libstdc++-v3/testsuite/20_util/optional/make_optional-2.cc @@ -70,7 +70,9 @@ static_assert( can_make_optional2<int, int&>::value ); static_assert( noexcept(std::make_optional(i)) ); static_assert( ! can_make_optional2<void, void>::value ); static_assert( can_make_optional2<Cont, Cont>::value ); +#if __cplusplus <= 202302 static_assert( noexcept(std::make_optional<Cont>({})) ); +#endif static_assert( can_make_optional2<Cont, const Cont&>::value ); static_assert( ! noexcept(std::make_optional(c)) ); static_assert( can_make_optional2<Cont, int>::value ); diff --git a/libstdc++-v3/testsuite/20_util/optional/make_optional_neg.cc b/libstdc++-v3/testsuite/20_util/optional/make_optional_neg.cc new file mode 100644 index 0000000..0949947 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/make_optional_neg.cc @@ -0,0 +1,20 @@ +// { dg-do compile { target c++17 } } + +#include <initializer_list> +#include <optional> + +struct S { int x; int* p; }; +int v; + +auto os1 = std::make_optional<S>({1, &v}); // { dg-error "no matching function for" "" { target c++26 } } + +struct Cont +{ + Cont(); + Cont(std::initializer_list<int>, int); +}; + +auto oc1 = std::make_optional<Cont>({}); // { dg-error "no matching function for" "" { target c++26 } } + +// { dg-prune-output "no type named 'type' in 'struct std::enable_if" } +// { dg-prune-output "type/value mismatch at argument 1 in template parameter list" } diff --git a/libstdc++-v3/testsuite/20_util/optional/monadic/or_else_neg.cc b/libstdc++-v3/testsuite/20_util/optional/monadic/or_else_neg.cc index f5028c1..12a67bb 100644 --- a/libstdc++-v3/testsuite/20_util/optional/monadic/or_else_neg.cc +++ b/libstdc++-v3/testsuite/20_util/optional/monadic/or_else_neg.cc @@ -26,4 +26,5 @@ test02() std::optional<move_only> mo; mo.or_else([]{ return std::optional<move_only>{}; }); // { dg-error "no matching function" } + // { dg-error "use of deleted function" "" { target *-*-* } 0 } } diff --git a/libstdc++-v3/testsuite/20_util/optional/monadic/ref_neg.cc b/libstdc++-v3/testsuite/20_util/optional/monadic/ref_neg.cc new file mode 100644 index 0000000..ed1b42b --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/monadic/ref_neg.cc @@ -0,0 +1,20 @@ +// { dg-do compile { target c++23 } } + +#include <optional> + +struct S { int x; int y; }; + +void test() +{ + std::optional<S> o; + const std::optional<S>& co = o; + + o.transform(&S::x); // { dg-error "from here" "optional<int&>" { target c++23_down } } + co.transform(&S::x); // { dg-error "from here" "optional<const int&>" { target c++23_down } } + std::move(o).transform(&S::x); // { dg-error "from here" "optional<int&&>" } + std::move(co).transform(&S::x); // { dg-error "from here" "optional<const int&&>" } +} + +// { dg-prune-output "in a union may not have reference type" } +// { dg-prune-output "static assertion failed" } +// { dg-prune-output "forming pointer to reference type" } diff --git a/libstdc++-v3/testsuite/20_util/optional/range.cc b/libstdc++-v3/testsuite/20_util/optional/range.cc new file mode 100644 index 0000000..4238c9c --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/range.cc @@ -0,0 +1,350 @@ +// { dg-do compile { target c++26 } } + +#include <concepts> +#include <format> +#include <iterator> +#include <optional> +#include <ranges> +#include <string_view> +#include <vector> + +#include <testsuite_hooks.h> + +struct NonMovable +{ + constexpr NonMovable() {} + constexpr NonMovable(int) {} + + NonMovable(NonMovable&&) = delete; + NonMovable& operator=(NonMovable&&) = delete; + + friend bool operator==(NonMovable const&, NonMovable const&) = default; +}; + +struct NonAssignable +{ + NonAssignable() = default; + NonAssignable(NonAssignable&&) = default; + NonAssignable& operator=(NonAssignable&&) = delete; + + friend bool operator==(NonAssignable const&, NonAssignable const&) = default; +}; + +template<typename T> +constexpr +void +test_range_concepts() +{ + using O = std::optional<T>; + static_assert(std::ranges::contiguous_range<O>); + static_assert(std::ranges::sized_range<O>); + static_assert(std::ranges::common_range<O>); + + // an optional<T&> is borrowed range + constexpr bool is_ref_opt = std::is_reference_v<T>; + static_assert(std::ranges::borrowed_range<O> == is_ref_opt); + + // for any T (including const U) such that optional<T> is not assignable, + // it does not satisfy ranges::view + constexpr bool is_opt_view = std::is_reference_v<T> || std::movable<T>; + static_assert(std::ranges::view<O> == is_opt_view); + static_assert(std::ranges::viewable_range<O> == is_opt_view); + +} + +template<typename O> +constexpr +void +test_iterator_concepts() +{ + using T = typename O::value_type; + using iterator = typename O::iterator; + static_assert(std::contiguous_iterator<iterator>); + static_assert(std::is_same_v<typename std::iterator_traits<iterator>::value_type, std::remove_cv_t<T>>); + static_assert(std::is_same_v<std::iter_value_t<iterator>, std::remove_cv_t<T>>); + static_assert(std::is_same_v<typename std::iterator_traits<iterator>::reference, T&>); + static_assert(std::is_same_v<std::iter_reference_t<iterator>, T&>); +} + +template<typename O> +constexpr +void +test_const_iterator_concepts() +{ + using T = typename O::value_type; + using const_iterator = typename O::const_iterator; + static_assert(std::contiguous_iterator<const_iterator>); + static_assert(std::is_same_v<typename std::iterator_traits<const_iterator>::value_type, std::remove_cv_t<T>>); + static_assert(std::is_same_v<std::iter_value_t<const_iterator>, std::remove_cv_t<T>>); + static_assert(std::is_same_v<typename std::iterator_traits<const_iterator>::reference, const T&>); + static_assert(std::is_same_v<std::iter_reference_t<const_iterator>, const T&>); +} + +template<typename T> +constexpr +void +test_empty() +{ + using O = std::optional<T>; + O empty; + VERIFY(!empty); + VERIFY(empty.begin() == empty.end()); + VERIFY(std::as_const(empty).begin() == std::as_const(empty).end()); + VERIFY(std::ranges::empty(empty)); + VERIFY(std::ranges::empty(std::as_const(empty))); + VERIFY(std::ranges::empty(empty | std::views::as_const)); + VERIFY(std::ranges::size(empty) == 0); + VERIFY(std::ranges::size(std::as_const(empty)) == 0); + + size_t count = 0; + for (const auto& x : empty) + ++count; + VERIFY(count == 0); +} + +template<typename T> +constexpr +void +test_non_empty(const T& value) +{ + using O = std::optional<T>; + using V = typename O::value_type; + O non_empty(std::in_place, value); + VERIFY(non_empty); + if constexpr (!std::is_array_v<V>) + VERIFY(*non_empty == value); + + VERIFY(non_empty.begin() != non_empty.end()); + VERIFY(non_empty.begin() < non_empty.end()); + VERIFY(std::as_const(non_empty).begin() != std::as_const(non_empty).end()); + VERIFY(std::as_const(non_empty).begin() < std::as_const(non_empty).end()); + VERIFY(!std::ranges::empty(non_empty)); + VERIFY(!std::ranges::empty(std::as_const(non_empty))); + VERIFY(!std::ranges::empty(non_empty | std::views::as_const)); + VERIFY(std::ranges::size(non_empty) == 1); + VERIFY(std::ranges::size(std::as_const(non_empty)) == 1); + + size_t count = 0; + for (const auto& x : non_empty) + ++count; + VERIFY(count == 1); + + if constexpr (std::is_move_assignable_v<V>) { + for (auto& x : non_empty) + x = V{}; + VERIFY(non_empty); + VERIFY(*non_empty == V{}); + } +} + +template<typename T> +constexpr +void +test(const T& value) +{ + using O = std::optional<T>; + test_range_concepts<T>(); + test_iterator_concepts<O>(); + if constexpr (!std::is_reference_v<T>) + test_const_iterator_concepts<O>(); + test_empty<T>(); + test_non_empty<T>(value); + static_assert(!std::formattable<O, char>); + static_assert(!std::formattable<O, wchar_t>); + static_assert(std::format_kind<O> == std::range_format::disabled); +} + +constexpr +void +range_chain_example() // from P3168 +{ + std::vector<int> v{2, 3, 4, 5, 6, 7, 8, 9, 1}; + auto test = [](int i) -> std::optional<int> { + switch(i) { + case 1: + case 3: + case 7: + case 9: + return i * 2; + default: + return {}; + } + }; + + auto result = v + | std::views::transform(test) + | std::views::filter([](auto x) { return bool(x); }) + | std::views::transform([](auto x){ return *x; }) + | std::ranges::to<std::vector>(); + + bool ok = result == std::vector<int>{6, 14, 18, 2}; + VERIFY(ok); +} + +template<typename T> +constexpr void test_not_range() +{ + static_assert(!requires { typename std::optional<T>::iterator; }); + static_assert(!requires(std::optional<T> o) { o.begin(); }); + static_assert(!requires(std::optional<T> o) { o.end(); }); +}; + +template<typename T> +constexpr bool is_optional = false; + +template<typename T> +constexpr bool is_optional<std::optional<T>> = true; + +template<bool usesOptional, typename T, typename U = std::remove_cv_t<T>> +constexpr void test_as_const(std::type_identity_t<U> u) +{ + std::optional<T> o(std::in_place, std::forward<U>(u)); + auto cv = std::views::as_const(o); + static_assert(is_optional<decltype(cv)> == usesOptional); + static_assert(std::is_same_v<decltype(*cv.begin()), const std::remove_reference_t<T>&>); + VERIFY(!std::ranges::empty(cv)); + + std::optional<T> e; + auto cve = std::views::as_const(e); + static_assert(is_optional<decltype(cve)> == usesOptional); + static_assert(std::is_same_v<decltype(*cve.begin()), const std::remove_reference_t<T>&>); + VERIFY(std::ranges::empty(cve)); +} + +template<bool usesOptional, typename T, typename U = std::remove_cv_t<T>> +constexpr void +test_reverse(std::type_identity_t<U> u) +{ + std::optional<T> o(std::in_place, std::forward<U>(u)); + auto rv = std::views::reverse(o); + static_assert(is_optional<decltype(rv)> == usesOptional); + static_assert(std::is_same_v<decltype(*rv.begin()), T&>); + VERIFY(!std::ranges::empty(rv)); + + std::optional<T> e; + auto rve = std::views::reverse(e); + static_assert(is_optional<decltype(rve)> == usesOptional); + static_assert(std::is_same_v<decltype(*rve.begin()), T&>); + VERIFY(std::ranges::empty(rve)); +} + +template<bool usesOptional, typename T, typename U = std::remove_cv_t<T>> +constexpr void +test_take(std::type_identity_t<U> u) +{ + std::optional<T> o(std::in_place, std::forward<U>(u)); + auto tvp = std::views::take(o, 3); + static_assert(is_optional<decltype(tvp)> == usesOptional); + static_assert(std::is_same_v<decltype(*tvp.begin()), T&>); + VERIFY(!std::ranges::empty(tvp)); + + auto tvz = std::views::take(o, 0); + static_assert(is_optional<decltype(tvz)> == usesOptional); + static_assert(std::is_same_v<decltype(*tvz.begin()), T&>); + VERIFY(std::ranges::empty(tvz)); + + std::optional<T> e; + auto tvep = std::views::take(e, 5); + static_assert(is_optional<decltype(tvep)> == usesOptional); + static_assert(std::is_same_v<decltype(*tvep.begin()), T&>); + VERIFY(std::ranges::empty(tvep)); + + auto tvez = std::views::take(e, 0); + static_assert(is_optional<decltype(tvez)> == usesOptional); + static_assert(std::is_same_v<decltype(*tvez.begin()), T&>); + VERIFY(std::ranges::empty(tvez)); +} + +template<bool usesOptional, typename T, typename U = std::remove_cv_t<T>> +constexpr void +test_drop(std::type_identity_t<U> u) +{ + std::optional<T> o(std::in_place, std::forward<U>(u)); + auto dvp = std::views::drop(o, 3); + static_assert(is_optional<decltype(dvp)> == usesOptional); + static_assert(std::is_same_v<decltype(*dvp.begin()), T&>); + VERIFY(std::ranges::empty(dvp)); + + auto dvz = std::views::drop(o, 0); + static_assert(is_optional<decltype(dvz)> == usesOptional); + static_assert(std::is_same_v<decltype(*dvz.begin()), T&>); + VERIFY(!std::ranges::empty(dvz)); + + std::optional<T> e; + auto dvep = std::views::drop(e, 5); + static_assert(is_optional<decltype(dvep)> == usesOptional); + static_assert(std::is_same_v<decltype(*dvep.begin()), T&>); + VERIFY(std::ranges::empty(dvep)); + + auto dvez = std::views::drop(e, 0); + static_assert(is_optional<decltype(dvez)> == usesOptional); + static_assert(std::is_same_v<decltype(*dvez.begin()), T&>); + VERIFY(std::ranges::empty(dvez)); +} + +constexpr +bool +all_tests() +{ + test(42); + int i = 42; + int arr[10]{}; + NonMovable nm; + NonAssignable na; + test(&i); + test(std::string_view("test")); + test(std::vector<int>{1, 2, 3, 4}); + test(std::optional<int>(42)); + test<const int>(42); + + test<int&>(i); + test<const int&>(i); + test<int(&)[10]>(arr); + test<const int(&)[10]>(arr); + test<NonMovable&>(nm); + test<const NonMovable&>(nm); + test<NonAssignable&>(na); + test<const NonAssignable&>(na); + test_not_range<void(&)()>(); + test_not_range<void(&)(int)>(); + test_not_range<int(&)[]>(); + test_not_range<const int(&)[]>(); + + range_chain_example(); + + test_as_const<false, int>(i); + test_as_const<false, const int>(i); + test_as_const<true, int&>(i); + test_as_const<true, const int&>(i); + test_as_const<false, NonMovable, int>(10); + test_as_const<false, const NonMovable, int>(10); + test_as_const<true, NonMovable&>(nm); + test_as_const<true, const NonMovable&>(nm); + test_as_const<false, NonAssignable>({}); + test_as_const<false, const NonAssignable>({}); + test_as_const<true, NonAssignable&>(na); + test_as_const<true, const NonAssignable&>(na); + +#define TEST_ADAPTOR(name) \ + test_##name<true, int>(i); \ + test_##name<false, const int>(i); \ + test_##name<true, int&>(i); \ + test_##name<true, const int&>(i); \ + test_##name<false, NonMovable, int>(10); \ + test_##name<false, const NonMovable, int>(10); \ + test_##name<true, NonMovable&>(nm); \ + test_##name<true, const NonMovable&>(nm); \ + test_##name<false, NonAssignable>({}); \ + test_##name<false, const NonAssignable>({}); \ + test_##name<true, NonAssignable&>(na); \ + test_##name<true, const NonAssignable&>(na) + + TEST_ADAPTOR(reverse); + TEST_ADAPTOR(take); + TEST_ADAPTOR(drop); +#undef TEST_ADAPTOR + return true; +} + +static_assert(all_tests()); + diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/access.cc b/libstdc++-v3/testsuite/20_util/optional/ref/access.cc new file mode 100644 index 0000000..37c8ff3 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/ref/access.cc @@ -0,0 +1,119 @@ +// { dg-do run { target c++26 } } + +#include <optional> +#include <type_traits> +#include <testsuite_hooks.h> + +struct NonMovable +{ + constexpr NonMovable() {} + NonMovable(NonMovable&&) = delete; +}; + +template<typename T> +constexpr +void test_value(T& t) +{ + std::optional<T&> o1(t); + const std::optional<T&> co1(t); + + static_assert( std::is_same_v<decltype(o1.value()), T&> ); + VERIFY( &o1.value() == &t ); + + static_assert( std::is_same_v<decltype(co1.value()), T&> ); + VERIFY( &co1.value() == &t ); + + static_assert( std::is_same_v<decltype(std::move(o1).value()), T&> ); + VERIFY( &std::move(o1).value() == &t ); + + static_assert( std::is_same_v<decltype(std::move(co1).value()), T&> ); + VERIFY( &std::move(co1).value() == &t ); + + std::optional<const T&> o2(t); + static_assert( std::is_same_v<decltype(o2.value()), const T&> ); + VERIFY( &o2.value() == &t ); +} + +struct Tracker +{ + int copy = 0; + int move = 0; + + constexpr Tracker(int v) : copy(v), move(v) {} + + constexpr Tracker(Tracker const& o) + : copy(o.copy+1), move(o.move) + {} + + constexpr Tracker(Tracker&& o) + : copy(o.copy), move(o.move+1) + {} + + Tracker& operator=(Tracker) = delete; +}; + +constexpr +void test_value_or() +{ + Tracker t(100), u(200); + std::optional<Tracker&> o(t); + + Tracker r1 = o.value_or(u); + VERIFY( r1.copy == 101 ); + VERIFY( r1.move == 100 ); + Tracker r2 = o.value_or(std::move(u)); + VERIFY( r2.copy == 101 ); + VERIFY( r2.move == 100 ); + Tracker r3 = std::move(o).value_or(u); + VERIFY( r3.copy == 101 ); + VERIFY( r3.move == 100 ); + Tracker r4 = std::move(o).value_or(std::move(u)); + VERIFY( r4.copy == 101 ); + VERIFY( r4.move == 100 ); + + o.reset(); + Tracker r5 = o.value_or(u); + VERIFY( r5.copy == 201 ); + VERIFY( r5.move == 200 ); + Tracker r6 = o.value_or(std::move(u)); + VERIFY( r6.copy == 200 ); + VERIFY( r6.move == 201 ); + Tracker r7 = std::move(o).value_or(u); + VERIFY( r7.copy == 201 ); + VERIFY( r7.move == 200 ); + Tracker r8 = std::move(o).value_or(std::move(u)); + VERIFY( r8.copy == 200 ); + VERIFY( r8.move == 201 ); +} + +template<typename T> +concept has_value_or_for = requires(std::optional<T&> t, T& u) +{ t.value_or(u); }; + +static_assert( has_value_or_for<int> ); +static_assert( has_value_or_for<NonMovable> ); +static_assert( !has_value_or_for<int[2]> ); +static_assert( has_value_or_for<int(*)[2]> ); +static_assert( !has_value_or_for<int()> ); +static_assert( has_value_or_for<int(*)()> ); + +int i; +NonMovable nm; +int arr[2]; +int foo(); + +int main() +{ + auto test_all = [] { + test_value<int>(i); + test_value<NonMovable>(nm); + test_value<int[2]>(arr); + test_value<int()>(foo); + + test_value_or(); + return true; + }; + + test_all(); + static_assert(test_all()); +} diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/assign.cc b/libstdc++-v3/testsuite/20_util/optional/ref/assign.cc new file mode 100644 index 0000000..be8b1c8 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/ref/assign.cc @@ -0,0 +1,430 @@ +// { dg-do run { target c++26 } } + +#include <optional> +#include <type_traits> +#include <testsuite_hooks.h> + +struct NonTrivial +{ + constexpr NonTrivial() {} + constexpr NonTrivial(NonTrivial const&) {}; + constexpr ~NonTrivial() {}; +}; + +struct NonMovable +{ + NonMovable() = default; + NonMovable(NonMovable&&) = delete; +}; + +template<typename T> +struct Conv +{ + T t; + + constexpr operator T() const noexcept + { return t; } +}; + +struct Tracker +{ + struct Counter + { + int copy; + int move; + }; + + Counter ctor{0,0}; + Counter assign{0,0}; + + Tracker() = default; + + constexpr Tracker(Tracker const& o) + : ctor(o.ctor), assign(o.assign) + { + ctor.copy += 1; + } + + constexpr Tracker(Tracker&& o) + : ctor(o.ctor), assign(o.assign) + { + ctor.move += 1; + } + + constexpr Tracker& operator=(const Tracker& o) + { + assign.copy += 1; + return *this; + } + + constexpr Tracker& operator=(Tracker&& o) + { + assign.move += 1; + return *this; + } + +}; + +template<typename T> +void +test_trivial() +{ + static_assert(std::is_copy_assignable_v<std::optional<T&>>); + static_assert(std::is_move_assignable_v<std::optional<T&>>); +} + +constexpr void +test_trivial_all() +{ + test_trivial<int>(); + test_trivial<NonTrivial>(); + test_trivial<std::optional<int&>>(); +} + +constexpr void +test_copy() +{ + Tracker t, u; + std::optional<Tracker&> e; + std::optional<Tracker&> o1(t); + std::optional<Tracker&> o2(u); + + o2 = o1; + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &t ); + VERIFY( o2.has_value() ); + VERIFY( &o2.value() == &t ); + VERIFY( t.ctor.copy == 0 ); + VERIFY( t.ctor.move == 0 ); + VERIFY( t.assign.copy == 0 ); + VERIFY( t.assign.move == 0 ); + + o2 = e; + VERIFY( !o2.has_value() ); + + o2 = std::move(o1); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &t ); + VERIFY( o2.has_value() ); + VERIFY( &o2.value() == &t ); + VERIFY( t.ctor.copy == 0 ); + VERIFY( t.ctor.move == 0 ); + VERIFY( t.assign.copy == 0 ); + VERIFY( t.assign.move == 0 ); + + o2 = std::move(e); + VERIFY( !o2.has_value() ); +} + +template<typename T, typename U> +concept can_emplace = requires (T t, U&& u) +{ t.emplace(std::forward<U>(u)); }; + +constexpr void +test_from_value() +{ + NonTrivial v, u; + const NonTrivial& cv = v; + const std::optional<NonTrivial&> s(u); + std::optional<NonTrivial&> o1; + std::optional<const NonTrivial&> co1; + + o1 = s; + o1 = v; + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + + o1.reset(); + VERIFY( !o1.has_value() ); + + o1 = v; + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + const NonTrivial&> ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + NonTrivial> ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + const NonTrivial> ); + + o1 = s; + o1.emplace(v); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + + o1 = std::nullopt; + VERIFY( !o1.has_value() ); + + o1.emplace(v); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + + static_assert( !can_emplace<std::optional<NonTrivial&>, const NonTrivial&> ); + static_assert( !can_emplace<std::optional<NonTrivial&>, NonTrivial> ); + static_assert( !can_emplace<std::optional<NonTrivial&>, const NonTrivial> ); + + co1 = s; + co1 = v; + VERIFY( co1.has_value() ); + VERIFY( &co1.value() == &v ); + + co1 = std::nullopt; + co1 = cv; + VERIFY( co1.has_value() ); + VERIFY( &co1.value() == &v ); + // No binding to rvalue + static_assert( !std::is_assignable_v<std::optional<const NonTrivial&>, + NonTrivial> ); + static_assert( !std::is_assignable_v<std::optional<const NonTrivial&>, + const NonTrivial> ); + + co1 = std::nullopt; + co1.emplace(v); + VERIFY( co1.has_value() ); + VERIFY( &co1.value() == &v ); + + co1 = s; + co1.emplace(cv); + VERIFY( co1.has_value() ); + VERIFY( &co1.value() == &v ); + // No binding to rvalue + static_assert( !can_emplace<std::optional<const NonTrivial&>, const NonTrivial> ); + static_assert( !can_emplace<std::optional<const NonTrivial&>, NonTrivial> ); + + + // Conversion create a pr-value that would bind to temporary + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + Conv<NonTrivial>&> ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + const Conv<NonTrivial>&> ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + Conv<NonTrivial>> ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + const Conv<NonTrivial>> ); + + static_assert( !can_emplace<std::optional<NonTrivial&>, Conv<NonTrivial>&> ); + static_assert( !can_emplace<std::optional<NonTrivial&>, const Conv<NonTrivial>&> ); + static_assert( !can_emplace<std::optional<NonTrivial&>, Conv<NonTrivial>> ); + static_assert( !can_emplace<std::optional<NonTrivial&>, const Conv<NonTrivial>> ); + + Conv<NonTrivial&> rw(v); + const Conv<NonTrivial&> crw(v); + + o1 = std::nullopt; + o1 = rw; + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + o1 = s; + o1 = crw; + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + o1 = s; + o1 = std::move(rw); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + o1 = std::nullopt; + o1 = std::move(crw); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + + o1 = s; + o1.emplace(rw); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + o1 = std::nullopt; + o1.emplace(crw); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + o1 = std::nullopt; + o1.emplace(std::move(rw)); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + o1 = s; + o1.emplace(std::move(crw)); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); +} + +constexpr void +test_from_opt_value() +{ + NonTrivial u; + std::optional<NonTrivial> v(std::in_place); + const std::optional<NonTrivial>& cv = v; + + const std::optional<NonTrivial&> s(u); + std::optional<NonTrivial&> o1; + std::optional<const NonTrivial&> co1; + + o1 = s; + o1 = v; + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v.value() ); + o1 = std::nullopt; + o1 = v; + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v.value() ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + const std::optional<NonTrivial>&> ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + std::optional<NonTrivial>> ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + const std::optional<NonTrivial>> ); + + co1 = s; + co1 = v; + VERIFY( co1.has_value() ); + VERIFY( &co1.value() == &v.value() ); + co1 = std::nullopt; + co1 = cv; + VERIFY( co1.has_value() ); + VERIFY( &co1.value() == &v.value() ); + // No binding to rvalue + static_assert( !std::is_assignable_v<std::optional<const NonTrivial&>, + std::optional<NonTrivial>> ); + static_assert( !std::is_assignable_v<std::optional<const NonTrivial&>, + const std::optional<NonTrivial>> ); + + // Conversion create a pr-value that would bind to temporary + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + std::optional<Conv<NonTrivial>>&> ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + const std::optional<Conv<NonTrivial>>&> ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + std::optional<Conv<NonTrivial>>> ); + static_assert( !std::is_assignable_v<std::optional<NonTrivial&>, + const std::optional<Conv<NonTrivial>>> ); + + std::optional<Conv<NonTrivial&>> rw(*v); + std::optional<const Conv<NonTrivial&>> crw(*v); + + o1 = std::nullopt; + o1 = rw; + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v.value() ); + o1 = s; + o1 = crw; + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v.value() ); + o1 = s; + o1 = std::move(rw); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v.value() ); + o1 = std::nullopt; + o1 = std::move(crw); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v.value() ); +} + +constexpr void +test_to_opt_value() +{ + Tracker t; + std::optional<Tracker&> er; + std::optional<Tracker&> r(t); + const std::optional<Tracker&> cr(t); + + std::optional<Tracker> o1; + o1 = r; + VERIFY( o1.has_value() ); + VERIFY( o1->ctor.copy == 1 ); + VERIFY( o1->ctor.move == 0 ); + VERIFY( o1->assign.copy == 0 ); + VERIFY( o1->assign.move == 0 ); + + o1 = r; + VERIFY( o1.has_value() ); + VERIFY( o1->ctor.copy == 1 ); + VERIFY( o1->ctor.move == 0 ); + VERIFY( o1->assign.copy == 1 ); + VERIFY( o1->assign.move == 0 ); + + o1 = er; + VERIFY( !o1.has_value() ); + + o1 = cr; + VERIFY( o1.has_value() ); + VERIFY( o1->ctor.copy == 1 ); + VERIFY( o1->ctor.move == 0 ); + VERIFY( o1->assign.copy == 0 ); + VERIFY( o1->assign.move == 0 ); + + o1 = cr; + VERIFY( o1.has_value() ); + VERIFY( o1->ctor.copy == 1 ); + VERIFY( o1->ctor.move == 0 ); + VERIFY( o1->assign.copy == 1 ); + VERIFY( o1->assign.move == 0 ); + + o1 = std::move(er); + + o1 = std::move(r); + VERIFY( o1.has_value() ); + VERIFY( o1->ctor.copy == 1 ); + VERIFY( o1->ctor.move == 0 ); + VERIFY( o1->assign.copy == 0 ); + VERIFY( o1->assign.move == 0 ); + + o1 = std::move(cr); + VERIFY( o1.has_value() ); + VERIFY( o1->ctor.copy == 1 ); + VERIFY( o1->ctor.move == 0 ); + VERIFY( o1->assign.copy == 1 ); + VERIFY( o1->assign.move == 0 ); +} + +constexpr void +test_swap() +{ + NonMovable a, b; + std::optional<NonMovable&> oa(a), ob(b); + + oa.swap(ob); + static_assert(noexcept(oa.swap(ob))); + VERIFY( oa.has_value() ); + VERIFY( &oa.value() == &b ); + VERIFY( ob.has_value() ); + VERIFY( &ob.value() == &a ); + + swap(oa, ob); + static_assert(std::is_nothrow_swappable_v<std::optional<NonMovable&>>); + VERIFY( oa.has_value() ); + VERIFY( &oa.value() == &a ); + VERIFY( ob.has_value() ); + VERIFY( &ob.value() == &b ); + + ob.reset(); + oa.swap(ob); + VERIFY( !oa.has_value() ); + VERIFY( ob.has_value() ); + VERIFY( &ob.value() == &a ); + + ob.reset(); + std::swap(oa, ob); + VERIFY( !oa.has_value() ); + VERIFY( !ob.has_value() ); + + std::optional<const NonMovable&> ca(a), cb(b); + swap(ca, cb); + VERIFY( ca.has_value() ); + VERIFY( &ca.value() == &b ); + VERIFY( cb.has_value() ); + VERIFY( &cb.value() == &a ); + + static_assert(!std::is_swappable_with_v<std::optional<int>&, std::optional<int&>&>); +} + +int main() +{ + auto test_all = [] { + test_copy(); + test_from_value(); + test_from_opt_value(); + test_to_opt_value(); + test_swap(); + return true; + }; + + test_all(); + static_assert(test_all()); +} diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/cons.cc b/libstdc++-v3/testsuite/20_util/optional/ref/cons.cc new file mode 100644 index 0000000..b13e8b9 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/ref/cons.cc @@ -0,0 +1,356 @@ +// { dg-do run { target c++26 } } + +#include <optional> +#include <type_traits> +#include <testsuite_hooks.h> + +struct NonTrivial +{ + constexpr NonTrivial() {} + constexpr NonTrivial(NonTrivial const&) {}; + constexpr ~NonTrivial() {}; +}; + +struct NonMovable +{ + constexpr NonMovable() {} + NonMovable(NonMovable&&) = delete; +}; + +template<typename T> +struct Conv +{ + T t; + + constexpr operator T() const noexcept + { return t; } +}; + +struct Tracker +{ + int copy = 0; + int move = 0; + + Tracker() = default; + + constexpr Tracker(Tracker const& o) + : copy(o.copy+1), move(o.move) + {} + + constexpr Tracker(Tracker&& o) + : copy(o.copy), move(o.move+1) + {} + + Tracker& operator=(Tracker) = delete; +}; + +template<typename T> +void +test_trivial() +{ + static_assert(std::is_trivially_copyable_v<std::optional<T&>>); + static_assert(std::is_copy_constructible_v<std::optional<T&>>); + static_assert(std::is_move_constructible_v<std::optional<T&>>); + static_assert(std::is_destructible_v<std::optional<T&>>); +} + +constexpr void +test_trivial_all() +{ + test_trivial<int>(); + test_trivial<NonTrivial>(); + test_trivial<NonMovable>(); + test_trivial<std::optional<int&>>(); +} + +constexpr void +test_copy() +{ + Tracker t; + std::optional<Tracker&> o1(t); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &t ); + VERIFY( t.copy == 0 ); + VERIFY( t.move == 0 ); + + std::optional<Tracker&> o2(o1); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &t ); + VERIFY( o2.has_value() ); + VERIFY( &o2.value() == &t ); + VERIFY( t.copy == 0 ); + VERIFY( t.move == 0 ); + + std::optional<Tracker&> o3(std::move(o1)); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &t ); + VERIFY( o3.has_value() ); + VERIFY( &o3.value() == &t ); + VERIFY( t.copy == 0 ); + VERIFY( t.move == 0 ); + + std::optional<Tracker&> e; + VERIFY( !e.has_value() ); + + std::optional<Tracker&> o4(e); + VERIFY( !e.has_value() ); + VERIFY( !o4.has_value() ); + + std::optional<Tracker&> o5(std::move(e)); + VERIFY( !e.has_value() ); + VERIFY( !o5.has_value() ); +} + + +constexpr void +test_from_value() +{ + NonTrivial v; + const NonTrivial& cv = v; + + std::optional<NonTrivial&> o1(v); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + const NonTrivial&> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + NonTrivial> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + const NonTrivial> ); + + std::optional<NonTrivial&> o2(std::in_place, v); + VERIFY( o2.has_value() ); + VERIFY( &o2.value() == &v ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + std::in_place_t, const NonTrivial&> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + std::in_place_t, NonTrivial> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + std::in_place_t, const NonTrivial> ); + + std::optional<const NonTrivial&> co1(v); + VERIFY( co1.has_value() ); + VERIFY( &co1.value() == &v ); + std::optional<const NonTrivial&> co2(cv); + VERIFY( co2.has_value() ); + VERIFY( &co2.value() == &v ); + // No binding to rvalue + static_assert( !std::is_constructible_v<std::optional<const NonTrivial&>, + NonTrivial> ); + static_assert( !std::is_constructible_v<std::optional<const NonTrivial&>, + const NonTrivial> ); + + std::optional<const NonTrivial&> co3(std::in_place, v); + VERIFY( co3.has_value() ); + VERIFY( &co3.value() == &v ); + std::optional<const NonTrivial&> co4(std::in_place, cv); + VERIFY( co4.has_value() ); + VERIFY( &co4.value() == &v ); + // No binding to rvalue + static_assert( !std::is_constructible_v<std::optional<const NonTrivial&>, + std::in_place_t, NonTrivial> ); + static_assert( !std::is_constructible_v<std::optional<const NonTrivial&>, + std::in_place_t, const NonTrivial> ); + + // Conversion create a pr-value that would bind to temporary + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + Conv<NonTrivial>&> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + const Conv<NonTrivial>&> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + Conv<NonTrivial>> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + const Conv<NonTrivial>> ); + + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + std::in_place_t, Conv<NonTrivial>&> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + std::in_place_t, const Conv<NonTrivial>&> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + std::in_place_t, Conv<NonTrivial>> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + std::in_place_t, const Conv<NonTrivial>> ); + + Conv<NonTrivial&> rw(v); + const Conv<NonTrivial&> crw(v); + + std::optional<NonTrivial&> ro1(rw); + VERIFY( ro1.has_value() ); + VERIFY( &ro1.value() == &v ); + std::optional<NonTrivial&> ro2(crw); + VERIFY( ro2.has_value() ); + VERIFY( &ro2.value() == &v ); + std::optional<NonTrivial&> ro3(std::move(rw)); + VERIFY( ro3.has_value() ); + VERIFY( &ro3.value() == &v ); + std::optional<NonTrivial&> ro4(std::move(crw)); + VERIFY( ro4.has_value() ); + VERIFY( &ro4.value() == &v ); + + std::optional<NonTrivial&> ro5(std::in_place, rw); + VERIFY( ro5.has_value() ); + VERIFY( &ro5.value() == &v ); + std::optional<NonTrivial&> ro6(std::in_place, crw); + VERIFY( ro6.has_value() ); + VERIFY( &ro6.value() == &v ); + std::optional<NonTrivial&> ro7(std::in_place, std::move(rw)); + VERIFY( ro7.has_value() ); + VERIFY( &ro7.value() == &v ); + std::optional<NonTrivial&> ro8(std::in_place, std::move(crw)); + VERIFY( ro8.has_value() ); + VERIFY( &ro8.value() == &v ); +} + +constexpr void +test_from_opt_value() +{ + std::optional<NonTrivial> v(std::in_place); + const std::optional<NonTrivial>& cv = v; + + std::optional<NonTrivial&> o1(v); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &v.value() ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + const std::optional<NonTrivial>&> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + std::optional<NonTrivial>> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + const std::optional<NonTrivial>> ); + + std::optional<const NonTrivial&> co1(v); + VERIFY( co1.has_value() ); + VERIFY( &co1.value() == &v.value() ); + std::optional<const NonTrivial&> co2(cv); + VERIFY( co2.has_value() ); + VERIFY( &co2.value() == &v.value() ); + // No binding to rvalue + static_assert( !std::is_constructible_v<std::optional<const NonTrivial&>, + std::optional<NonTrivial>> ); + static_assert( !std::is_constructible_v<std::optional<const NonTrivial&>, + const std::optional<NonTrivial>> ); + + // Conversion create a pr-value that would bind to temporary + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + std::optional<Conv<NonTrivial>>&> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + const std::optional<Conv<NonTrivial>>&> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + std::optional<Conv<NonTrivial>>> ); + static_assert( !std::is_constructible_v<std::optional<NonTrivial&>, + const std::optional<Conv<NonTrivial>>> ); + + std::optional<Conv<NonTrivial&>> rw(*v); + std::optional<const Conv<NonTrivial&>> crw(*v); + + std::optional<NonTrivial&> ro1(rw); + VERIFY( ro1.has_value() ); + VERIFY( &ro1.value() == &v.value() ); + std::optional<NonTrivial&> ro2(crw); + VERIFY( ro2.has_value() ); + VERIFY( &ro2.value() == &v.value() ); + std::optional<NonTrivial&> ro3(std::move(rw)); + VERIFY( ro3.has_value() ); + VERIFY( &ro3.value() == &v.value() ); + std::optional<NonTrivial&> ro4(std::move(crw)); + VERIFY( ro4.has_value() ); + VERIFY( &ro4.value() == &v.value() ); +} + +constexpr void +test_to_opt_value() +{ + Tracker t; + std::optional<Tracker&> r(t); + const std::optional<Tracker&> cr(t); + + std::optional<Tracker> o1(r); + VERIFY( o1.has_value() ); + VERIFY( o1->copy == 1 ); + VERIFY( o1->move == 0 ); + + std::optional<Tracker> o2(cr); + VERIFY( o2.has_value() ); + VERIFY( o2->copy == 1 ); + VERIFY( o2->move == 0 ); + + std::optional<Tracker> o3(std::move(r)); + VERIFY( o3.has_value() ); + VERIFY( o3->copy == 1 ); + VERIFY( o3->move == 0 ); + + std::optional<Tracker> o4(std::move(cr)); + VERIFY( o4.has_value() ); + VERIFY( o4->copy == 1 ); + VERIFY( o4->move == 0 ); + + std::optional<Tracker&> er; + const std::optional<Tracker&> cer; + + std::optional<Tracker> e1(er); + VERIFY( !e1.has_value() ); + + std::optional<Tracker> e2(cer); + VERIFY( !e2.has_value() ); + + std::optional<Tracker> e3(std::move(er)); + VERIFY( !e3.has_value() ); + + std::optional<Tracker> e4(std::move(cer)); + VERIFY( !e4.has_value() ); +} + +constexpr void +test_opt_opt() +{ + std::optional<int> s(43); + + std::optional<std::optional<int>&> o1(s); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &s ); + + std::optional<std::optional<int>&> o2(std::in_place, s); + VERIFY( o2.has_value() ); + VERIFY( &o2.value() == &s ); + + std::optional<std::optional<int>> o3(o1); + VERIFY( o2.has_value() ); + VERIFY( o2.value().has_value() ); + VERIFY( o2.value() == 43 ); + + s.reset(); + std::optional<std::optional<int>&> o4(s); + VERIFY( o4.has_value() ); + VERIFY( &o4.value() == &s ); + + std::optional<std::optional<int>&> o5(std::in_place, s); + VERIFY( o5.has_value() ); + VERIFY( &o5.value() == &s ); + + std::optional<std::optional<int>> o6(o1); + VERIFY( o6.has_value() ); + VERIFY( !o6.value().has_value() ); + + std::optional<std::optional<int>> s2(std::in_place); + std::optional<std::optional<int>&> oo1(s2); + VERIFY( oo1.has_value() ); + VERIFY( &oo1.value() == &s2.value() ); + + s2.reset(); + std::optional<std::optional<int>&> oo2(s2); + VERIFY( !oo2.has_value() ); +} + +int main() +{ + auto test_all = [] { + test_copy(); + test_from_value(); + test_from_opt_value(); + test_to_opt_value(); + test_opt_opt(); + return true; + }; + + test_all(); + static_assert(test_all()); +} diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/internal_traits.cc b/libstdc++-v3/testsuite/20_util/optional/ref/internal_traits.cc new file mode 100644 index 0000000..ef50da7 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/ref/internal_traits.cc @@ -0,0 +1,11 @@ +// { dg-do compile { target c++26 } } + +#include <optional> +#include <variant> + +template<typename T> +constexpr bool _Never_valueless + = std::__detail::__variant::_Never_valueless_alt<T>::value; + +static_assert( _Never_valueless<std::optional<int&>> ); +static_assert( _Never_valueless<std::optional<const int&>> ); diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/1.cc b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/1.cc new file mode 100644 index 0000000..d9946d0 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/1.cc @@ -0,0 +1,74 @@ +// { dg-do run { target c++20 } } + +#include <optional> +#include <utility> +#include <testsuite_hooks.h> + +struct NonTrivial +{ + constexpr NonTrivial() {} + constexpr NonTrivial(NonTrivial const&) {}; + constexpr ~NonTrivial() {}; +}; + +template<typename T> +struct Conv +{ + T t; + + constexpr operator T() const noexcept + { return t; } +}; + +constexpr bool test() +{ + NonTrivial t; + const NonTrivial& ct = t; + +#if __cplusplus > 202302 + auto o1 = std::make_optional<NonTrivial&>(t); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() == &t ); + + auto o2 = std::make_optional<const NonTrivial&>(t); + VERIFY( o2.has_value() ); + VERIFY( &o2.value() == &t ); + + auto o3 = std::make_optional<const NonTrivial&>(ct); + VERIFY( o3.has_value() ); + VERIFY( &o3.value() == &t ); + + Conv<NonTrivial&> rw(t); + auto o4 = std::make_optional<NonTrivial&>(rw); + VERIFY( o4.has_value() ); + VERIFY( &o4.value() == &t ); + + auto o5 = std::make_optional<NonTrivial&>(std::as_const(rw)); + VERIFY( o5.has_value() ); + VERIFY( &o5.value() == &t ); + + auto o6 = std::make_optional<NonTrivial&>(Conv<NonTrivial&>(t)); + VERIFY( o6.has_value() ); + VERIFY( &o6.value() == &t ); +#else + auto o1 = std::make_optional<NonTrivial&>(t); + VERIFY( o1.has_value() ); + VERIFY( &o1.value() != &t ); + + auto o3 = std::make_optional<const NonTrivial&>(ct); + VERIFY( o3.has_value() ); + VERIFY( &o3.value() != &t ); + + auto o2 = std::make_optional<NonTrivial&&>(std::move(t)); + VERIFY( o2.has_value() ); + VERIFY( &o2.value() != &t ); +#endif + + return true; +} + +int main() +{ + test(); + static_assert(test()); +} diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_args_neg.cc b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_args_neg.cc new file mode 100644 index 0000000..6166c65 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_args_neg.cc @@ -0,0 +1,43 @@ +// { dg-do compile { target c++17 } } + +#include <optional> + +struct C { + C(); + C(int); +}; +C s(10); +const C cs(1); + +template<typename T> +using decay_pre26 = +#if __cplusplus > 202302 + T; +#else + std::decay_t<T>; +#endif + +auto z1 = std::make_optional<C&>(); // { dg-error "no matching function for call" } +auto z2 = std::make_optional<const C&>(); // { dg-error "no matching function for call" } +auto z3 = std::make_optional<C&&>(); // { dg-error "no matching function for call" } +auto z4 = std::make_optional<const C&&>(); // { dg-error "no matching function for call" } + +auto o1 = std::make_optional<C&>(10); // { dg-error "no matching function for call" } +auto o2 = std::make_optional<const C&>(10); // { dg-error "from here" } +auto o3 = std::make_optional<C&&>(10); // { dg-error "from here" } +auto o4 = std::make_optional<const C&&>(10); // { dg-error "from here" } + +auto t1 = std::make_optional<C&>(10, 20); // { dg-error "no matching function for call" } +auto t2 = std::make_optional<const C&>(10, 20); // { dg-error "no matching function for call" } +auto t3 = std::make_optional<C&&>(10, 20); // { dg-error "no matching function for call" } +auto t3 = std::make_optional<const C&&>(10, 20); // { dg-error "no matching function for call" } + +// { dg-prune-output "no type named 'type' in 'struct std::enable_if" } +// { dg-prune-output "type/value mismatch at argument 1 in template parameter list" } +// { dg-prune-output "in a union may not have reference type" } +// { dg-prune-output "static assertion failed" } +// { dg-prune-output "forming pointer to reference type" } +// { dg-prune-output "cannot bind .* reference of type" } +// { dg-prune-output "binding reference of type" } +// { dg-prune-output "no matching function for call to 'std::optional" } + diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_lvalue_neg.cc b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_lvalue_neg.cc new file mode 100644 index 0000000..aed6791 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_lvalue_neg.cc @@ -0,0 +1,40 @@ +// { dg-do compile { target c++17 } } + +#include <optional> +#include <type_traits> + +struct C { + C(); + C(int); +}; +C s(10); +const C cs(1); + +template<typename T> +using decay_pre26 = +#if __cplusplus > 202302 + T; +#else + std::decay_t<T>; +#endif + +auto lr1 = std::make_optional<C&>(s); // changed meaning +static_assert( std::is_same_v< decltype(lr1), std::optional<decay_pre26<C&>>> ); +auto lr2 = std::make_optional<const C&>(s); // { dg-error "here" "" { target c++23_down } } +auto lr3 = std::make_optional<C&&>(s); // { dg-error "no matching function for call" } +auto lr4 = std::make_optional<const C&&>(s); // { dg-error "no matching function for call" } + +auto clr1 = std::make_optional<C&>(cs); // { dg-error "no matching function for call" } +auto clr2 = std::make_optional<const C&>(cs); // changed meaning +static_assert( std::is_same_v< decltype(clr2), std::optional<decay_pre26<const C&>>> ); +auto clr3 = std::make_optional<C&&>(cs); // { dg-error "no matching function for call" } +auto clr3 = std::make_optional<const C&&>(cs); // { dg-error "no matching function for call" } + +// { dg-prune-output "no type named 'type' in 'struct std::enable_if" } +// { dg-prune-output "type/value mismatch at argument 1 in template parameter list" } +// { dg-prune-output "in a union may not have reference type" } +// { dg-prune-output "static assertion failed" } +// { dg-prune-output "forming pointer to reference type" } +// { dg-prune-output "cannot bind .* reference of type" } +// { dg-prune-output "binding reference of type" } +// { dg-prune-output "no matching function for call to `std::optional" } diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_rvalue_neg.cc b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_rvalue_neg.cc new file mode 100644 index 0000000..22f669c --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/ref/make_optional/from_rvalue_neg.cc @@ -0,0 +1,38 @@ +// { dg-do compile { target c++17 } } + +#include <optional> +#include <type_traits> + +struct C { + C(); + C(int); +}; +C s(10); +const C cs(1); + +template<typename T> +using decay_pre26 = +#if __cplusplus > 202302 + T; +#else + std::decay_t<T>; +#endif + +auto p1 = std::make_optional<C&>(C(10)); // { dg-error "no matching function for call" } +auto p2 = std::make_optional<const C&>(C(10)); // { dg-error "from here" } +auto p3 = std::make_optional<C&&>(C(10)); // { dg-error "from here" "" { target c++26 } } +auto p4 = std::make_optional<const C&&>(C(10)); // { dg-error "from here" } + +auto b1 = std::make_optional<C&>({10}); // { dg-error "no matching function for call" } +auto b2 = std::make_optional<const C&>({10}); // { dg-error "no matching function for call" "" { target c++26 } } +auto b3 = std::make_optional<C&&>({10}); // { dg-error "no matching function for call" "" { target c++26 } } +auto b4 = std::make_optional<const C&&>({10}); // { dg-error "no matching function for call" "" { target c++26 } } + +// { dg-prune-output "no type named 'type' in 'struct std::enable_if" } +// { dg-prune-output "type/value mismatch at argument 1 in template parameter list" } +// { dg-prune-output "in a union may not have reference type" } +// { dg-prune-output "static assertion failed" } +// { dg-prune-output "forming pointer to reference type" } +// { dg-prune-output "cannot bind .* reference of type" } +// { dg-prune-output "binding reference of type" } +// { dg-prune-output "no matching function for call to 'std::optional" } diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/monadic.cc b/libstdc++-v3/testsuite/20_util/optional/ref/monadic.cc new file mode 100644 index 0000000..e8460c6 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/ref/monadic.cc @@ -0,0 +1,192 @@ +// { dg-do run { target c++26 } } + +#include <optional> +#include <type_traits> +#include <testsuite_hooks.h> +#include <utility> + +struct NonMovable +{ + constexpr NonMovable() {} + NonMovable(NonMovable&&) = delete; +}; + +struct Tracker +{ + int copy = 0; + int move = 0; + + Tracker() = default; + + constexpr Tracker(Tracker const& o) + : copy(o.copy+1), move(o.move) + {} + + constexpr Tracker(Tracker&& o) + : copy(o.copy), move(o.move+1) + {} + + Tracker& operator=(Tracker) = delete; + + void reset() { + copy = move = 0; + } +}; + +template<typename T> +auto identity_of = []<typename U>(U&& t) -> std::optional<T> +{ + static_assert( std::is_same_v<T, U&&> ); + VERIFY( t.copy == 0 ); + VERIFY( t.move == 0 ); + return std::optional<T>(t); +}; + +constexpr void +test_and_then() +{ + std::optional<Tracker> t(std::in_place); + std::optional<Tracker&> rt(t); + std::optional<const Tracker&> rct(t); + + auto r1 = t.and_then(identity_of<Tracker&>); + VERIFY( r1.has_value() ); + VERIFY( &r1.value() == &t.value() ); + + auto r2 = rt.and_then(identity_of<Tracker&>); + VERIFY( r2.has_value() ); + VERIFY( &r2.value() == &t.value() ); + + std::as_const(rt).and_then(identity_of<Tracker&>); + std::move(rt).and_then(identity_of<Tracker&>); + + auto r4 = rct.and_then(identity_of<const Tracker&>); + VERIFY( r4.has_value() ); + VERIFY( &r4.value() == &t.value() ); + + std::as_const(rct).and_then(identity_of<const Tracker&>); + std::move(rct).and_then(identity_of<const Tracker&>); + + auto r5 = rt.and_then([](Tracker&) { return std::optional<int>(42); }); + static_assert( std::is_same_v<decltype(r5), std::optional<int>> ); + VERIFY( r5.has_value() ); + VERIFY( r5.value() == 42 ); + + auto r6 = rct.and_then([](const Tracker&) { return std::optional<int>(); }); + static_assert( std::is_same_v<decltype(r6), std::optional<int>> ); + VERIFY( !r6.has_value() ); + + rct.reset(); + auto r7 = rct.and_then([](const Tracker&) { VERIFY(false); return std::optional<int>(42); }); + static_assert( std::is_same_v<decltype(r7), std::optional<int>> ); + VERIFY( !r7.has_value() ); + + rt.reset(); + auto r8 = rt.and_then([](Tracker&) { VERIFY(false); return std::optional<int>(); }); + static_assert( std::is_same_v<decltype(r8), std::optional<int>> ); + VERIFY( !r8.has_value() ); +} + +template<typename T> +constexpr void +test_or_else() +{ + T t, u; + + std::optional<T&> ot(t); + auto r1 = ot.or_else([&] { VERIFY(false); return std::optional<T&>(u); }); + VERIFY( &ot.value() == &t ); + VERIFY( r1.has_value() ); + VERIFY( &r1.value() == &t ); + auto r2 = std::move(ot).or_else([&] { VERIFY(false); return std::optional<T&>(); }); + VERIFY( &ot.value() == &t ); + VERIFY( r2.has_value() ); + VERIFY( &r2.value() == &t ); + + ot.reset(); + auto r3 = ot.or_else([&] { return std::optional<T&>(u); }); + VERIFY( !ot.has_value() ); + VERIFY( r3.has_value() ); + VERIFY( &r3.value() == &u ); + auto r4 = std::move(ot).or_else([] { return std::optional<T&>(); }); + VERIFY( !ot.has_value() ); + VERIFY( !r4.has_value() ); +} + +constexpr void +test_transform() +{ + std::optional<Tracker> t(std::in_place); + + auto r1 = t.transform(&Tracker::copy); + static_assert( std::is_same_v<decltype(r1), std::optional<int&>> ); + VERIFY( r1.has_value() ); + VERIFY( &r1.value() == &t->copy ); + auto r2 = std::as_const(t).transform(&Tracker::move); + static_assert( std::is_same_v<decltype(r2), std::optional<const int&>> ); + VERIFY( r2.has_value() ); + VERIFY( &r2.value() == &t->move ); + + std::optional<Tracker&> rt(t); + auto r3 = rt.transform(&Tracker::copy); + static_assert( std::is_same_v<decltype(r3), std::optional<int&>> ); + VERIFY( r3.has_value() ); + VERIFY( &r3.value() == &t->copy ); + auto r4 = std::as_const(rt).transform(&Tracker::copy); + static_assert( std::is_same_v<decltype(r4), std::optional<int&>> ); + VERIFY( r4.has_value() ); + VERIFY( &r4.value() == &t->copy ); + auto r5 = std::move(rt).transform(&Tracker::copy); + static_assert( std::is_same_v<decltype(r5), std::optional<int&>> ); + VERIFY( r5.has_value() ); + VERIFY( &r5.value() == &t->copy ); + + auto r6 = rt.transform([] (Tracker& t) { return 10; }); + static_assert( std::is_same_v<decltype(r6), std::optional<int>> ); + VERIFY( r6.has_value() ); + VERIFY( &r6.value() != &t->copy ); + VERIFY( r6.value() == 10 ); + + auto r7 = rt.transform([] (Tracker& t) { return NonMovable(); }); + static_assert( std::is_same_v<decltype(r7), std::optional<NonMovable>> ); + VERIFY( r7.has_value() ); + + rt.reset(); + auto r8 = rt.transform([] (Tracker& t) { VERIFY(false); return 42; }); + static_assert( std::is_same_v<decltype(r8), std::optional<int>> ); + VERIFY( !r8.has_value() ); + + std::optional<const Tracker&> crt(t); + auto r9 = crt.transform(&Tracker::copy); + static_assert( std::is_same_v<decltype(r9), std::optional<const int&>> ); + VERIFY( r9.has_value() ); + VERIFY( &r9.value() == &t->copy ); + auto r10 = std::as_const(crt).transform(&Tracker::copy); + static_assert( std::is_same_v<decltype(r10), std::optional<const int&>> ); + VERIFY( r10.has_value() ); + VERIFY( &r10.value() == &t->copy ); + auto r11 = std::move(crt).transform(&Tracker::copy); + static_assert( std::is_same_v<decltype(r11), std::optional<const int&>> ); + VERIFY( r11.has_value() ); + VERIFY( &r11.value() == &t->copy ); + + crt.reset(); + auto r12 = rt.transform([] (Tracker& t) { VERIFY(false); return 42; }); + static_assert( std::is_same_v<decltype(r12), std::optional<int>> ); + VERIFY( !r12.has_value() ); +} + +int main() +{ + auto test_all = [] { + test_and_then(); + test_transform(); + test_or_else<Tracker>(); + test_or_else<const Tracker>(); + test_or_else<NonMovable>(); + return true; + }; + + test_all(); + static_assert(test_all()); +} diff --git a/libstdc++-v3/testsuite/20_util/optional/ref/relops.cc b/libstdc++-v3/testsuite/20_util/optional/ref/relops.cc new file mode 100644 index 0000000..e0b30c8 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/ref/relops.cc @@ -0,0 +1,229 @@ +// { dg-do run { target c++26 } } + +#include <optional> +#include <functional> +#include <testsuite_hooks.h> +#include <type_traits> + +template<typename T, typename H = std::hash<T>> +constexpr bool has_disabled_hash + = !std::is_default_constructible_v<H> + && !std::is_copy_constructible_v<H> + && !std::is_move_constructible_v<H> + && !std::is_copy_assignable_v<H> + && !std::is_move_assignable_v<H>; + +static_assert(has_disabled_hash<std::optional<int&>>); +static_assert(has_disabled_hash<std::optional<const int&>>); + +template<typename T, typename V> +constexpr void +test_compare_val(V& l, V& h) +{ + std::optional<T> t; + + VERIFY( !(t == l) ); + VERIFY( (t != l) ); + VERIFY( (t < l) ); + VERIFY( (t <= l) ); + VERIFY( !(t > l) ); + VERIFY( !(t >= l) ); + VERIFY( (t <=> l) < 0 ); + + VERIFY( !(l == t) ); + VERIFY( (l != t) ); + VERIFY( !(l < t) ); + VERIFY( !(l <= t) ); + VERIFY( (l > t) ); + VERIFY( (l >= t) ); + VERIFY( (l <=> t) > 0 ); + + t.emplace(l); + VERIFY( (t == l) ); + VERIFY( !(t != l) ); + VERIFY( !(t < l) ); + VERIFY( (t <= l) ); + VERIFY( !(t > l) ); + VERIFY( (t >= l) ); + VERIFY( (t <=> l) == 0 ); + + VERIFY( (l == t) ); + VERIFY( !(l != t) ); + VERIFY( !(l < t) ); + VERIFY( (l <= t) ); + VERIFY( !(l > t) ); + VERIFY( (l >= t) ); + VERIFY( (t <=> l) == 0 ); + + t.emplace(h); + VERIFY( !(t == l) ); + VERIFY( (t != l) ); + VERIFY( !(t < l) ); + VERIFY( !(t <= l) ); + VERIFY( (t > l) ); + VERIFY( (t >= l) ); + VERIFY( (t <=> l) > 0 ); + + VERIFY( !(l == t) ); + VERIFY( (l != t) ); + VERIFY( (l < t) ); + VERIFY( (l <= t) ); + VERIFY( !(l > t) ); + VERIFY( !(l >= t) ); + VERIFY( (l <=> t) < 0 ); +} + +template<typename T, typename U, typename V> +constexpr void +test_compare_opts(V& l, V& h) +{ + std::optional<T> t; + std::optional<U> u; + + VERIFY( (t == u) ); + VERIFY( !(t != u) ); + VERIFY( !(t < u) ); + VERIFY( (t <= u) ); + VERIFY( !(t > u) ); + VERIFY( (t >= u) ); + VERIFY( (t <=> u) == 0 ); + + t.emplace(l); + VERIFY( !(t == u) ); + VERIFY( (t != u) ); + VERIFY( !(t < u) ); + VERIFY( !(t <= u) ); + VERIFY( (t > u) ); + VERIFY( (t >= u) ); + VERIFY( (t <=> u) > 0 ); + + u.emplace(l); + VERIFY( (t == u) ); + VERIFY( !(t != u) ); + VERIFY( !(t < u) ); + VERIFY( (t <= u) ); + VERIFY( !(t > u) ); + VERIFY( (t <= u) ); + VERIFY( (t <=> u) == 0 ); + + u.emplace(h); + VERIFY( !(t == u) ); + VERIFY( (t != u) ); + VERIFY( (t < u) ); + VERIFY( (t <= u) ); + VERIFY( !(t > u) ); + VERIFY( !(t >= u) ); + VERIFY( (t <=> u) < 0 ); + + t.reset(); + u.emplace(l); + VERIFY( !(t == u) ); + VERIFY( (t != u) ); + VERIFY( (t < u) ); + VERIFY( (t <= u) ); + VERIFY( !(t > u) ); + VERIFY( !(t >= u) ); + VERIFY( (t <=> u) < 0 ); + + t.emplace(h); + VERIFY( !(t == u) ); + VERIFY( (t != u) ); + VERIFY( !(t < u) ); + VERIFY( !(t <= u) ); + VERIFY( (t > u) ); + VERIFY( (t >= u) ); + VERIFY( (t <=> u) > 0 ); +} + +template<typename V> +constexpr void +test_compare(V l, V h) +{ + test_compare_val<V&>(l, h); + test_compare_val<const V&>(l, h); + + test_compare_opts<V&, V&>(l, h); + test_compare_opts<V, V&>(l, h); + test_compare_opts<V&, V>(l, h); + + test_compare_opts<const V&, const V&>(l, h); + test_compare_opts<V, const V&>(l, h); + test_compare_opts<const V&, V>(l, h); + + test_compare_opts<V&, const V&>(l, h); + test_compare_opts<const V&, V&>(l, h); +} + +struct TreeWay +{ + int v; + friend auto operator<=>(TreeWay, TreeWay) = default; +}; + +struct Other +{ + int v; + + constexpr Other(int p) : v(p) {} + constexpr Other(TreeWay p) : v(p.v) {} + + friend bool operator==(Other, Other) = default; + friend auto operator<=>(Other, Other) = default; + + friend constexpr bool + operator==(const Other& lhs, const TreeWay& rhs) + { return lhs.v == rhs.v; } + + friend constexpr std::strong_ordering + operator<=>(const Other& lhs, const TreeWay& rhs) + { return lhs.v <=> rhs.v; } +}; + +constexpr void +test_heterogeneus_cmp() +{ + TreeWay l{10}; + Other h{20}; + + std::optional<TreeWay&> t; + std::optional<const Other&> u; + + VERIFY( (t == u) ); + VERIFY( !(t != u) ); + VERIFY( !(t < u) ); + VERIFY( (t <= u) ); + VERIFY( !(t > u) ); + VERIFY( (t >= u) ); + VERIFY( (t <=> u) == 0 ); + + t.emplace(l); + VERIFY( !(t == u) ); + VERIFY( (t != u) ); + VERIFY( !(t < u) ); + VERIFY( !(t <= u) ); + VERIFY( (t > u) ); + VERIFY( (t >= u) ); + VERIFY( (t <=> u) > 0 ); + + u.emplace(h); + VERIFY( !(t == u) ); + VERIFY( (t != u) ); + VERIFY( (t < u) ); + VERIFY( (t <= u) ); + VERIFY( !(t > u) ); + VERIFY( !(t >= u) ); + VERIFY( (t <=> u) < 0 ); +} + +int main() +{ + auto test_all = [] { + test_compare(2, 5); + test_compare(TreeWay{11}, TreeWay{12}); + test_heterogeneus_cmp(); + return true; + }; + + test_all(); + static_assert(test_all()); +} diff --git a/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc b/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc index 3e39325..ec47552 100644 --- a/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc +++ b/libstdc++-v3/testsuite/20_util/optional/relops/constrained.cc @@ -8,8 +8,8 @@ # error "Feature-test macro for constrained_equality has wrong value" #endif -template<typename T, typename U = T> -concept eq_comparable +template<typename T, typename U> +concept eq_comparable_impl = requires (const std::optional<T>& t, const std::optional<U>& u) { t == u; *t == u; @@ -17,7 +17,19 @@ concept eq_comparable }; template<typename T, typename U = T> -concept ne_comparable +concept eq_comparable = + eq_comparable_impl<T, U> +#if __cplusplus > 202302l + && eq_comparable_impl<T&, U&> + && eq_comparable_impl<T const&, U const&> + && eq_comparable_impl<T const&, U&> + && eq_comparable_impl<T, U const&> + && eq_comparable_impl<T&, U> +#endif +; + +template<typename T, typename U> +concept ne_comparable_impl = requires (const std::optional<T>& t, const std::optional<U>& u) { t != u; *t != u; @@ -25,7 +37,19 @@ concept ne_comparable }; template<typename T, typename U = T> -concept lt_comparable +concept ne_comparable = + ne_comparable_impl<T, U> +#if __cplusplus > 202302l + && ne_comparable_impl<T&, U&> + && ne_comparable_impl<T const&, U const&> + && ne_comparable_impl<T const&, U&> + && ne_comparable_impl<T, U const&> + && ne_comparable_impl<T&, U> +#endif +; + +template<typename T, typename U> +concept lt_comparable_impl = requires (const std::optional<T>& t, const std::optional<U>& u) { t < u; *t < u; @@ -33,7 +57,19 @@ concept lt_comparable }; template<typename T, typename U = T> -concept le_comparable +concept lt_comparable = + lt_comparable_impl<T, U> +#if __cplusplus > 202302l + && lt_comparable_impl<T&, U&> + && lt_comparable_impl<T const&, U const&> + && lt_comparable_impl<T const&, U&> + && lt_comparable_impl<T, U const&> + && lt_comparable_impl<T&, U> +#endif +; + +template<typename T, typename U> +concept le_comparable_impl = requires (const std::optional<T>& t, const std::optional<U>& u) { t <= u; *t <= u; @@ -41,7 +77,19 @@ concept le_comparable }; template<typename T, typename U = T> -concept gt_comparable +concept le_comparable = + le_comparable_impl<T, U> +#if __cplusplus > 202302l + && le_comparable_impl<T&, U&> + && le_comparable_impl<T const&, U const&> + && le_comparable_impl<T const&, U&> + && le_comparable_impl<T, U const&> + && le_comparable_impl<T&, U> +#endif +; + +template<typename T, typename U> +concept gt_comparable_impl = requires (const std::optional<T>& t, const std::optional<U>& u) { t > u; *t > u; @@ -49,13 +97,37 @@ concept gt_comparable }; template<typename T, typename U = T> -concept ge_comparable +concept gt_comparable = + gt_comparable_impl<T, U> +#if __cplusplus > 202302l + && gt_comparable_impl<T&, U&> + && gt_comparable_impl<T const&, U const&> + && gt_comparable_impl<T const&, U&> + && gt_comparable_impl<T, U const&> + && gt_comparable_impl<T&, U> +#endif +; + +template<typename T, typename U> +concept ge_comparable_impl = requires (const std::optional<T>& t, const std::optional<U>& u) { t >= u; *t >= u; t >= *u; }; +template<typename T, typename U = T> +concept ge_comparable = + ge_comparable_impl<T, U> +#if __cplusplus > 202302l + && ge_comparable_impl<T&, U&> + && ge_comparable_impl<T const&, U const&> + && ge_comparable_impl<T const&, U&> + && ge_comparable_impl<T, U const&> + && ge_comparable_impl<T&, U> +#endif +; + static_assert( eq_comparable<int> ); static_assert( ne_comparable<int> ); static_assert( lt_comparable<int> ); diff --git a/libstdc++-v3/testsuite/20_util/optional/relops/lwg4370.cc b/libstdc++-v3/testsuite/20_util/optional/relops/lwg4370.cc new file mode 100644 index 0000000..a292cbe --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/relops/lwg4370.cc @@ -0,0 +1,55 @@ +// { dg-do compile { target c++17 } } + +// LWG 4370. Comparison of optional<T> to T may be ill-formed + +#include <optional> +#include <testsuite_hooks.h> + +struct Bool +{ + Bool(bool); + operator bool() const; +}; + +template<typename T> void operator&&(Bool, T) = delete; +template<typename T> void operator&&(T, Bool) = delete; +template<typename T> void operator||(Bool, T) = delete; +template<typename T> void operator||(T, Bool) = delete; + +struct S +{ + Bool operator==(S) const; + Bool operator!=(S) const; + Bool operator<(S) const; + Bool operator>(S) const; + Bool operator<=(S) const; + Bool operator>=(S) const; +}; + +void +test_lwg4370() +{ + std::optional<S> o; + (void)(o == o); + (void)(o != o); + (void)(o < o); + (void)(o > o); + (void)(o <= o); + (void)(o >= o); + + S s; + (void)(o == s); + (void)(s == o); + (void)(o != s); + (void)(s != o); + (void)(o < s); + (void)(s < o); + (void)(o > s); + (void)(s > o); + (void)(o <= s); + (void)(s <= o); + (void)(o >= s); + (void)(s >= o); +} + + diff --git a/libstdc++-v3/testsuite/20_util/optional/requirements.cc b/libstdc++-v3/testsuite/20_util/optional/requirements.cc index 68e5905..9e8cf83 100644 --- a/libstdc++-v3/testsuite/20_util/optional/requirements.cc +++ b/libstdc++-v3/testsuite/20_util/optional/requirements.cc @@ -26,8 +26,10 @@ # error "Feature test macro for optional has wrong value in <optional>" #elif __cplusplus == 202002L && __cpp_lib_optional != 202106L # error "Feature test macro for optional has wrong value for C++20 in <optional>" -#elif __cplusplus > 202002L && __cpp_lib_optional != 202110L -# error "Feature test macro for optional has wrong value for C++23 in <version>" +#elif __cplusplus == 202302L && __cpp_lib_optional != 202110L +# error "Feature test macro for optional has wrong value for C++23 in <optional>" +#elif __cplusplus > 202302L && __cpp_lib_optional != 202506L +# error "Feature test macro for optional has wrong value for C++26 in <optional>" #endif #include <testsuite_hooks.h> diff --git a/libstdc++-v3/testsuite/20_util/optional/requirements_neg.cc b/libstdc++-v3/testsuite/20_util/optional/requirements_neg.cc index 688c305..142fbbf 100644 --- a/libstdc++-v3/testsuite/20_util/optional/requirements_neg.cc +++ b/libstdc++-v3/testsuite/20_util/optional/requirements_neg.cc @@ -2,17 +2,32 @@ #include <optional> +// C++ < 26: // T shall be a type other than cv in_place_t or cv nullopt_t // that meets the Cpp17Destructible requirements +// C++ >= 26: +// A type X is a valid contained type for optional if X is an lvalue reference +// type or a complete non-array object type, and remove_cvref_t<X> is a type +// other than in_place_t or nullopt_t. If a specialization of optional +// is instantiated with a type T that is not a valid contained type for +// optional, the program is ill-formed. If T is an object type, +// T shall meet the Cpp17Destructible requirements std::optional<std::nullopt_t> o1; // { dg-error "here" } std::optional<const std::nullopt_t> o2; // { dg-error "here" } std::optional<std::in_place_t> o3; // { dg-error "here" } std::optional<const std::in_place_t> o4; // { dg-error "here" } -std::optional<int&> o5; // { dg-error "here" } +std::optional<int&> o5; // { dg-error "here" "optional<T&> is a C++26 feature" { target c++23_down } } std::optional<int[1]> o6; // { dg-error "here" } std::optional<int[]> o7; // { dg-error "here" } std::optional<int()> o8; // { dg-error "here" } +std::optional<const int &> o9; // { dg-error "here" "optional<T&> is a C++26 feature" { target c++23_down } } +std::optional<std::in_place_t &> o10; // { dg-error "here" } +std::optional<const std::in_place_t &> o11; // { dg-error "here" } +std::optional<std::nullopt_t &> o12; // { dg-error "here" } +std::optional<const std::nullopt_t &> o13; // { dg-error "here" } +std::optional<int &&> o14; // { dg-error "here" } +std::optional<const int &&> o15; // { dg-error "here" } // { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/20_util/optional/version.cc b/libstdc++-v3/testsuite/20_util/optional/version.cc index 657a399..ae9339a 100644 --- a/libstdc++-v3/testsuite/20_util/optional/version.cc +++ b/libstdc++-v3/testsuite/20_util/optional/version.cc @@ -9,8 +9,10 @@ # error "Feature test macro for optional has wrong value for C++17 in <version>" #elif __cplusplus == 202002L && __cpp_lib_optional != 202106L # error "Feature test macro for optional has wrong value for C++20 in <version>" -#elif __cplusplus > 202002L && __cpp_lib_optional != 202110L +#elif __cplusplus == 202302L && __cpp_lib_optional != 202110L # error "Feature test macro for optional has wrong value for C++23 in <version>" +#elif __cplusplus > 202302L && __cpp_lib_optional != 202506L +# error "Feature test macro for optional has wrong value for C++26 in <version>" #endif #if __cplusplus >= 202302L @@ -21,8 +23,17 @@ #endif #endif +#if __cplusplus > 202302L +# ifndef __cpp_lib_optional_range_support +# error "Feature test macro for optional range support is missing in <version>" +# elif __cpp_lib_optional_range_support != 202406L +# error "Feature test macro for optional range support has wrong value for C++26 in <version>" +# endif +#endif + #undef __cpp_lib_optional #undef __cpp_lib_freestanding_optional +#undef __cpp_lib_optional_range_support #include <optional> #if __cplusplus >= 202302L @@ -32,3 +43,12 @@ # error "Feature test macro for freestanding std::optional has wrong value in <optional>" #endif #endif + +#if __cplusplus > 202302L +# ifndef __cpp_lib_optional_range_support +# error "Feature test macro for optional range support is missing in <optional>" +# endif +# if __cpp_lib_optional_range_support != 202406L +# error "Feature test macro for optional range support has wrong value for C++26 in <optional>" +# endif +#endif diff --git a/libstdc++-v3/testsuite/20_util/owner_equal/cmp.cc b/libstdc++-v3/testsuite/20_util/owner_equal/cmp.cc new file mode 100644 index 0000000..311ddf2 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/owner_equal/cmp.cc @@ -0,0 +1,105 @@ +// { dg-do run { target c++26 } } +// { dg-require-effective-target hosted } + +// N5008 20.3.2.6 Struct owner_equal [util.smartptr.owner.equal] + +#include <memory> +#include <algorithm> +#include <testsuite_hooks.h> + +struct A { }; + +struct B { A a[2]; }; + +int +test01() +{ + // test empty shared_ptr owners compare equivalent + std::owner_equal eq; + std::shared_ptr<A> p1; + std::shared_ptr<A> p2; + VERIFY( eq(p1, p2) && eq(p2, p1) ); + std::weak_ptr<A> p3; + VERIFY( eq(p1, p3) && eq(p3, p1) ); + VERIFY( eq(p1, p3) && eq(p3, p1) ); + return 0; +} + + +// Construction from pointer +int +test02() +{ + std::owner_equal eq; + + std::shared_ptr<A> empty; + + std::shared_ptr<A> a1(new A); + VERIFY( !eq(empty, a1) && !eq(a1, empty) ); + + std::shared_ptr<A> a2(new A); + VERIFY( !eq(a1, a2) && !eq(a2, a1) ); + + std::weak_ptr<A> w1(a1); + VERIFY( eq(a1, w1) && eq(w1, a1) ); + + std::weak_ptr<A> w2(a2); + VERIFY( !eq(w1, w2) && !eq(w2, w1) ); + + a1.reset(); + VERIFY( eq(empty, a1) && eq(a1, empty) ); + VERIFY( !eq(a1, w1) && !eq(w1, a1) ); + + a2.reset(); + VERIFY( eq(a2, a1) && eq(a1, a2) ); + + return 0; +} + +// aliasing +int +test03() +{ + std::owner_equal eq; + + std::shared_ptr<B> b(new B); + std::shared_ptr<A> a0(b, &b->a[0]); + std::shared_ptr<A> a1(b, &b->a[1]); + // values are different but owners are equivalent: + VERIFY( a0 < a1 && eq(a0, a1) && eq(b, a0) && eq(b, a1) ); + + std::weak_ptr<A> w0(a0); + std::weak_ptr<A> w1(a1); + VERIFY( eq(w0, w1) && eq(w1, w0) ); + VERIFY( eq(a0, w1) && eq(w1, a0) ); + VERIFY( eq(w0, a1) && eq(a1, w0) ); + + return 0; +} + +// as binary predicate +int +test04() +{ + std::owner_equal eq; + + std::shared_ptr<B> b(new B); + std::shared_ptr<A> a0(b, &b->a[0]); + std::shared_ptr<A> a1(b, &b->a[1]); + std::shared_ptr<A> c(new A); + std::weak_ptr<A> a[3]{a0, a1, c}; + std::weak_ptr<A>* p = std::unique(a, a+3, eq); + VERIFY( p == &a[2] ); + + return 0; +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); + return 0; +} diff --git a/libstdc++-v3/testsuite/20_util/owner_equal/noexcept.cc b/libstdc++-v3/testsuite/20_util/owner_equal/noexcept.cc new file mode 100644 index 0000000..fb479f6 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/owner_equal/noexcept.cc @@ -0,0 +1,30 @@ +// { dg-do compile { target c++26 } } +// { dg-require-effective-target hosted } + +// N5008 20.3.2.6 Struct owner_equal [util.smartptr.owner.equal] + +#include <memory> + +#ifndef __cpp_lib_smart_ptr_owner_equality +# error "Feature-test macro for smart ptr owner equality missing in <memory>" +#elif __cpp_lib_smart_ptr_owner_equality != 202306L +# error "Feature-test macro for smart ptr owner equality has wrong value in <memory>" +#endif + +const std::owner_equal eq; +const std::shared_ptr<int> si; +const std::weak_ptr<int> wi; +static_assert( noexcept(!eq(si, si)) ); +static_assert( noexcept(!eq(si, wi)) ); +static_assert( noexcept(!eq(wi, si)) ); +static_assert( noexcept(!eq(wi, wi)) ); +static_assert( noexcept(!eq(si, wi)) ); +static_assert( noexcept(!eq(wi, si)) ); +const std::shared_ptr<long> sl; +const std::weak_ptr<char> wc; +static_assert( noexcept(!eq(si, si)) ); +static_assert( noexcept(!eq(si, sl)) ); +static_assert( noexcept(!eq(sl, si)) ); +static_assert( noexcept(!eq(si, wc)) ); +static_assert( noexcept(!eq(wc, si)) ); +static_assert( noexcept(!eq(wc, wi)) ); diff --git a/libstdc++-v3/testsuite/20_util/owner_equal/version.cc b/libstdc++-v3/testsuite/20_util/owner_equal/version.cc new file mode 100644 index 0000000..db29154 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/owner_equal/version.cc @@ -0,0 +1,13 @@ +// { dg-do compile { target c++26 } } +// { dg-require-effective-target hosted } + +// N5008 17.3.2 Header <version> synopsis [version.syn] + +#include <version> + +#ifndef __cpp_lib_smart_ptr_owner_equality +# error "Feature-test macro for smart ptr owner equality missing in <version>" +#elif __cpp_lib_smart_ptr_owner_equality != 202306L +# error "Feature-test macro for smart ptr owner equality has wrong value in <version>" +#endif + diff --git a/libstdc++-v3/testsuite/20_util/owner_hash/cmp.cc b/libstdc++-v3/testsuite/20_util/owner_hash/cmp.cc new file mode 100644 index 0000000..c03a926 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/owner_hash/cmp.cc @@ -0,0 +1,87 @@ +// { dg-do run { target c++26 } } +// { dg-require-effective-target hosted } + +// N5008 20.3.2.5 Struct owner_hash [util.smartptr.owner.hash] + +#include <memory> +#include <algorithm> +#include <testsuite_hooks.h> + +struct A { }; + +struct B { A a[2]; }; + +int +test01() +{ + // test empty shared_ptr hashes compare equivalent + std::owner_hash oh; + std::shared_ptr<A> p1; + std::shared_ptr<A> p2; + VERIFY( oh(p1) == oh(p2) ); + std::weak_ptr<A> p3; + VERIFY( oh(p1) == oh(p3) ); + VERIFY( oh(p1) == oh(p3) ); + return 0; +} + + +// Construction from pointer +int +test02() +{ + std::owner_hash oh; + + std::shared_ptr<A> empty; + + std::shared_ptr<A> a1(new A); + VERIFY( oh(empty) != oh(a1) ); + + std::shared_ptr<A> a2(new A); + VERIFY( oh(a1) != oh(a2) ); + + std::weak_ptr<A> w1(a1); + VERIFY( oh(a1) == oh(w1) ); + + std::weak_ptr<A> w2(a2); + VERIFY( oh(w1) != oh(w2) ); + + a1.reset(); + VERIFY( oh(empty) == oh(a1) ); + VERIFY( oh(a1) != oh(w1) ); + + a2.reset(); + VERIFY( oh(a2) == oh(a1) ); + + return 0; +} + +// aliasing +int +test03() +{ + std::owner_hash oh; + + std::shared_ptr<B> b(new B); + std::shared_ptr<A> a0(b, &b->a[0]); + std::shared_ptr<A> a1(b, &b->a[1]); + // values are different but owners are ohuivalent: + VERIFY( a0 < a1 && oh(a0) == oh(a1) && oh(b) == oh(a0) && oh(b) == oh(a1) ); + + std::weak_ptr<A> w0(a0); + std::weak_ptr<A> w1(a1); + VERIFY( oh(w0) == oh(w1) ); + VERIFY( oh(a0) == oh(w1) ); + VERIFY( oh(w0) == oh(a1) ); + + return 0; +} + +int +main() +{ + test01(); + test02(); + test03(); + return 0; +} diff --git a/libstdc++-v3/testsuite/20_util/owner_hash/noexcept.cc b/libstdc++-v3/testsuite/20_util/owner_hash/noexcept.cc new file mode 100644 index 0000000..12b2f2f --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/owner_hash/noexcept.cc @@ -0,0 +1,16 @@ +// { dg-do compile { target c++26 } } +// { dg-require-effective-target hosted } + +// N5008 20.3.2.5 Struct owner_hash [util.smartptr.owner.hash] + +#include <memory> + +const std::owner_hash oh; +const std::shared_ptr<int> si; +const std::weak_ptr<int> wi; +static_assert( noexcept(!oh(si)) ); +static_assert( noexcept(!oh(wi)) ); +const std::shared_ptr<long> sl; +const std::weak_ptr<char> wc; +static_assert( noexcept(!oh(sl)) ); +static_assert( noexcept(!oh(wc)) ); diff --git a/libstdc++-v3/testsuite/20_util/pair/astuple/get_by_type.cc b/libstdc++-v3/testsuite/20_util/pair/astuple/get_by_type.cc index 33ebf7a..05a61c3 100644 --- a/libstdc++-v3/testsuite/20_util/pair/astuple/get_by_type.cc +++ b/libstdc++-v3/testsuite/20_util/pair/astuple/get_by_type.cc @@ -33,3 +33,55 @@ void test01() const int&& cpsecond __attribute__((unused)) = std::get<int>(std::move(cp)); } + +// PR libstdc++/121745 return of get(pair<_Up, _Tp>&& __p) may be ill-formed +void +test_pr121745(std::pair<float&, int&> p) +{ + float& pfirst = std::get<float&>(std::move(p)); + int& psecond = std::get<int&>(std::move(p)); + + const auto& p2 = p; + float& p2first = std::get<float&>(std::move(p2)); + int& p2second = std::get<int&>(std::move(p2)); +} + +template<typename T, typename Pair> +using get_t = decltype(std::get<T>(std::declval<Pair>())); + +// Check that get<T>(Pair) returns Ret +template<typename T, typename Pair, typename Ret> +constexpr bool verify = std::is_same<get_t<T, Pair>, Ret>::value; + +template<typename T1, typename T2> +void +check() +{ + // Overloads for accessing first member + static_assert( verify<T1, std::pair<T1, T2>&, T1&>, + "T1& get(pair<T1, T2>&)" ); + static_assert( verify<T1, const std::pair<T1, T2>&, const T1&>, + "const T1& get(const pair<T1, T2>&)" ); + static_assert( verify<T1, std::pair<T1, T2>&&, T1&&>, + "T1&& get(pair<T1, T2>&&)" ); + static_assert( verify<T1, const std::pair<T1, T2>&&, const T1&&>, + "const T1&& get(const pair<T1, T2>&&)" ); + + // Overloads for accessing second member + static_assert( verify<T2, std::pair<T1, T2>&, T2&>, + "T2& get(pair<T1, T2>&)" ); + static_assert( verify<T2, const std::pair<T1, T2>&, const T2&>, + "const T2& get(const pair<T1, T2>&)" ); + static_assert( verify<T2, std::pair<T1, T2>&&, T2&&>, + "T2&& get(pair<T1, T2>&&)" ); + static_assert( verify<T2, const std::pair<T1, T2>&&, const T2&&>, + "const T2&& get(const pair<T1, T2>&&)" ); +} + +void +test_all() +{ + check<float, int>(); + check<float&, int&>(); + check<float&&, int&&>(); +} diff --git a/libstdc++-v3/testsuite/20_util/pair/cons/110853.cc b/libstdc++-v3/testsuite/20_util/pair/cons/110853.cc new file mode 100644 index 0000000..57ebfb8 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/pair/cons/110853.cc @@ -0,0 +1,10 @@ +// { dg-do compile { target c++17 } } +// PR libstdc++/110853 +// Bad interaction between deduction guide with decay and constraints +// (CTAD, std::pair and function lvalue) + +#include <utility> + +void func() {} +std::pair p(1, func); +std::pair<int, void (*)()>& r = p; diff --git a/libstdc++-v3/testsuite/20_util/ratio/operations/ops_overflow_neg.cc b/libstdc++-v3/testsuite/20_util/ratio/operations/ops_overflow_neg.cc index 5eff8e3..368866a 100644 --- a/libstdc++-v3/testsuite/20_util/ratio/operations/ops_overflow_neg.cc +++ b/libstdc++-v3/testsuite/20_util/ratio/operations/ops_overflow_neg.cc @@ -1,6 +1,6 @@ // { dg-do compile { target c++11 } } // { dg-require-cstdint "" } - +// { dg-additional-options "-Wsystem-headers" } // 2008-07-03 Chris Fairles <chris.fairles@gmail.com> // Copyright (C) 2008-2025 Free Software Foundation, Inc. diff --git a/libstdc++-v3/testsuite/20_util/reference_wrapper/p2655r3.cc b/libstdc++-v3/testsuite/20_util/reference_wrapper/p2655r3.cc new file mode 100644 index 0000000..f2fafec --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/reference_wrapper/p2655r3.cc @@ -0,0 +1,75 @@ +// P2655R3 - common_reference_t of reference_wrapper Should Be a Reference Type +// Implemented as a DR against C++20 +// { dg-do compile { target c++20 } } + +#include <functional> + +#if __cpp_lib_common_reference_wrapper != 202302L +# error "Feature-test macro __cpp_lib_common_reference_wrapper has wrong value in <functional>" +#endif + +using std::is_same_v; +using std::common_reference_t; +using std::reference_wrapper; + +static_assert( is_same_v<common_reference_t<const reference_wrapper<int>&, int&>, int&> ); +static_assert( is_same_v<common_reference_t<int&, const reference_wrapper<int>&>, int&> ); + +static_assert( is_same_v<common_reference_t<reference_wrapper<int>, int&>, + common_reference_t<int&, int&>> ); +static_assert( is_same_v<common_reference_t<reference_wrapper<int>, const int&>, + common_reference_t<int&, const int&>> ); +static_assert( is_same_v<common_reference_t<reference_wrapper<const int>, int&>, + common_reference_t<const int&, int&>> ); + +static_assert( is_same_v<common_reference_t<int&, reference_wrapper<int>>, + common_reference_t<int&, int&>> ); +static_assert( is_same_v<common_reference_t<const int&, reference_wrapper<int>>, + common_reference_t<int&, const int&>> ); +static_assert( is_same_v<common_reference_t<int&, reference_wrapper<const int>>, + common_reference_t<const int&, int&>> ); + +static_assert( is_same_v<common_reference_t<reference_wrapper<int>&, reference_wrapper<int>&>, + reference_wrapper<int>&> ); + +static_assert( is_same_v<common_reference_t<reference_wrapper<char>, + reference_wrapper<int>>, + int> ); +static_assert( is_same_v<common_reference_t<reference_wrapper<reference_wrapper<int>>, + reference_wrapper<int>>, + reference_wrapper<int>> ); +static_assert( is_same_v<common_reference_t<reference_wrapper<int>, + reference_wrapper<reference_wrapper<int>>>, + reference_wrapper<int>> ); + +struct A { }; +struct B { operator A&() const; }; + +template<typename T, typename U> +concept has_common_reference = requires { + typename std::common_reference_t<T, U>; +}; + +static_assert( is_same_v<common_reference_t<reference_wrapper<A>, const B&>, A&> ); +// reference_wrapper<const B> is not convertible to A&, as it would require two user +// defined conversions. +static_assert( !has_common_reference<A, reference_wrapper<const B>> ); +static_assert( !has_common_reference<reference_wrapper<A>, reference_wrapper<const B>> ); + +struct D1 : A {}; +struct D2 : A {}; + +template<template<class> typename Qual1, template<class> typename Qual2> +struct std::basic_common_reference<D1, D2, Qual1, Qual2> + : std::common_reference<Qual1<A>, Qual2<A>> +{ }; + +template<template<class> typename Qual1, template<class> typename Qual2> +struct std::basic_common_reference<D2, D1, Qual1, Qual2> + : std::common_reference<Qual1<A>, Qual2<A>> +{ }; + +static_assert( is_same_v<common_reference_t<D1&, D2&>, A&> ); +static_assert( is_same_v<common_reference_t<reference_wrapper<D1>, D2&>, A&> ); +static_assert( is_same_v<common_reference_t<D1&, reference_wrapper<D2>>, A&> ); +static_assert( !has_common_reference<reference_wrapper<D1>, reference_wrapper<D2>> ); diff --git a/libstdc++-v3/testsuite/20_util/scoped_allocator/69293_neg.cc b/libstdc++-v3/testsuite/20_util/scoped_allocator/69293_neg.cc index d4be086..a3fbeba 100644 --- a/libstdc++-v3/testsuite/20_util/scoped_allocator/69293_neg.cc +++ b/libstdc++-v3/testsuite/20_util/scoped_allocator/69293_neg.cc @@ -42,6 +42,8 @@ static_assert(uses_allocator<X, inner_alloc_type>{}, ""); static_assert(!is_constructible<X, allocator_arg_t, inner_alloc_type>{}, ""); static_assert(!is_constructible<X, inner_alloc_type>{}, ""); +// { dg-error "too many initializers" "" { target c++20 } 0 } + void test01() { diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/assign/auto_ptr.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/assign/auto_ptr.cc index 96678d2..d4096ce 100644 --- a/libstdc++-v3/testsuite/20_util/shared_ptr/assign/auto_ptr.cc +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/assign/auto_ptr.cc @@ -1,4 +1,4 @@ -// { dg-options "-Wno-deprecated" } +// { dg-options "-Wno-deprecated-declarations" } // { dg-add-options using-deprecated } // { dg-do run { target c++11 } } // { dg-require-effective-target hosted } diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/assign/auto_ptr_neg.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/assign/auto_ptr_neg.cc index d7dfcf1..09f7ff4 100644 --- a/libstdc++-v3/testsuite/20_util/shared_ptr/assign/auto_ptr_neg.cc +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/assign/auto_ptr_neg.cc @@ -1,4 +1,4 @@ -// { dg-options "-Wno-deprecated" } +// { dg-options "-Wno-deprecated-declarations" } // { dg-add-options using-deprecated } // { dg-do compile { target c++11 } } // { dg-require-effective-target hosted } diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/assign/auto_ptr_rvalue.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/assign/auto_ptr_rvalue.cc index 8c98f7e..c94fcdd 100644 --- a/libstdc++-v3/testsuite/20_util/shared_ptr/assign/auto_ptr_rvalue.cc +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/assign/auto_ptr_rvalue.cc @@ -1,4 +1,4 @@ -// { dg-options "-Wno-deprecated" } +// { dg-options "-Wno-deprecated -Wno-deprecated-declarations" } // { dg-add-options using-deprecated } // { dg-do compile { target c++11 } } // { dg-require-effective-target hosted } diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/1.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/1.cc index b3d2cde..2be9f30 100644 --- a/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/1.cc +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/1.cc @@ -18,7 +18,7 @@ // { dg-do run { target c++11 } } // { dg-require-gthreads "" } // { dg-require-effective-target hosted } -// { dg-additional-options "-Wno-deprecated" { target c++20 } } +// { dg-additional-options "-Wno-deprecated-declarations" { target c++20 } } #include <memory> diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/2.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/2.cc index 9c75922..95baedd 100644 --- a/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/2.cc +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/2.cc @@ -18,7 +18,7 @@ // { dg-do run { target c++11 } } // { dg-require-gthreads "" } // { dg-require-effective-target hosted } -// { dg-additional-options "-Wno-deprecated" { target c++20 } } +// { dg-additional-options "-Wno-deprecated-declarations" { target c++20 } } #include <memory> diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/3.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/3.cc index a722792..3cbe08b 100644 --- a/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/3.cc +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/3.cc @@ -1,7 +1,7 @@ // { dg-do run { target c++11 } } // { dg-additional-options "-pthread" { target pthread } } // { dg-require-gthreads "" } -// { dg-additional-options "-Wno-deprecated" { target c++20 } } +// { dg-additional-options "-Wno-deprecated-declarations" { target c++20 } } // Copyright (C) 2014-2025 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/pr118757.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/pr118757.cc new file mode 100644 index 0000000..f49ae38 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/atomic/pr118757.cc @@ -0,0 +1,30 @@ +// { dg-do run { target c++20 } } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-require-gthreads "" } +// { dg-require-effective-target hosted } + +#include <memory> +#include <chrono> +#include <thread> +#include <barrier> + +std::shared_ptr<int> q = std::make_shared<int>(42); +std::atomic<std::shared_ptr<int>> p = q; + +std::barrier bar(2); + +void signaller() +{ + std::this_thread::sleep_for(std::chrono::seconds(1)); + p.store(std::shared_ptr<int>(q, nullptr)); + p.notify_one(); + bar.arrive_and_wait(); +} + +int main(int, char**) +{ + std::thread thr(signaller); + p.wait(q); + bar.arrive_and_wait(); + thr.join(); +} diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/cons/43820_neg.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/43820_neg.cc index 544cc01..12639fa 100644 --- a/libstdc++-v3/testsuite/20_util/shared_ptr/cons/43820_neg.cc +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/43820_neg.cc @@ -1,4 +1,4 @@ -// { dg-options "-Wno-deprecated" } +// { dg-options "-Wno-deprecated-declarations" } // { dg-add-options using-deprecated } // { dg-do compile { target c++11 } } // { dg-require-effective-target hosted } diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/cons/auto_ptr.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/auto_ptr.cc index 2e4f0ba..847210b 100644 --- a/libstdc++-v3/testsuite/20_util/shared_ptr/cons/auto_ptr.cc +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/auto_ptr.cc @@ -1,4 +1,4 @@ -// { dg-options "-Wno-deprecated" } +// { dg-options "-Wno-deprecated-declarations" } // { dg-add-options using-deprecated } // { dg-do run { target c++11 } } // { dg-require-effective-target hosted } diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/cons/auto_ptr_neg.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/auto_ptr_neg.cc index 11b62d4..5779f85 100644 --- a/libstdc++-v3/testsuite/20_util/shared_ptr/cons/auto_ptr_neg.cc +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/auto_ptr_neg.cc @@ -1,4 +1,4 @@ -// { dg-options "-Wno-deprecated" } +// { dg-options "-Wno-deprecated-declarations" } // { dg-add-options using-deprecated } // { dg-do compile { target c++11 } } // { dg-require-effective-target hosted } diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/cons/move.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/move.cc index 2dc161d..e380d76 100644 --- a/libstdc++-v3/testsuite/20_util/shared_ptr/cons/move.cc +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/cons/move.cc @@ -140,6 +140,8 @@ test05() { reset_count_struct __attribute__((unused)) reset; + // The std::move here prevents copy elision, so we construct from a prvalue. + // { dg-prune-output "-Wpessimizing-move" } std::shared_ptr<A> a(std::move(std::shared_ptr<A>(new A))); VERIFY( a.use_count() == 1 ); VERIFY( A::ctor_count == 1 ); diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/creation/dr925.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/dr925.cc index 162f92d..4743387 100644 --- a/libstdc++-v3/testsuite/20_util/shared_ptr/creation/dr925.cc +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/dr925.cc @@ -1,4 +1,4 @@ -// { dg-options "-Wno-deprecated" } +// { dg-options "-Wno-deprecated-declarations" } // { dg-add-options using-deprecated } // { dg-do run { target c++11 } } // { dg-require-effective-target hosted } diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/observers/owner_equal.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/observers/owner_equal.cc new file mode 100644 index 0000000..7ec8691 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/observers/owner_equal.cc @@ -0,0 +1,74 @@ +// { dg-do run { target c++26 } } +// { dg-require-effective-target hosted } + +// N5008 20.3.2.2.6 shared_ptr observers [util.smartptr.shared.obs] + +#include <memory> +#include <testsuite_hooks.h> + +struct A +{ + int i; + virtual ~A() { } +}; + +struct B : A +{ +}; + +void +test01() +{ + // test empty shared_ptr owners compare equivalent + std::shared_ptr<A> p1; + std::shared_ptr<B> p2; + VERIFY( p1.owner_equal(p2) && p2.owner_equal(p1) ); +} + + +// Construction from pointer +void +test02() +{ + std::shared_ptr<A> a0; + + std::shared_ptr<A> a1(new A); + VERIFY( !a1.owner_equal(a0) && !a0.owner_equal(a1) ); + + std::shared_ptr<B> b1(new B); + VERIFY( !a1.owner_equal(b1) && !b1.owner_equal(a1) ); + + std::shared_ptr<A> a2(a1); + VERIFY( a1.owner_equal(a2) && a2.owner_equal(a1) ); + a2 = b1; + VERIFY( b1.owner_equal(a2) && a2.owner_equal(b1) ); + + std::weak_ptr<A> w1(a1); + VERIFY( a1.owner_equal(w1) && w1.owner_equal(a1) ); + std::weak_ptr<A> w2(a2); + VERIFY( b1.owner_equal(w2) && w2.owner_equal(b1) ); + + static_assert( noexcept(a1.owner_equal(a0)) ); + static_assert( noexcept(a1.owner_equal(b1)) ); + static_assert( noexcept(b1.owner_equal(a1)) ); + static_assert( noexcept(a1.owner_equal(w1)) ); + static_assert( noexcept(b1.owner_equal(w1)) ); +} + +// Aliasing +void +test03() +{ + std::shared_ptr<A> p1(new A()); + std::shared_ptr<int> p2(p1, &p1->i); + VERIFY( p1.owner_equal(p2) && p2.owner_equal(p1) ); +} + +int +main() +{ + test01(); + test02(); + test03(); + return 0; +} diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/observers/owner_hash.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/observers/owner_hash.cc new file mode 100644 index 0000000..8e6c02c --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/observers/owner_hash.cc @@ -0,0 +1,71 @@ +// { dg-do run { target c++26 } } +// { dg-require-effective-target hosted } + +// N5008 20.3.2.2.6 shared_ptr observers [util.smartptr.shared.obs] + +#include <memory> +#include <testsuite_hooks.h> + +struct A +{ + int i; + virtual ~A() { } +}; + +struct B : A +{ +}; + +void +test01() +{ + // test empty shared_ptr hashes compare equivalent + std::shared_ptr<A> p1; + std::shared_ptr<B> p2; + VERIFY( p1.owner_hash() == p2.owner_hash() ); +} + + +// Construction from pointer +void +test02() +{ + std::shared_ptr<A> a0; + + std::shared_ptr<A> a1(new A); + VERIFY( a1.owner_hash() != a0.owner_hash() ); + + std::shared_ptr<B> b1(new B); + VERIFY( a1.owner_hash() != b1.owner_hash() ); + + std::shared_ptr<A> a2(a1); + VERIFY( a1.owner_hash() == a2.owner_hash() ); + a2 = b1; + VERIFY( b1.owner_hash() == a2.owner_hash() ); + + std::weak_ptr<A> w1(a1); + VERIFY( a1.owner_hash() == w1.owner_hash() ); + std::weak_ptr<A> w2(a2); + VERIFY( b1.owner_hash() == w2.owner_hash() ); + + static_assert( noexcept(a1.owner_hash()) ); + static_assert( noexcept(b1.owner_hash()) ); +} + +// Aliasing +void +test03() +{ + std::shared_ptr<A> p1(new A()); + std::shared_ptr<int> p2(p1, &p1->i); + VERIFY( p1.owner_hash() == p2.owner_hash() ); +} + +int +main() +{ + test01(); + test02(); + test03(); + return 0; +} diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/requirements/1.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/requirements/1.cc index 8ddb5d2..19090d1 100644 --- a/libstdc++-v3/testsuite/20_util/shared_ptr/requirements/1.cc +++ b/libstdc++-v3/testsuite/20_util/shared_ptr/requirements/1.cc @@ -3,31 +3,122 @@ #include <memory> #include <testsuite_tr1.h> +#include <type_traits> using namespace __gnu_test; +template<template<class> class SmartPtr> void test01() { - std::shared_ptr<ClassType> ptr; - std::shared_ptr<const ClassType> ptr2 = ptr; + SmartPtr<ClassType> ptr; + SmartPtr<const ClassType> ptr2 = ptr; #if __cpp_lib_shared_ptr_arrays >= 201611L - std::shared_ptr<ClassType[10]> ptr_array; - std::shared_ptr<ClassType[]> ptr_array2 = ptr_array; - std::shared_ptr<ClassType const []> ptr_array3 = ptr_array; + SmartPtr<ClassType[10]> ptr_array; + SmartPtr<ClassType[]> ptr_array2 = ptr_array; + SmartPtr<ClassType const []> ptr_array3 = ptr_array; #endif } +template<template<class> class SmartPtr> void test02() { - std::shared_ptr<IncompleteClass> ptr; - std::shared_ptr<const IncompleteClass> ptr2 = ptr; + SmartPtr<IncompleteClass> ptr; + SmartPtr<const IncompleteClass> ptr2 = ptr; #if __cpp_lib_shared_ptr_arrays >= 201611L - std::shared_ptr<IncompleteClass[10]> ptr_array; - std::shared_ptr<IncompleteClass[]> ptr_array2 = ptr_array; - std::shared_ptr<IncompleteClass const []> ptr_array3 = ptr_array; + SmartPtr<IncompleteClass[10]> ptr_array; + SmartPtr<IncompleteClass[]> ptr_array2 = ptr_array; + SmartPtr<IncompleteClass const []> ptr_array3 = ptr_array; #endif } + +template<template<class> class SmartPtr> +void +test03() +{ + static_assert( std::is_convertible<SmartPtr<int>, SmartPtr<const int>>::value); + static_assert(!std::is_convertible<SmartPtr<const int>, SmartPtr<int>>::value); + static_assert( std::is_convertible<SmartPtr<ClassType>, SmartPtr<const ClassType>>::value); + static_assert(!std::is_convertible<SmartPtr<const ClassType>, SmartPtr<ClassType>>::value); + static_assert( std::is_convertible<SmartPtr<IncompleteClass>, SmartPtr<const IncompleteClass>>::value); + static_assert(!std::is_convertible<SmartPtr<const IncompleteClass>, SmartPtr<IncompleteClass>>::value); + static_assert( std::is_convertible<SmartPtr<void>, SmartPtr<const void>>::value); + static_assert(!std::is_convertible<SmartPtr<const void>, SmartPtr<void>>::value); + + static_assert( std::is_convertible<SmartPtr<int>, SmartPtr<void>>::value); + static_assert(!std::is_convertible<SmartPtr<void>, SmartPtr<int>>::value); + static_assert( std::is_convertible<SmartPtr<int>, SmartPtr<const void>>::value); + static_assert( std::is_convertible<SmartPtr<const int>, SmartPtr<const void>>::value); + static_assert(!std::is_convertible<SmartPtr<const int>, SmartPtr<void>>::value); + static_assert(!std::is_convertible<SmartPtr<const void>, SmartPtr<const int>>::value); + static_assert( std::is_convertible<SmartPtr<ClassType>, SmartPtr<void>>::value); + static_assert( std::is_convertible<SmartPtr<ClassType>, SmartPtr<const void>>::value); + + static_assert(!std::is_convertible<SmartPtr<int*>, SmartPtr<const int*>>::value); + static_assert( std::is_convertible<SmartPtr<int*>, SmartPtr<const int* const>>::value); + static_assert(!std::is_convertible<SmartPtr<const int*>, SmartPtr<int*>>::value); + static_assert(!std::is_convertible<SmartPtr<const int* const>, SmartPtr<int*>>::value); + + static_assert(!std::is_convertible<SmartPtr<ClassType*>, SmartPtr<const ClassType*>>::value); + static_assert( std::is_convertible<SmartPtr<ClassType*>, SmartPtr<const ClassType* const>>::value); + static_assert(!std::is_convertible<SmartPtr<const ClassType*>, SmartPtr<ClassType*>>::value); + static_assert(!std::is_convertible<SmartPtr<const ClassType* const>, SmartPtr<ClassType*>>::value); + + static_assert(!std::is_convertible<SmartPtr<void*>, SmartPtr<const void*>>::value); + static_assert( std::is_convertible<SmartPtr<void*>, SmartPtr<const void* const>>::value); + static_assert(!std::is_convertible<SmartPtr<const void*>, SmartPtr<void*>>::value); + static_assert(!std::is_convertible<SmartPtr<const void* const>, SmartPtr<void*>>::value); + +#if __cpp_lib_shared_ptr_arrays >= 201611L + static_assert( std::is_convertible<SmartPtr<int[10]>, SmartPtr<int[]>>::value); + static_assert(!std::is_convertible<SmartPtr<int[]>, SmartPtr<int[10]>>::value); + static_assert( std::is_convertible<SmartPtr<int[10]>, SmartPtr<int const[]>>::value); + static_assert( std::is_convertible<SmartPtr<int[10]>, SmartPtr<int const[10]>>::value); + static_assert( std::is_convertible<SmartPtr<int[]>, SmartPtr<int const[]>>::value); + static_assert(!std::is_convertible<SmartPtr<int const[]>, SmartPtr<int[]>>::value); + static_assert( std::is_convertible<SmartPtr<int const[10]>, SmartPtr<int const[]>>::value); + static_assert(!std::is_convertible<SmartPtr<int const[]>, SmartPtr<int const[10]>>::value); + + static_assert( std::is_convertible<SmartPtr<ClassType[10]>, SmartPtr<ClassType[]>>::value); + static_assert(!std::is_convertible<SmartPtr<ClassType[]>, SmartPtr<ClassType[10]>>::value); + static_assert( std::is_convertible<SmartPtr<ClassType[10]>, SmartPtr<ClassType const[]>>::value); + static_assert( std::is_convertible<SmartPtr<ClassType[10]>, SmartPtr<ClassType const[10]>>::value); + static_assert( std::is_convertible<SmartPtr<ClassType[]>, SmartPtr<ClassType const[]>>::value); + static_assert(!std::is_convertible<SmartPtr<ClassType const[]>, SmartPtr<ClassType[]>>::value); + static_assert( std::is_convertible<SmartPtr<ClassType const[10]>, SmartPtr<ClassType const[]>>::value); + static_assert(!std::is_convertible<SmartPtr<ClassType const[]>, SmartPtr<ClassType const[10]>>::value); + + static_assert( std::is_convertible<SmartPtr<IncompleteClass[10]>, SmartPtr<IncompleteClass[]>>::value); + static_assert(!std::is_convertible<SmartPtr<IncompleteClass[]>, SmartPtr<IncompleteClass[10]>>::value); + static_assert( std::is_convertible<SmartPtr<IncompleteClass[10]>, SmartPtr<IncompleteClass const[]>>::value); + static_assert( std::is_convertible<SmartPtr<IncompleteClass[10]>, SmartPtr<IncompleteClass const[10]>>::value); + static_assert( std::is_convertible<SmartPtr<IncompleteClass[]>, SmartPtr<IncompleteClass const[]>>::value); + static_assert(!std::is_convertible<SmartPtr<IncompleteClass const[]>, SmartPtr<IncompleteClass[]>>::value); + static_assert( std::is_convertible<SmartPtr<IncompleteClass const[10]>, SmartPtr<IncompleteClass const[]>>::value); + static_assert(!std::is_convertible<SmartPtr<IncompleteClass const[]>, SmartPtr<IncompleteClass const[10]>>::value); + + static_assert( std::is_convertible<SmartPtr<int*[10]>, SmartPtr<int*[]>>::value); + static_assert(!std::is_convertible<SmartPtr<int*[]>, SmartPtr<int*[10]>>::value); + static_assert( std::is_convertible<SmartPtr<int*[10]>, SmartPtr<int* const[10]>>::value); + static_assert( std::is_convertible<SmartPtr<int*[10]>, SmartPtr<int* const[]>>::value); + + static_assert(!std::is_convertible<SmartPtr<int*[]>, SmartPtr<void*[]>>::value); + static_assert(!std::is_convertible<SmartPtr<int*[]>, SmartPtr<void const *[]>>::value); +#endif +} + +int +main() +{ + test01<std::shared_ptr>(); + test01<std::weak_ptr>(); + + test02<std::shared_ptr>(); + test02<std::weak_ptr>(); + + test03<std::shared_ptr>(); + test03<std::weak_ptr>(); +} diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/destroy/121024.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/destroy/121024.cc new file mode 100644 index 0000000..781dd40 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/destroy/121024.cc @@ -0,0 +1,77 @@ +// { dg-do compile { target c++26 } } + +// Bug 121024 +// ranges::destroy and ranges::destroy_n do not end lifetime of trivial types + +#include <memory> + +consteval bool is_within_lifetime(const auto* p) noexcept +{ + return __builtin_constant_p(*p); +} + +template<typename T> +struct Buf +{ + constexpr Buf() : p(std::allocator<T>().allocate(2)) { } + constexpr ~Buf() { std::allocator<T>().deallocate(p, 2); } + T* p; +}; + +template<typename T> +consteval bool +test_destroy() +{ + Buf<T> buf; + std::uninitialized_value_construct(buf.p, buf.p + 2); + std::destroy(buf.p, buf.p + 2); + return not is_within_lifetime(buf.p) && not is_within_lifetime(buf.p + 1); +} + +template<typename T> +consteval bool +test_destroy_n() +{ + Buf<T> buf; + std::uninitialized_value_construct_n(buf.p, 2); + std::destroy_n(buf.p, 2); + return not is_within_lifetime(buf.p) && not is_within_lifetime(buf.p + 1); +} + +template<typename T> +consteval bool +test_ranges_destroy() +{ + Buf<T> buf; + std::uninitialized_value_construct(buf.p, buf.p + 2); + std::ranges::destroy(buf.p, buf.p + 2); + return not is_within_lifetime(buf.p) && not is_within_lifetime(buf.p + 1); +} + +template<typename T> +consteval bool +test_ranges_destroy_n() +{ + Buf<T> buf; + std::uninitialized_value_construct_n(buf.p, 2); + std::ranges::destroy_n(buf.p, 2); + return not is_within_lifetime(buf.p) && not is_within_lifetime(buf.p + 1); +} + +struct O +{ + constexpr O() { } + constexpr ~O() { } +}; + +// These all fail for GCC because is_within_lifetime still returns true +// after the lifetime has been ended. +// { dg-xfail-if "PR c++/102284" { *-*-* } } +static_assert( test_destroy<int>() ); +static_assert( test_destroy<O>() ); +static_assert( test_destroy_n<int>() ); +static_assert( test_destroy_n<O>() ); +static_assert( test_ranges_destroy<int>() ); +static_assert( test_ranges_destroy<O>() ); +static_assert( test_ranges_destroy_n<int>() ); +static_assert( test_ranges_destroy_n<O>() ); diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/memory_management_tools/destroy_n_neg.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/memory_management_tools/destroy_n_neg.cc new file mode 100644 index 0000000..12c0dc5 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/memory_management_tools/destroy_n_neg.cc @@ -0,0 +1,59 @@ +// Copyright (C) 2017-2025 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-do compile { target c++17 } } + +#include <memory> + +// This has a trivial destructor, but should not be destructible! +struct DeletedDtor { + ~DeletedDtor() = delete; +}; + +void +test01() +{ + alignas(DeletedDtor) unsigned char buf[sizeof(DeletedDtor)]; + auto p = ::new (buf) DeletedDtor(); + std::destroy_n(p, 1); +} + +class PrivateDtor { + ~PrivateDtor() { } +}; + +void +test02() +{ + alignas(PrivateDtor) unsigned char buf[sizeof(PrivateDtor)]; + auto p = ::new (buf) PrivateDtor(); + std::destroy_n(p, 1); +} + +#if __cpp_constexpr_dynamic_alloc // >= C++20 +consteval bool +test03() +{ + DeletedDtor* p = nullptr; + std::destroy_n(p, 0); + return true; +} +static_assert(test03()); +#endif + +// { dg-error "deleted function .*DeletedDtor" "" { target *-*-* } 0 } +// { dg-error "PrivateDtor.* is private" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/memory_management_tools/destroy_neg.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/memory_management_tools/destroy_neg.cc index 5946a82..096f218 100644 --- a/libstdc++-v3/testsuite/20_util/specialized_algorithms/memory_management_tools/destroy_neg.cc +++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/memory_management_tools/destroy_neg.cc @@ -29,8 +29,7 @@ test01() { alignas(DeletedDtor) unsigned char buf[sizeof(DeletedDtor)]; auto p = ::new (buf) DeletedDtor(); - std::destroy(p, p + 1); // { dg-error "here" } - std::destroy_n(p, 1); // { dg-error "here" } + std::destroy(p, p + 1); } class PrivateDtor { @@ -42,8 +41,19 @@ test02() { alignas(PrivateDtor) unsigned char buf[sizeof(PrivateDtor)]; auto p = ::new (buf) PrivateDtor(); - std::destroy(p, p + 1); // { dg-error "here" } - std::destroy_n(p, 1); // { dg-error "here" } + std::destroy(p, p + 1); } -// { dg-error "value type is destructible" "" { target *-*-* } 0 } +#if __cpp_constexpr_dynamic_alloc // >= C++20 +consteval bool +test03() +{ + DeletedDtor* p = nullptr; + std::destroy(p, p); + return true; +} +static_assert(test03()); +#endif + +// { dg-error "deleted function .*DeletedDtor" "" { target *-*-* } 0 } +// { dg-error "PrivateDtor.* is private" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_default_construct/120397.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_default_construct/120397.cc new file mode 100644 index 0000000..7aa05d7 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_default_construct/120397.cc @@ -0,0 +1,19 @@ +// { dg-do compile { target c++17 } } + +#include <memory> + +// PR libstdc++/120397 +// std::uninitialized_value_construct cannot create arrays of non-trivially +// destructible types + +struct X { X() { } ~X() { } }; + +void def(X (*x)[1]) +{ + std::uninitialized_default_construct(x, x+1); +} + +void def_n(X (*x)[1]) +{ + std::uninitialized_default_construct_n(x, 1); +} diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_fill/120931.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_fill/120931.cc new file mode 100644 index 0000000..766fac0 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_fill/120931.cc @@ -0,0 +1,16 @@ +// { dg-options "-std=gnu++98" } +// { dg-do compile { target c++98_only } } +// std::deque<character types>::resize() method fails with -std=c++98 + +#include <memory> +#include <testsuite_iterators.h> + +void +test_pr120931() +{ + using __gnu_test::test_container; + using __gnu_test::forward_iterator_wrapper; + unsigned char c[1]; + test_container<unsigned char, forward_iterator_wrapper> f(c); + std::uninitialized_fill(f.begin(), f.end(), 0); +} diff --git a/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct/120397.cc b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct/120397.cc new file mode 100644 index 0000000..f4d9fce --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/specialized_algorithms/uninitialized_value_construct/120397.cc @@ -0,0 +1,19 @@ +// { dg-do compile { target c++17 } } + +#include <memory> + +// PR libstdc++/120397 +// std::uninitialized_value_construct cannot create arrays of non-trivially +// destructible types + +struct X { X() { } ~X() { } }; + +void val(X (*x)[1]) +{ + std::uninitialized_value_construct(x, x+1); +} + +void val_n(X (*x)[1]) +{ + std::uninitialized_value_construct_n(x, 1); +} diff --git a/libstdc++-v3/testsuite/20_util/synchronized_pool_resource/118681.cc b/libstdc++-v3/testsuite/20_util/synchronized_pool_resource/118681.cc new file mode 100644 index 0000000..facbf00 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/synchronized_pool_resource/118681.cc @@ -0,0 +1,6 @@ +// { dg-do run { target c++17 } } +// { dg-require-gthreads "" } +// Bug 118681 - unsynchronized_pool_resource may fail to respect alignment + +#define RESOURCE std::pmr::synchronized_pool_resource +#include "../unsynchronized_pool_resource/118681.cc" diff --git a/libstdc++-v3/testsuite/20_util/system_clock/99832.cc b/libstdc++-v3/testsuite/20_util/system_clock/99832.cc new file mode 100644 index 0000000..693d4d6 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/system_clock/99832.cc @@ -0,0 +1,14 @@ +// { dg-options "-O0 -g0" } +// { dg-do compile { target c++20 } } +// { dg-final { scan-assembler-not "system_clock9to_time_t" } } + +// Bug libstdc++/99832 +// std::chrono::system_clock::to_time_t needs ABI tag for 32-bit time_t + +#include <chrono> + +std::time_t +test_pr99832(std::chrono::system_clock::time_point t) +{ + return std::chrono::system_clock::to_time_t(t); +} diff --git a/libstdc++-v3/testsuite/20_util/tuple/dr3528.cc b/libstdc++-v3/testsuite/20_util/tuple/dr3528.cc new file mode 100644 index 0000000..c20ff95 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/tuple/dr3528.cc @@ -0,0 +1,46 @@ +// { dg-do compile { target c++17 } } + +// LWG 3528. make_from_tuple can perform (the equivalent of) a C-style cast + +#include <tuple> +#include <array> +#include <utility> + +template<typename T, typename Tuple> +using make_t = decltype(std::make_from_tuple<T>(std::declval<Tuple>())); + +template<typename T, typename Tuple, typename = void> +constexpr bool can_make = false; +template<typename T, typename Tuple> +constexpr bool can_make<T, Tuple, std::void_t<make_t<T, Tuple>>> = true; + +static_assert( can_make<int, std::tuple<int>> ); +static_assert( can_make<int, std::tuple<int>&> ); +static_assert( can_make<int, const std::tuple<int>&> ); +static_assert( can_make<int, std::array<short, 1>> ); +static_assert( can_make<int, const std::array<short, 1>&&> ); +static_assert( can_make<std::tuple<int, int>, std::pair<unsigned, long>> ); +static_assert( can_make<std::pair<int, int>, std::array<int, 2>> ); +static_assert( can_make<const int*, std::tuple<int*>> ); +static_assert( can_make<void*, std::tuple<int*>> ); +static_assert( can_make<int, std::tuple<>> ); +static_assert( ! can_make<int, std::tuple<int, int>> ); +static_assert( ! can_make<int, std::pair<short, char>> ); +static_assert( ! can_make<int, std::pair<short, char>&> ); +static_assert( ! can_make<int, std::tuple<const char*>> ); +static_assert( ! can_make<int*, std::tuple<const int*>> ); +static_assert( ! can_make<int*, std::tuple<void*>> ); +static_assert( ! can_make<int, std::array<int, 2>> ); +static_assert( ! can_make<void, std::tuple<>> ); +static_assert( ! can_make<void, std::array<int, 1>> ); + +struct Two +{ + Two(const char*, int); +}; + +static_assert( can_make<Two, std::tuple<char*, unsigned>> ); +static_assert( ! can_make<Two, std::tuple<const char*, int, int>> ); +static_assert( can_make<Two, std::pair<const char*, long>> ); +static_assert( ! can_make<Two, std::pair<int*, long>> ); +static_assert( ! can_make<std::pair<int, int>, std::array<int, 3>> ); diff --git a/libstdc++-v3/testsuite/20_util/tuple/requirements/empty_trivial.cc b/libstdc++-v3/testsuite/20_util/tuple/requirements/empty_trivial.cc new file mode 100644 index 0000000..ee18bb3 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/tuple/requirements/empty_trivial.cc @@ -0,0 +1,17 @@ +// { dg-do compile { target c++11 } } + +#include <tuple> +#include <type_traits> + +// Check that tuple<> has the expected trivial properties +static_assert(std::is_trivially_copyable<std::tuple<>>::value, + "tuple<> should be trivially copyable"); +static_assert(std::is_trivially_copy_constructible<std::tuple<>>::value, + "tuple<> should be trivially copy constructible"); +static_assert(std::is_trivially_move_constructible<std::tuple<>>::value, + "tuple<> should be trivially move constructible"); +static_assert(std::is_trivially_copy_assignable<std::tuple<>>::value, + "tuple<> should be trivially copy assignable"); +static_assert(std::is_trivially_move_assignable<std::tuple<>>::value, + "tuple<> should be trivially move assignable"); + diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/cons/auto_ptr.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/cons/auto_ptr.cc index c9ac7f9..b49171d 100644 --- a/libstdc++-v3/testsuite/20_util/unique_ptr/cons/auto_ptr.cc +++ b/libstdc++-v3/testsuite/20_util/unique_ptr/cons/auto_ptr.cc @@ -1,4 +1,4 @@ -// { dg-options "-Wno-deprecated" } +// { dg-options "-Wno-deprecated-declarations" } // { dg-add-options using-deprecated } // { dg-do run { target c++11 } } diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/cons/auto_ptr_neg.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/cons/auto_ptr_neg.cc index 7b9758f..6440dd7 100644 --- a/libstdc++-v3/testsuite/20_util/unique_ptr/cons/auto_ptr_neg.cc +++ b/libstdc++-v3/testsuite/20_util/unique_ptr/cons/auto_ptr_neg.cc @@ -1,4 +1,4 @@ -// { dg-options "-Wno-deprecated" } +// { dg-options "-Wno-deprecated-declarations" } // { dg-add-options using-deprecated } // { dg-do compile { target c++11 } } diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/lwg4148.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/lwg4148.cc index c70d7a6..f5697e4 100644 --- a/libstdc++-v3/testsuite/20_util/unique_ptr/lwg4148.cc +++ b/libstdc++-v3/testsuite/20_util/unique_ptr/lwg4148.cc @@ -1,4 +1,5 @@ // { dg-do compile { target c++11 } } +// { dg-additional-options "-Wsystem-headers" } // LWG 4148. unique_ptr::operator* should not allow dangling references diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/modifiers/93562.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/modifiers/93562.cc index 95df7af..0443578 100644 --- a/libstdc++-v3/testsuite/20_util/unique_ptr/modifiers/93562.cc +++ b/libstdc++-v3/testsuite/20_util/unique_ptr/modifiers/93562.cc @@ -76,11 +76,32 @@ test03() VERIFY(p2.get_deleter().id == -1); } +namespace B +{ + struct Deleter + { + Deleter& operator=(const Deleter&) = delete; + + void operator()(int* p) const noexcept { delete[] p; } + + // found by ADL + friend void swap(Deleter& lhs, Deleter& rhs) noexcept + { std::swap(lhs.id, rhs.id); } + + int id; + }; + + static_assert(!std::is_move_assignable<Deleter>::value, "not assignable"); +#if __cplusplus >= 201703L + static_assert(std::is_swappable_v<Deleter>, "but swappable"); +#endif +} // namespace B + void test04() { - std::unique_ptr<int[], A::Deleter> p1(new int[1]{1}, { -1 }); - std::unique_ptr<int[], A::Deleter> p2(new int[2]{2, 2}, { -2 }); + std::unique_ptr<int[], B::Deleter> p1(new int[1]{1}, { -1 }); + std::unique_ptr<int[], B::Deleter> p2(new int[2]{2, 2}, { -2 }); int* const pi1 = p1.get(); int* const pi2 = p2.get(); // This type must swappable even though the deleter is not move-assignable: diff --git a/libstdc++-v3/testsuite/20_util/unsynchronized_pool_resource/118681.cc b/libstdc++-v3/testsuite/20_util/unsynchronized_pool_resource/118681.cc new file mode 100644 index 0000000..9935f79 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/unsynchronized_pool_resource/118681.cc @@ -0,0 +1,58 @@ +// { dg-do run { target c++17 } } +// Bug 118681 - unsynchronized_pool_resource may fail to respect alignment + +#include <memory_resource> +#include <cstdio> +#include <testsuite_hooks.h> + +#ifndef RESOURCE +# define RESOURCE std::pmr::unsynchronized_pool_resource +#endif + +bool any_misaligned = false; + +bool +is_aligned(void* p, [[maybe_unused]] std::size_t size, std::size_t alignment) +{ + const bool misaligned = reinterpret_cast<std::uintptr_t>(p) % alignment; +#ifdef DEBUG + std::printf("allocate(%2zu, %2zu): %p is aligned %scorrectly\n", + size, alignment, p, misaligned ? "in" : ""); + any_misaligned |= misaligned; + return true; +#endif + return ! misaligned; +} + +void +test_alignment(std::pmr::memory_resource& res, bool dealloc) +{ + for (std::size_t alignment : { 8, 16, 32, 64 }) + { + for (std::size_t size : { 9, 12, 24, 40, 48, 56, 72 }) + { + void* p1 = res.allocate(size, alignment); + void* p2 = res.allocate(size, alignment); + + VERIFY( is_aligned(p1, size, alignment) ); + VERIFY( is_aligned(p2, size, alignment) ); + + if (dealloc) + { + res.deallocate(p1, size, alignment); + res.deallocate(p2, size, alignment); + } + } + } +} + +int main() +{ + RESOURCE res; + test_alignment(res, true); + res.release(); + test_alignment(res, false); + res.release(); + + VERIFY( ! any_misaligned ); +} diff --git a/libstdc++-v3/testsuite/20_util/variable_templates_for_traits.cc b/libstdc++-v3/testsuite/20_util/variable_templates_for_traits.cc index 253886b..b489587 100644 --- a/libstdc++-v3/testsuite/20_util/variable_templates_for_traits.cc +++ b/libstdc++-v3/testsuite/20_util/variable_templates_for_traits.cc @@ -1,4 +1,4 @@ -// { dg-additional-options "-Wno-deprecated" { target c++2a } } +// { dg-additional-options "-Wno-deprecated-declarations" { target c++2a } } // { dg-do compile { target c++17 } } // Copyright (C) 2014-2025 Free Software Foundation, Inc. diff --git a/libstdc++-v3/testsuite/20_util/variant/112591.cc b/libstdc++-v3/testsuite/20_util/variant/112591.cc new file mode 100644 index 0000000..b1b07c4 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/variant/112591.cc @@ -0,0 +1,32 @@ +// { dg-do run { target c++17 } } + +#include <variant> +#include <testsuite_hooks.h> + +struct NonEmpty { int x; }; +struct TrivialEmpty {}; +struct NonTrivialEmpty { ~NonTrivialEmpty() {} }; + +template<typename T> +struct Compose : T +{ + std::variant<T, int> v; +}; + +template<typename T> +bool testAlias() +{ + Compose<T> c; + return static_cast<T*>(&c) == &std::get<T>(c.v); +} + +int main() +{ + VERIFY( !testAlias<NonEmpty>() ); + VERIFY( !testAlias<TrivialEmpty>() ); +#if __cplusplus >= 202002L + VERIFY( !testAlias<NonTrivialEmpty>() ); +#else + VERIFY( testAlias<NonTrivialEmpty>() ); +#endif +} diff --git a/libstdc++-v3/testsuite/20_util/variant/87619.cc b/libstdc++-v3/testsuite/20_util/variant/87619.cc index d988925..ac7dd46 100644 --- a/libstdc++-v3/testsuite/20_util/variant/87619.cc +++ b/libstdc++-v3/testsuite/20_util/variant/87619.cc @@ -16,6 +16,7 @@ // <http://www.gnu.org/licenses/>. // { dg-do compile { target c++17 } } +// { dg-options "-ftemplate-depth=270" } #include <variant> #include <utility> @@ -23,6 +24,7 @@ template<std::size_t I> struct S { + ~S() {} }; template <std::size_t... Is> diff --git a/libstdc++-v3/testsuite/20_util/weak_ptr/cons/virtual_bases.cc b/libstdc++-v3/testsuite/20_util/weak_ptr/cons/virtual_bases.cc new file mode 100644 index 0000000..ac3e4bc --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/weak_ptr/cons/virtual_bases.cc @@ -0,0 +1,80 @@ +// { dg-do run { target c++11 } } + +#include <memory> +#include <testsuite_hooks.h> + +struct BaseBase { virtual ~BaseBase() = default; }; +struct Base : BaseBase { virtual ~Base() = default; }; +struct Derived1 : Base { virtual ~Derived1() = default; }; +struct Derived2 : virtual Base { virtual ~Derived2() = default; }; +struct Derived3 : virtual Base { virtual ~Derived3() = default; }; +struct Derived4 : Derived2, Derived3 { virtual ~Derived4() = default; }; +struct Derived5 : Derived4 { virtual ~Derived5() = default; }; + +template<typename T> +void test01() +{ + std::shared_ptr<T> ptr(new T); + VERIFY(ptr); + + std::weak_ptr<T> wptr1 = ptr; + VERIFY(wptr1.lock()); + + std::weak_ptr<Base> wptr2 = ptr; + VERIFY(wptr2.lock()); + + std::weak_ptr<Base> wptr3 = wptr1; + VERIFY(wptr3.lock()); + + std::weak_ptr<BaseBase> wptr4 = ptr; + VERIFY(wptr4.lock()); + + std::weak_ptr<BaseBase> wptr5 = std::move(wptr1); + VERIFY(wptr5.lock()); + + ptr.reset(); + + VERIFY(!wptr1.lock()); + VERIFY(!wptr2.lock()); + VERIFY(!wptr3.lock()); + VERIFY(!wptr4.lock()); + VERIFY(!wptr5.lock()); +} + +template<typename T> +void test02() +{ + std::shared_ptr<T> ptr(new T); + VERIFY(ptr); + + std::weak_ptr<T> wptr1 = ptr; + VERIFY(wptr1.lock()); + + std::weak_ptr<Base> wptr2 = ptr; + VERIFY(wptr2.lock()); + + ptr.reset(); + + std::weak_ptr<Base> wptr3 = wptr1; + std::weak_ptr<BaseBase> wptr4 = wptr1; + std::weak_ptr<BaseBase> wptr5 = std::move(wptr1); + + VERIFY(!wptr1.lock()); + VERIFY(!wptr2.lock()); + VERIFY(!wptr3.lock()); + VERIFY(!wptr4.lock()); + VERIFY(!wptr5.lock()); +} + +int main() +{ + test01<Derived1>(); + test01<Derived2>(); + test01<Derived4>(); + test01<Derived5>(); + + test02<Derived1>(); + test02<Derived2>(); + test02<Derived4>(); + test02<Derived5>(); +} diff --git a/libstdc++-v3/testsuite/20_util/weak_ptr/observers/owner_equal.cc b/libstdc++-v3/testsuite/20_util/weak_ptr/observers/owner_equal.cc new file mode 100644 index 0000000..0217a6e --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/weak_ptr/observers/owner_equal.cc @@ -0,0 +1,52 @@ +// { dg-do run { target c++26 } } +// { dg-require-effective-target hosted } + +// N5008 20.3.2.3.6 weak_ptr observers [util.smartptr.weak.obs] + +#include <memory> +#include <testsuite_hooks.h> + +struct A { }; +struct B { }; + +void +test01() +{ + // test empty weak_ptr owners compare equivalent + std::weak_ptr<A> p1; + std::weak_ptr<B> p2; + VERIFY( p1.owner_equal(p2) && p2.owner_equal(p1) ); + + std::shared_ptr<B> p3; + VERIFY( p1.owner_equal(p3) && p3.owner_equal(p1) ); + + static_assert( noexcept(p1.owner_equal(p1)) ); + static_assert( noexcept(p1.owner_equal(p2)) ); + static_assert( noexcept(p1.owner_equal(p3)) ); + static_assert( noexcept(p2.owner_equal(p1)) ); +} + + +void +test02() +{ + std::shared_ptr<A> a0; + std::weak_ptr<A> w0(a0); + + std::shared_ptr<A> a1(new A); + std::weak_ptr<A> w1(a1); + VERIFY( a1.owner_equal(w1) && w1.owner_equal(a1) ); + VERIFY( !w1.owner_equal(w0) && !w0.owner_equal(w1) ); + VERIFY( !w1.owner_equal(a0) && !a0.owner_equal(w1) ); + + std::shared_ptr<B> b1(new B); + VERIFY( !w1.owner_equal(b1) && !b1.owner_equal(w1) ); +} + +int +main() +{ + test01(); + test02(); + return 0; +} diff --git a/libstdc++-v3/testsuite/20_util/weak_ptr/observers/owner_hash.cc b/libstdc++-v3/testsuite/20_util/weak_ptr/observers/owner_hash.cc new file mode 100644 index 0000000..148a93b --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/weak_ptr/observers/owner_hash.cc @@ -0,0 +1,50 @@ +// { dg-do run { target c++26 } } +// { dg-require-effective-target hosted } + +// N5008 20.3.2.3.6 weak_ptr observers [util.smartptr.weak.obs] + +#include <memory> +#include <testsuite_hooks.h> + +struct A { }; +struct B { }; + +void +test01() +{ + // test empty weak_ptr hashes compare equivalent + std::weak_ptr<A> p1; + std::weak_ptr<B> p2; + VERIFY( p1.owner_hash() == p2.owner_hash() ); + + std::shared_ptr<B> p3; + VERIFY( p1.owner_hash() == p3.owner_hash() ); + + static_assert( noexcept(p1.owner_hash()) ); + static_assert( noexcept(p2.owner_hash()) ); +} + + +void +test02() +{ + std::shared_ptr<A> a0; + std::weak_ptr<A> w0(a0); + + std::shared_ptr<A> a1(new A); + std::weak_ptr<A> w1(a1); + VERIFY( a1.owner_hash() == w1.owner_hash() ); + VERIFY( w1.owner_hash() != w0.owner_hash() ); + VERIFY( w1.owner_hash() != a0.owner_hash() ); + + std::shared_ptr<B> b1(new B); + VERIFY( w1.owner_hash() != b1.owner_hash() ); +} + +int +main() +{ + test01(); + test02(); + return 0; +} diff --git a/libstdc++-v3/testsuite/20_util/weak_ptr/pr118757.cc b/libstdc++-v3/testsuite/20_util/weak_ptr/pr118757.cc new file mode 100644 index 0000000..77757f3 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/weak_ptr/pr118757.cc @@ -0,0 +1,31 @@ +// { dg-do run { target c++20 } } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-require-gthreads "" } +// { dg-require-effective-target hosted } + +#include <memory> +#include <chrono> +#include <thread> +#include <barrier> + +std::shared_ptr<int> s = std::make_shared<int>(42); +std::weak_ptr<int> q = s; +std::atomic<std::weak_ptr<int>> p = q; + +std::barrier bar(2); + +void signaller() +{ + std::this_thread::sleep_for(std::chrono::seconds(1)); + p.store(std::shared_ptr<int>(s, nullptr)); + p.notify_one(); + bar.arrive_and_wait(); +} + +int main(int, char**) +{ + std::thread thr(signaller); + p.wait(q); + bar.arrive_and_wait(); + thr.join(); +} diff --git a/libstdc++-v3/testsuite/20_util/weak_ptr/requirements/1.cc b/libstdc++-v3/testsuite/20_util/weak_ptr/requirements/1.cc deleted file mode 100644 index 04ea837..0000000 --- a/libstdc++-v3/testsuite/20_util/weak_ptr/requirements/1.cc +++ /dev/null @@ -1,33 +0,0 @@ -// { dg-do compile { target c++11 } } -// { dg-require-effective-target hosted } - -#include <memory> -#include <testsuite_tr1.h> - -using namespace __gnu_test; - -void -test01() -{ - std::weak_ptr<ClassType> ptr; - std::weak_ptr<const ClassType> ptr2 = ptr; - -#if __cpp_lib_shared_ptr_arrays >= 201611L - std::weak_ptr<ClassType[10]> ptr_array; - std::weak_ptr<ClassType[]> ptr_array2 = ptr_array; - std::weak_ptr<ClassType const []> ptr_array3 = ptr_array; -#endif -} - -void -test02() -{ - std::weak_ptr<IncompleteClass> ptr; - std::weak_ptr<const IncompleteClass> ptr2 = ptr; - -#if __cpp_lib_shared_ptr_arrays >= 201611L - std::weak_ptr<IncompleteClass[10]> ptr_array; - std::weak_ptr<IncompleteClass[]> ptr_array2 = ptr_array; - std::weak_ptr<IncompleteClass const []> ptr_array3 = ptr_array; -#endif -} diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/operator_plus.cc b/libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/operator_plus.cc index 571f853..92e0569 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/operator_plus.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/allocator/char/operator_plus.cc @@ -17,8 +17,6 @@ // <http://www.gnu.org/licenses/>. // { dg-do run { target c++11 } } -// COW strings don't support C++11 allocator propagation: -// { dg-require-effective-target cxx11_abi } #include <string> #include <testsuite_hooks.h> diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/allocator/wchar_t/operator_plus.cc b/libstdc++-v3/testsuite/21_strings/basic_string/allocator/wchar_t/operator_plus.cc index 0da6843..b75b26a 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/allocator/wchar_t/operator_plus.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/allocator/wchar_t/operator_plus.cc @@ -17,8 +17,6 @@ // <http://www.gnu.org/licenses/>. // { dg-do run { target c++11 } } -// COW strings don't support C++11 allocator propagation: -// { dg-require-effective-target cxx11_abi } #include <string> #include <testsuite_hooks.h> diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/cons/from_range.cc b/libstdc++-v3/testsuite/21_strings/basic_string/cons/from_range.cc index 6331050..df9e4c3 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/cons/from_range.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/cons/from_range.cc @@ -73,16 +73,19 @@ do_test(Alloc alloc) } template<typename Range> -void +constexpr void do_test_a() { do_test<Range>(std::allocator<char>()); - do_test<Range>(__gnu_test::uneq_allocator<char>(42)); do_test<Range>(std::allocator<wchar_t>()); - do_test<Range>(__gnu_test::uneq_allocator<wchar_t>(42)); + + if not consteval { + do_test<Range>(__gnu_test::uneq_allocator<char>(42)); + do_test<Range>(__gnu_test::uneq_allocator<wchar_t>(42)); + } } -bool +constexpr bool test_ranges() { using namespace __gnu_test; @@ -101,9 +104,9 @@ test_ranges() // Not lvalue-convertible to char struct C { - C(char v) : val(v) { } - operator char() && { return val; } - bool operator==(char b) const { return b == val; } + constexpr C(char v) : val(v) { } + constexpr operator char() && { return val; } + constexpr bool operator==(char b) const { return b == val; } char val; }; using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; @@ -112,18 +115,10 @@ test_ranges() return true; } -constexpr bool -test_constexpr() -{ -#if _GLIBCXX_USE_CXX11_ABI - // XXX: this doesn't test the non-forward_range code paths are constexpr. - do_test<std::string_view>(std::allocator<char>()); -#endif // _GLIBCXX_USE_CXX11_ABI - return true; -} - int main() { test_ranges(); - static_assert( test_constexpr() ); +#if _GLIBCXX_USE_CXX11_ABI + static_assert( test_ranges() ); +#endif // _GLIBCXX_USE_CXX11_ABI } diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/modifiers/append/append_range.cc b/libstdc++-v3/testsuite/21_strings/basic_string/modifiers/append/append_range.cc index 6c0bc0c..984db36 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/modifiers/append/append_range.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/modifiers/append/append_range.cc @@ -49,7 +49,7 @@ do_test() } template<typename Range> -void +constexpr void do_test_a() { do_test<Range, std::allocator<char>>(); @@ -58,7 +58,7 @@ do_test_a() do_test<Range, __gnu_test::SimpleAllocator<wchar_t>>(); } -bool +constexpr bool test_ranges() { using namespace __gnu_test; @@ -77,9 +77,9 @@ test_ranges() // Not lvalue-convertible to char struct C { - C(char v) : val(v) { } - operator char() && { return val; } - bool operator==(char b) const { return b == val; } + constexpr C(char v) : val(v) { } + constexpr operator char() && { return val; } + constexpr bool operator==(char b) const { return b == val; } char val; }; using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; @@ -107,19 +107,11 @@ test_overlapping() VERIFY( c == "1234abcd1234" ); } -constexpr bool -test_constexpr() -{ -#if _GLIBCXX_USE_CXX11_ABI - // XXX: this doesn't test the non-forward_range code paths are constexpr. - do_test<std::string_view, std::allocator<char>>(); -#endif // _GLIBCXX_USE_CXX11_ABI - return true; -} - int main() { test_ranges(); test_overlapping(); - static_assert( test_constexpr() ); +#if _GLIBCXX_USE_CXX11_ABI + static_assert( test_ranges() ); +#endif // _GLIBCXX_USE_CXX11_ABI } diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/modifiers/assign/assign_range.cc b/libstdc++-v3/testsuite/21_strings/basic_string/modifiers/assign/assign_range.cc index 310c8bc..aa1b329 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/modifiers/assign/assign_range.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/modifiers/assign/assign_range.cc @@ -41,7 +41,7 @@ do_test() } template<typename Range> -void +constexpr void do_test_a() { do_test<Range, std::allocator<char>>(); @@ -50,7 +50,7 @@ do_test_a() do_test<Range, __gnu_test::SimpleAllocator<wchar_t>>(); } -bool +constexpr bool test_ranges() { using namespace __gnu_test; @@ -69,9 +69,9 @@ test_ranges() // Not lvalue-convertible to char struct C { - C(char v) : val(v) { } - operator char() && { return val; } - bool operator==(char b) const { return b == val; } + constexpr C(char v) : val(v) { } + constexpr operator char() && { return val; } + constexpr bool operator==(char b) const { return b == val; } char val; }; using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; @@ -98,19 +98,11 @@ test_overlapping() VERIFY( c == "1234" ); } -constexpr bool -test_constexpr() -{ -#if _GLIBCXX_USE_CXX11_ABI - // XXX: this doesn't test the non-forward_range code paths are constexpr. - do_test<std::string_view, std::allocator<char>>(); -#endif // _GLIBCXX_USE_CXX11_ABI - return true; -} - int main() { test_ranges(); test_overlapping(); - static_assert( test_constexpr() ); +#if _GLIBCXX_USE_CXX11_ABI + static_assert( test_ranges() ); +#endif // _GLIBCXX_USE_CXX11_ABI } diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/modifiers/insert/insert_range.cc b/libstdc++-v3/testsuite/21_strings/basic_string/modifiers/insert/insert_range.cc index 4fead32..c026fd4 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/modifiers/insert/insert_range.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/modifiers/insert/insert_range.cc @@ -54,7 +54,7 @@ do_test() } template<typename Range> -void +constexpr void do_test_a() { do_test<Range, std::allocator<char>>(); @@ -63,7 +63,7 @@ do_test_a() do_test<Range, __gnu_test::SimpleAllocator<wchar_t>>(); } -bool +constexpr bool test_ranges() { using namespace __gnu_test; @@ -82,9 +82,9 @@ test_ranges() // Not lvalue-convertible to char struct C { - C(char v) : val(v) { } - operator char() && { return val; } - bool operator==(char b) const { return b == val; } + constexpr C(char v) : val(v) { } + constexpr operator char() && { return val; } + constexpr bool operator==(char b) const { return b == val; } char val; }; using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; @@ -112,19 +112,11 @@ test_overlapping() VERIFY( c == "12123434abcd" ); } -constexpr bool -test_constexpr() -{ -#if _GLIBCXX_USE_CXX11_ABI - // XXX: this doesn't test the non-forward_range code paths are constexpr. - do_test<std::string_view, std::allocator<char>>(); -#endif // _GLIBCXX_USE_CXX11_ABI - return true; -} - int main() { test_ranges(); test_overlapping(); - static_assert( test_constexpr() ); +#if _GLIBCXX_USE_CXX11_ABI + static_assert( test_ranges() ); +#endif // _GLIBCXX_USE_CXX11_ABI } diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/modifiers/replace/replace_with_range.cc b/libstdc++-v3/testsuite/21_strings/basic_string/modifiers/replace/replace_with_range.cc index 9acf11a..4c6bba5 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/modifiers/replace/replace_with_range.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/modifiers/replace/replace_with_range.cc @@ -54,7 +54,7 @@ do_test() } template<typename Range> -void +constexpr void do_test_a() { do_test<Range, std::allocator<char>>(); @@ -63,7 +63,7 @@ do_test_a() do_test<Range, __gnu_test::SimpleAllocator<wchar_t>>(); } -bool +constexpr bool test_ranges() { using namespace __gnu_test; @@ -82,9 +82,9 @@ test_ranges() // Not lvalue-convertible to char struct C { - C(char v) : val(v) { } - operator char() && { return val; } - bool operator==(char b) const { return b == val; } + constexpr C(char v) : val(v) { } + constexpr operator char() && { return val; } + constexpr bool operator==(char b) const { return b == val; } char val; }; using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; @@ -115,19 +115,11 @@ test_overlapping() VERIFY( c == "12123434abcd" ); } -constexpr bool -test_constexpr() -{ -#if _GLIBCXX_USE_CXX11_ABI - // XXX: this doesn't test the non-forward_range code paths are constexpr. - do_test<std::string_view, std::allocator<char>>(); -#endif // _GLIBCXX_USE_CXX11_ABI - return true; -} - int main() { test_ranges(); test_overlapping(); - static_assert( test_constexpr() ); +#if _GLIBCXX_USE_CXX11_ABI + static_assert( test_ranges() ); +#endif // _GLIBCXX_USE_CXX11_ABI } diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stoi.cc b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stoi.cc index 9fc143c..4d2a8ba 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stoi.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stoi.cc @@ -86,7 +86,7 @@ test01() } VERIFY( test ); VERIFY( i1 == 7 ); - VERIFY( idx1 = 1 ); + VERIFY( idx1 == 1 ); try { diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stol.cc b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stol.cc index 85d5e6d..55e39dd 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stol.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stol.cc @@ -86,7 +86,7 @@ test01() } VERIFY( test ); VERIFY( l1 == 7 ); - VERIFY( idx1 = 1 ); + VERIFY( idx1 == 1 ); try { diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stold.cc b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stold.cc index b64ad0c..dd777c4 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stold.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stold.cc @@ -31,6 +31,11 @@ void test01() { + /* If these conditions are not met, basic_string.h doesn't define + std::stold(const string&, size_t* = 0), and then the test would + fail to compile. */ +#if (_GLIBCXX_HAVE_STRTOLD && ! _GLIBCXX_HAVE_BROKEN_STRTOLD) \ + || __DBL_MANT_DIG__ == __LDBL_MANT_DIG__ bool test = false; using namespace std; @@ -106,6 +111,7 @@ test01() test = false; } VERIFY( test ); +#endif } int main() diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stoll.cc b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stoll.cc index a698bae..5b23d6c 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stoll.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stoll.cc @@ -87,7 +87,7 @@ test01() } VERIFY( test ); VERIFY( ll1 == 7 ); - VERIFY( idx1 = 1 ); + VERIFY( idx1 == 1 ); try { diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stoul.cc b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stoul.cc index 47c20ce..00afc94 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stoul.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stoul.cc @@ -86,7 +86,7 @@ test01() } VERIFY( test ); VERIFY( ul1 == 7 ); - VERIFY( idx1 = 1 ); + VERIFY( idx1 == 1 ); try { diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stoull.cc b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stoull.cc index ad81b67..f44eb1d 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stoull.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/char/stoull.cc @@ -87,7 +87,7 @@ test01() } VERIFY( test ); VERIFY( ull1 == 7 ); - VERIFY( idx1 = 1 ); + VERIFY( idx1 == 1 ); try { diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/stoi.cc b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/stoi.cc index 88e1da7..1aec77f 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/stoi.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/stoi.cc @@ -88,7 +88,7 @@ test01() } VERIFY( test ); VERIFY( i1 == 7 ); - VERIFY( idx1 = 1 ); + VERIFY( idx1 == 1 ); try { diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/stol.cc b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/stol.cc index 98388f9..41e9d66 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/stol.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/stol.cc @@ -88,7 +88,7 @@ test01() } VERIFY( test ); VERIFY( l1 == 7 ); - VERIFY( idx1 = 1 ); + VERIFY( idx1 == 1 ); try { diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/stoll.cc b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/stoll.cc index d7a49f5..5c7a25b 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/stoll.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/stoll.cc @@ -88,7 +88,7 @@ test01() } VERIFY( test ); VERIFY( ll1 == 7 ); - VERIFY( idx1 = 1 ); + VERIFY( idx1 == 1 ); try { diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/stoul.cc b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/stoul.cc index b553796..d33289a 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/stoul.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/stoul.cc @@ -88,7 +88,7 @@ test01() } VERIFY( test ); VERIFY( ul1 == 7 ); - VERIFY( idx1 = 1 ); + VERIFY( idx1 == 1 ); try { diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/stoull.cc b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/stoull.cc index 503101f..67c9639 100644 --- a/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/stoull.cc +++ b/libstdc++-v3/testsuite/21_strings/basic_string/numeric_conversions/wchar_t/stoull.cc @@ -88,7 +88,7 @@ test01() } VERIFY( test ); VERIFY( ull1 == 7 ); - VERIFY( idx1 = 1 ); + VERIFY( idx1 == 1 ); try { diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/char.cc b/libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/char.cc new file mode 100644 index 0000000..c384948 --- /dev/null +++ b/libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/char.cc @@ -0,0 +1,46 @@ +// { dg-do run { target c++26 } } + +#include <stdexcept> +#include <string> +#include <string_view> +#include <testsuite_hooks.h> + +void test01(void) { + typedef std::string::size_type csize_type; + typedef std::string::const_reference cref; + typedef std::string::reference ref; + csize_type csz01; + + const char str_lit01[] = "rockaway, pacifica"; + const std::string str01(str_lit01); + std::string_view str02; + + csz01 = str01.size(); + str02 = str01.subview(0, 1); + VERIFY(str02 == "r"); + str02 = str01.subview(10); + VERIFY(str02 == "pacifica"); + + try { + str02 = str01.subview(csz01 + 1); + VERIFY(false); + } catch (std::out_of_range &fail) { + VERIFY(true); + } catch (...) { + VERIFY(false); + } + + try { + str02 = str01.subview(csz01); + VERIFY(str02.size() == 0); + } catch (std::out_of_range &fail) { + VERIFY(false); + } catch (...) { + VERIFY(false); + } +} + +int main() { + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/wchar_t.cc b/libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/wchar_t.cc new file mode 100644 index 0000000..3b8e6a8 --- /dev/null +++ b/libstdc++-v3/testsuite/21_strings/basic_string/operations/subview/wchar_t.cc @@ -0,0 +1,46 @@ +// { dg-do run { target c++26 } } + +#include <stdexcept> +#include <string> +#include <string_view> +#include <testsuite_hooks.h> + +void test01(void) { + typedef std::wstring::size_type csize_type; + typedef std::wstring::const_reference cref; + typedef std::wstring::reference ref; + csize_type csz01; + + const wchar_t str_lit01[] = L"rockaway, pacifica"; + const std::wstring str01(str_lit01); + std::wstring_view str02; + + csz01 = str01.size(); + str02 = str01.subview(0, 1); + VERIFY(str02 == L"r"); + str02 = str01.subview(10); + VERIFY(str02 == L"pacifica"); + + try { + str02 = str01.subview(csz01 + 1); + VERIFY(false); + } catch (std::out_of_range &fail) { + VERIFY(true); + } catch (...) { + VERIFY(false); + } + + try { + str02 = str01.subview(csz01); + VERIFY(str02.size() == 0); + } catch (std::out_of_range &fail) { + VERIFY(false); + } catch (...) { + VERIFY(false); + } +} + +int main() { + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/char.cc b/libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/char.cc new file mode 100644 index 0000000..d6b66e6 --- /dev/null +++ b/libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/char.cc @@ -0,0 +1,51 @@ +// { dg-do run { target c++26 } } + +#include <string_view> +#include <testsuite_hooks.h> + +#if __STDC_HOSTED__ +#include <stdexcept> +#endif + +void test01() { + typedef std::string_view::size_type csize_type; + typedef std::string_view::const_reference cref; + typedef std::string_view::reference ref; + csize_type csz01; + + const char str_lit01[] = "rockaway, pacifica"; + const std::string_view str01(str_lit01); + std::string_view str02; + + csz01 = str01.size(); + str02 = str01.subview(0, 1); + VERIFY(str02 == "r"); + str02 = str01.subview(10); + VERIFY(str02 == "pacifica"); + +#if __STDC_HOSTED__ + try { + str02 = str01.subview(csz01 + 1); + VERIFY(false); + } catch (std::out_of_range &fail) { + VERIFY(true); + } catch (...) { + VERIFY(false); + } + + try { + str02 = str01.subview(csz01); + VERIFY(str02.size() == 0); + VERIFY(str02.begin() == str01.end()); + VERIFY(true); + } catch (...) { + VERIFY(false); + } +#endif // HOSTED +} + +int main() { + test01(); + + return 0; +} diff --git a/libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/wchar_t.cc b/libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/wchar_t.cc new file mode 100644 index 0000000..86b5095 --- /dev/null +++ b/libstdc++-v3/testsuite/21_strings/basic_string_view/operations/subview/wchar_t.cc @@ -0,0 +1,51 @@ +// { dg-do run { target c++26 } } + +#include <string_view> +#include <testsuite_hooks.h> + +#if __STDC_HOSTED__ +#include <stdexcept> +#endif + +void test01() { + typedef std::wstring_view::size_type csize_type; + typedef std::wstring_view::const_reference cref; + typedef std::wstring_view::reference ref; + csize_type csz01; + + const wchar_t str_lit01[] = L"rockaway, pacifica"; + const std::wstring_view str01(str_lit01); + std::wstring_view str02; + + csz01 = str01.size(); + str02 = str01.subview(0, 1); + VERIFY(str02 == L"r"); + str02 = str01.subview(10); + VERIFY(str02 == L"pacifica"); + +#if __STDC_HOSTED__ + try { + str02 = str01.subview(csz01 + 1); + VERIFY(false); + } catch (std::out_of_range &fail) { + VERIFY(true); + } catch (...) { + VERIFY(false); + } + + try { + str02 = str01.subview(csz01); + VERIFY(str02.size() == 0); + VERIFY(str02.begin() == str01.end()); + VERIFY(true); + } catch (...) { + VERIFY(false); + } +#endif // HOSTED +} + +int main() { + test01(); + + return 0; +} diff --git a/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode.h b/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode.h index 9fe9ac8..2ba23bf 100644 --- a/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode.h +++ b/libstdc++-v3/testsuite/22_locale/codecvt/codecvt_unicode.h @@ -1439,7 +1439,7 @@ ucs2_to_utf8_out_error (const std::codecvt<InternT, ExternT, mbstate_t> &cvt) // make the trailing surrogate a BMP char {5, 10, 3, 6, u'z', 4}, - // don't replace anything in the test cases bellow, just show the surrogate + // don't replace anything in the test cases below, just show the surrogate // pair (fourth CP) fully or partially {5, 10, 3, 6, u'b', 0}, {5, 7, 3, 6, u'b', 0}, // no space for fourth CP @@ -2072,7 +2072,7 @@ utf16_to_ucs2_in_error (const std::codecvt<InternT, char, mbstate_t> &cvt, // make the trailing surrogate a BMP char {10, 5, 6, 3, u'z', 4}, - // don't replace anything in the test cases bellow, just show the surrogate + // don't replace anything in the test cases below, just show the surrogate // pair (fourth CP) fully or partially (just the first surrogate) {10, 5, 6, 3, u'b', 0}, {8, 5, 6, 3, u'b', 0}, diff --git a/libstdc++-v3/testsuite/22_locale/ctype/is/string/89728_neg.cc b/libstdc++-v3/testsuite/22_locale/ctype/is/string/89728_neg.cc index a34b2ae..24aba99 100644 --- a/libstdc++-v3/testsuite/22_locale/ctype/is/string/89728_neg.cc +++ b/libstdc++-v3/testsuite/22_locale/ctype/is/string/89728_neg.cc @@ -18,7 +18,6 @@ // <http://www.gnu.org/licenses/>. // { dg-error "invalid use of incomplete type" "" { target *-*-* } 0 } -// { dg-error "invalid 'static_cast'" "" { target c++98_only } 0 } #include <locale> diff --git a/libstdc++-v3/testsuite/22_locale/num_put/put/char/lwg4084.cc b/libstdc++-v3/testsuite/22_locale/num_put/put/char/lwg4084.cc index b7c7da1..6ce4e8f 100644 --- a/libstdc++-v3/testsuite/22_locale/num_put/put/char/lwg4084.cc +++ b/libstdc++-v3/testsuite/22_locale/num_put/put/char/lwg4084.cc @@ -20,7 +20,11 @@ test_nan() out << ' ' << nan << ' ' << -nan; out << std::showpos; out << ' ' << nan << ' ' << -nan; +#ifdef _AIX // non-conforming + VERIFY( out.str() == " NaNQ -NaNQ NaNQ -NaNQ NaNQ -NaNQ +NaNQ -NaNQ" ); +#else VERIFY( out.str() == " nan -nan NAN -NAN NAN -NAN +NAN -NAN" ); +#endif } void @@ -36,7 +40,11 @@ test_inf() out << ' ' << inf << ' ' << -inf; out << std::showpos; out << ' ' << inf << ' ' << -inf; +#ifdef _AIX // non-conforming + VERIFY( out.str() == " INF -INF INF -INF INF -INF +INF -INF" ); +#else VERIFY( out.str() == " inf -inf INF -INF INF -INF +INF -INF" ); +#endif } int main() diff --git a/libstdc++-v3/testsuite/22_locale/time_get/get/char/3.cc b/libstdc++-v3/testsuite/22_locale/time_get/get/char/3.cc index 48a5f12..49bcd2a 100644 --- a/libstdc++-v3/testsuite/22_locale/time_get/get/char/3.cc +++ b/libstdc++-v3/testsuite/22_locale/time_get/get/char/3.cc @@ -226,7 +226,7 @@ test01() format = "%e"; ret = tget.get(iter(iss), end, iss, err, &time, format.data(), format.data()+format.size()); - VERIFY( err == ios_base::failbit|ios_base::eofbit ); + VERIFY( err == (ios_base::failbit|ios_base::eofbit) ); VERIFY( ret == end ); iss.str("35"); diff --git a/libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/3.cc b/libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/3.cc index b7eb5bc..4b1b6e4 100644 --- a/libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/3.cc +++ b/libstdc++-v3/testsuite/22_locale/time_get/get/wchar_t/3.cc @@ -226,7 +226,7 @@ test01() format = L"%e"; ret = tget.get(iter(iss), end, iss, err, &time, format.data(), format.data()+format.size()); - VERIFY( err == ios_base::failbit|ios_base::eofbit ); + VERIFY( err == (ios_base::failbit|ios_base::eofbit) ); VERIFY( ret == end ); iss.str(L"35"); diff --git a/libstdc++-v3/testsuite/23_containers/array/creation/3_neg.cc b/libstdc++-v3/testsuite/23_containers/array/creation/3_neg.cc index ae06302..1335228 100644 --- a/libstdc++-v3/testsuite/23_containers/array/creation/3_neg.cc +++ b/libstdc++-v3/testsuite/23_containers/array/creation/3_neg.cc @@ -54,3 +54,5 @@ test03() } // { dg-prune-output "static assertion failed" } +// { dg-prune-output "use of deleted function" } +// { dg-prune-output "could not convert" } diff --git a/libstdc++-v3/testsuite/23_containers/array/tuple_interface/get_neg.cc b/libstdc++-v3/testsuite/23_containers/array/tuple_interface/get_neg.cc index 25511e7..e1e9ce9b 100644 --- a/libstdc++-v3/testsuite/23_containers/array/tuple_interface/get_neg.cc +++ b/libstdc++-v3/testsuite/23_containers/array/tuple_interface/get_neg.cc @@ -26,6 +26,6 @@ int n1 = std::get<1>(a); int n2 = std::get<1>(std::move(a)); int n3 = std::get<1>(ca); -// { dg-error "static assertion failed" "" { target *-*-* } 394 } -// { dg-error "static assertion failed" "" { target *-*-* } 403 } -// { dg-error "static assertion failed" "" { target *-*-* } 412 } +// { dg-error "static assertion failed" "" { target *-*-* } 396 } +// { dg-error "static assertion failed" "" { target *-*-* } 405 } +// { dg-error "static assertion failed" "" { target *-*-* } 414 } diff --git a/libstdc++-v3/testsuite/23_containers/bitset/lwg4294.cc b/libstdc++-v3/testsuite/23_containers/bitset/lwg4294.cc new file mode 100644 index 0000000..977555f --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/bitset/lwg4294.cc @@ -0,0 +1,11 @@ +// { dg-do compile { target c++11 } } + +// Bug 121046 +// Asking is_constructible_v<std::bitset<1>, NonTrivial*> is ill-formed + +// LWG 4294. bitset(const CharT*) constructor needs to be constrained + +#include <bitset> +struct NonTrivial { ~NonTrivial() { } }; +static_assert( ! std::is_constructible<std::bitset<1>, NonTrivial*>::value, + "std::bitset cannot be constructed from this pointer" ); diff --git a/libstdc++-v3/testsuite/23_containers/deque/capacity/shrink_to_fit.cc b/libstdc++-v3/testsuite/23_containers/deque/capacity/shrink_to_fit.cc index 7cb6707..6371755 100644 --- a/libstdc++-v3/testsuite/23_containers/deque/capacity/shrink_to_fit.cc +++ b/libstdc++-v3/testsuite/23_containers/deque/capacity/shrink_to_fit.cc @@ -1,4 +1,5 @@ // { dg-do run { target c++11 } } +// { dg-require-effective-target std_allocator_new } // 2010-01-08 Paolo Carlini <paolo.carlini@oracle.com> @@ -19,18 +20,42 @@ // with this library; see the file COPYING3. If not see // <http://www.gnu.org/licenses/>. -#include <vector> +#include <deque> #include <testsuite_hooks.h> +#include <replacement_memory_operators.h> // libstdc++/42573 void test01() { - std::vector<int> d(100); - d.push_back(1); - d.push_back(1); - // VERIFY( d.size() < d.capacity() ); + using namespace std; + __gnu_test::counter::reset(); + + const size_t buf_size = _GLIBCXX_STD_C::__deque_buf_size(sizeof(size_t)); + deque<size_t> d; + for (size_t i = 0; i != buf_size; ++i) + d.push_back(i); + + // No shrink if 1st buffer is full, create some front capacity. + d.pop_front(); + + // 1 node array allocation + 2 node allocation = 3. + VERIFY( __gnu_test::counter::count() == 3 ); + VERIFY( __gnu_test::counter::get()._M_increments == 3 ); + d.shrink_to_fit(); - // VERIFY( d.size() == d.capacity() ); + + // No reallocation if no exception support, shrink_to_fit is then a + // no-op. +#if __cpp_exceptions + // 1 node array allocation + 1 node allocation = 2. + const int expected_count = 2; + const int expected_increments = 2; +#else + const int expected_count = 3; + const int expected_increments = 0; +#endif + VERIFY( __gnu_test::counter::count() == expected_count ); + VERIFY( __gnu_test::counter::get()._M_increments == 3 + expected_increments ); } int main() diff --git a/libstdc++-v3/testsuite/23_containers/deque/debug/erase.cc b/libstdc++-v3/testsuite/23_containers/deque/debug/erase.cc new file mode 100644 index 0000000..d8c36bb --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/deque/debug/erase.cc @@ -0,0 +1,27 @@ +// { dg-do run { target c++20 } } +// { dg-require-debug-mode "" } + +#include <deque> +#include <testsuite_hooks.h> + +void test01() +{ + std::deque<int> d; + + for (int i = 0; i != 10; ++i) + d.push_back(i); + + auto before = d.begin() + 4; + auto last = d.end() - 1; + + VERIFY( std::erase(d, 6) == 1 ); + + VERIFY(before._M_dereferenceable()); + VERIFY(last._M_singular()); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/deque/debug/invalidation/erase.cc b/libstdc++-v3/testsuite/23_containers/deque/debug/invalidation/erase.cc new file mode 100644 index 0000000..c18a5ff --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/deque/debug/invalidation/erase.cc @@ -0,0 +1,28 @@ +// { dg-do run { target c++20 } } + +#include <debug/deque> +#include <testsuite_hooks.h> + +using __gnu_debug::deque; + +void test01() +{ + deque<int> d; + + for (int i = 0; i != 10; ++i) + d.push_back(i); + + auto before = d.begin() + 4; + auto last = d.end() -1; + + VERIFY( std::erase(d, 6) == 1 ); + + VERIFY(before._M_dereferenceable()); + VERIFY(last._M_singular()); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/deque/modifiers/emplace/118087.cc b/libstdc++-v3/testsuite/23_containers/deque/modifiers/emplace/118087.cc new file mode 100644 index 0000000..3606e88 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/deque/modifiers/emplace/118087.cc @@ -0,0 +1,43 @@ +// { dg-do run { target c++11 } } + +// PR libstdc++/118087 +// std::deque::emplace does not do uses-allocator construction + +#include <deque> +#include <scoped_allocator> +#include <testsuite_hooks.h> +#include <testsuite_allocator.h> + +template<typename T> +using Alloc = __gnu_test::propagating_allocator<T, true>; + +struct X +{ + using allocator_type = Alloc<int>; + X() { } + X(const X&) { } + X(X&&) { } + X(const allocator_type& a) : alloc(a) { } + X(const X&, const allocator_type& a) : alloc(a) { } + X(X&&, const allocator_type& a) : alloc(a) { } + + X& operator=(const X&) = default; + + allocator_type alloc{-1}; +}; + +int main() +{ + std::deque<X, std::scoped_allocator_adaptor<Alloc<X>>> d(2, Alloc<X>(50)); + VERIFY(d[0].alloc.get_personality() == 50); + VERIFY(d[1].alloc.get_personality() == 50); + + d.emplace(d.begin() + 1); + VERIFY(d[1].alloc.get_personality() == 50); + + d.emplace_front(); + VERIFY(d[0].alloc.get_personality() == 50); + + d.emplace_back(); + VERIFY(d[d.size() - 1].alloc.get_personality() == 50); +} diff --git a/libstdc++-v3/testsuite/23_containers/flat_map/1.cc b/libstdc++-v3/testsuite/23_containers/flat_map/1.cc index d9d88c4..5bcda34 100644 --- a/libstdc++-v3/testsuite/23_containers/flat_map/1.cc +++ b/libstdc++-v3/testsuite/23_containers/flat_map/1.cc @@ -1,4 +1,5 @@ // { dg-do run { target c++23 } } +// { dg-timeout-factor 2 } #include <flat_map> @@ -55,16 +56,17 @@ test_deduction_guide() std::vector<long, __gnu_test::SimpleAllocator<long>>, std::vector<float, __gnu_test::SimpleAllocator<float>>>>); - // LWG4223: deduces flat_map<long, float const>, which in turn instantiates - // std::vector<cosnt float> that is ill-formed. - // __gnu_test::test_input_range<std::pair<const long, const float>> r2(0, 0); - // std::flat_map it5(r2.begin(), r2.begin()); - // std::flat_map fr5(std::from_range, r2); + __gnu_test::test_input_range<std::pair<const long, const float>> r2(0, 0); + std::flat_map it5(r2.begin(), r2.begin()); + static_assert(std::is_same_v<decltype(it5), std::flat_map<long, float>>); + std::flat_map fr5(std::from_range, r2); + static_assert(std::is_same_v<decltype(fr5), std::flat_map<long, float>>); - // LWG4223: deduces flat_map<const long&, float&> - //__gnu_test::test_input_range<std::pair<const long&, float&>> r3(0, 0); - // std::flat_map it6(r3.begin(), r3.begin()); - // std::flat_map fr6(std::from_range, r3); + __gnu_test::test_input_range<std::pair<const long&, float&>> r3(0, 0); + std::flat_map it6(r3.begin(), r3.begin()); + static_assert(std::is_same_v<decltype(it6), std::flat_map<long, float>>); + std::flat_map fr6(std::from_range, r3); + static_assert(std::is_same_v<decltype(fr6), std::flat_map<long, float>>); __gnu_test::test_input_range<std::tuple<long, float>> r4(0, 0); std::flat_map it7(r4.begin(), r4.begin()); @@ -241,6 +243,37 @@ test06() VERIFY( std::ranges::equal(m | std::views::values, (int[]){2, 3, 4, 5, 6}) ); } +void +test07() +{ + // PR libstdc++/119427 - std::erase_if(std::flat_foo) does not work + // PR libstdc++/120465 - erase_if for flat_map calls predicate with incorrect type + std::flat_map<int, int> m = {std::pair{1, 2}, {3, 4}, {5, 6}}; + auto n = std::erase_if(m, [](auto x) { return x.first == 1 || x.second == 6; }); + VERIFY( n == 2 ); + VERIFY( std::ranges::equal(m, (std::pair<int,int>[]){{3,4}}) ); +} + +void +test08() +{ + // PR libstdc++/120432 - flat_map operator[] is broken for const lvalue keys + std::flat_map<int, int> m; + const int k = 42; + m[k] = 0; +} + +void +test09() +{ + // PR libstdc++/122921 - The value_type of flat_map's iterator should be + // pair<Key, T> instead of pair<const Key, T> + using type = std::flat_map<int, int>; + using value_type = std::ranges::range_value_t<type>; + using value_type = type::value_type; + using value_type = std::pair<int, int>; +} + int main() { @@ -253,4 +286,7 @@ main() test04(); test05(); test06(); + test07(); + test08(); + test09(); } diff --git a/libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc b/libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc index ff180bf..f143f3a 100644 --- a/libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc +++ b/libstdc++-v3/testsuite/23_containers/flat_multimap/1.cc @@ -1,4 +1,5 @@ // { dg-do run { target c++23 } } +// { dg-timeout-factor 2 } #include <flat_map> #include <deque> @@ -53,16 +54,17 @@ test_deduction_guide() std::vector<long, __gnu_test::SimpleAllocator<long>>, std::vector<float, __gnu_test::SimpleAllocator<float>>>>); - // LWG4223: deduces flat_multimap<long, float const>, which in turn instantiates - // std::vector<cosnt float> that is ill-formed. - // __gnu_test::test_input_range<std::pair<const long, const float>> r2(0, 0); - // std::flat_multimap it5(r2.begin(), r2.begin()); - // std::flat_multimap fr5(std::from_range, r2); + __gnu_test::test_input_range<std::pair<const long, const float>> r2(0, 0); + std::flat_multimap it5(r2.begin(), r2.begin()); + static_assert(std::is_same_v<decltype(it5), std::flat_multimap<long, float>>); + std::flat_multimap fr5(std::from_range, r2); + static_assert(std::is_same_v<decltype(fr5), std::flat_multimap<long, float>>); - // LWG4223: deduces flat_multimap<const long&, float&> - //__gnu_test::test_input_range<std::pair<const long&, float&>> r3(0, 0); - // std::flat_multimap it6(r3.begin(), r3.begin()); - // std::flat_multimap fr6(std::from_range, r3); + __gnu_test::test_input_range<std::pair<const long&, float&>> r3(0, 0); + std::flat_multimap it6(r3.begin(), r3.begin()); + static_assert(std::is_same_v<decltype(it6), std::flat_multimap<long, float>>); + std::flat_multimap fr6(std::from_range, r3); + static_assert(std::is_same_v<decltype(fr6), std::flat_multimap<long, float>>); __gnu_test::test_input_range<std::tuple<long, float>> r4(0, 0); std::flat_multimap it7(r4.begin(), r4.begin()); @@ -219,6 +221,28 @@ test06() VERIFY( std::ranges::equal(m | std::views::values, (int[]){2, 3, 4, 5, 6}) ); } +void +test07() +{ + // PR libstdc++/119427 - std::erase_if(std::flat_foo) does not work + // PR libstdc++/120465 - erase_if for flat_map calls predicate with incorrect type + std::flat_multimap<int, int> m = {std::pair{1, 2}, {3, 4}, {3, 3}, {5, 6}, {6, 6}}; + auto n = std::erase_if(m, [](auto x) { return x.first == 1 || x.second == 6; }); + VERIFY( n == 3 ); + VERIFY( std::ranges::equal(m, (std::pair<int,int>[]){{3,4},{3,3}}) ); +} + +void +test09() +{ + // PR libstdc++/122921 - The value_type of flat_map's iterator should be + // pair<Key, T> instead of pair<const Key, T> + using type = std::flat_multimap<int, int>; + using value_type = std::ranges::range_value_t<type>; + using value_type = type::value_type; + using value_type = std::pair<int, int>; +} + int main() { @@ -231,4 +255,6 @@ main() test04(); test05(); test06(); + test07(); + test09(); } diff --git a/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc b/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc index 7d9a33c..63855e0 100644 --- a/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc +++ b/libstdc++-v3/testsuite/23_containers/flat_multiset/1.cc @@ -235,6 +235,16 @@ test08() VERIFY( copy_counter == 2 ); } +void +test09() +{ + // PR libstdc++/119427 - std::erase_if(std::flat_foo) does not work + std::flat_multiset<int> s = {1,1,2,2,3,4,5}; + auto n = std::erase_if(s, [](int x) { return x % 2 != 0; }); + VERIFY( n == 4 ); + VERIFY( std::ranges::equal(s, (int[]){2,2,4}) ); +} + int main() { @@ -247,4 +257,5 @@ main() test06(); test07(); test08(); + test09(); } diff --git a/libstdc++-v3/testsuite/23_containers/flat_set/1.cc b/libstdc++-v3/testsuite/23_containers/flat_set/1.cc index ed24fab..b1d9002 100644 --- a/libstdc++-v3/testsuite/23_containers/flat_set/1.cc +++ b/libstdc++-v3/testsuite/23_containers/flat_set/1.cc @@ -248,6 +248,16 @@ test08() VERIFY( copy_counter == 1 ); } +void +test09() +{ + // PR libstdc++/119427 - std::erase_if(std::flat_foo) does not work + std::flat_set<int> s = {1,2,3,4,5}; + auto n = std::erase_if(s, [](int x) { return x % 2 != 0; }); + VERIFY( n == 3 ); + VERIFY( std::ranges::equal(s, (int[]){2,4}) ); +} + int main() { @@ -260,4 +270,5 @@ main() test06(); test07(); test08(); + test09(); } diff --git a/libstdc++-v3/testsuite/23_containers/forward_list/48101_neg.cc b/libstdc++-v3/testsuite/23_containers/forward_list/48101_neg.cc index 2f2ea2a..d18195e 100644 --- a/libstdc++-v3/testsuite/23_containers/forward_list/48101_neg.cc +++ b/libstdc++-v3/testsuite/23_containers/forward_list/48101_neg.cc @@ -26,6 +26,5 @@ test01() } // { dg-error "non-const, non-volatile value_type" "" { target *-*-* } 0 } -// { dg-prune-output "std::allocator<.* has no member named " } // { dg-prune-output "must have the same value_type as its allocator" } // { dg-prune-output "rebind_alloc" } diff --git a/libstdc++-v3/testsuite/23_containers/forward_list/modifiers/122661.cc b/libstdc++-v3/testsuite/23_containers/forward_list/modifiers/122661.cc new file mode 100644 index 0000000..41ac32c --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/forward_list/modifiers/122661.cc @@ -0,0 +1,20 @@ +// { dg-do compile { target c++11 } } + +// Bug 122661 - Incorrect value category handling in forward_list::assign + +#include <forward_list> + +struct S +{ + S(); + S& operator=(S const&) & = delete; + S& operator=(S const&) &&; +}; + +void +test_pr122661() +{ + std::forward_list<S> fl; + S* iter = nullptr; + fl.assign(iter, iter); +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/access/capacity.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/access/capacity.cc new file mode 100644 index 0000000..2797e20 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/access/capacity.cc @@ -0,0 +1,51 @@ +// { dg-do run { target c++26 } } + +#include <inplace_vector> + +#include <span> +#include <testsuite_hooks.h> + +template<size_t N, typename T> +constexpr void +test_reserve() +{ + std::inplace_vector<T, N> v; + + static_assert(v.max_size() == N); + static_assert(v.capacity() == N); + + // static methods + v.shrink_to_fit(); + v.reserve(0); + v.reserve(N); + +#ifdef __cpp_exceptions +#ifdef __cpp_lib_constexpr_exceptions +#error remove the consteval check +#endif + if consteval { + return; + } + + try + { + v.reserve(N + 2); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } +#endif +} + +int main() +{ + auto test_all = [] { + test_reserve<0, int>(); + test_reserve<4, int>(); + return true; + }; + + test_all(); + static_assert(test_all());; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/access/elem.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/access/elem.cc new file mode 100644 index 0000000..bc06aa0 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/access/elem.cc @@ -0,0 +1,115 @@ +// { dg-do run { target c++26 } } + +#include <inplace_vector> + +#include <span> +#include <stdexcept> +#include <testsuite_hooks.h> +#include <utility> + +template<size_t N, typename T> +constexpr void +test_out_of_capacity() +{ + std::inplace_vector<T, N> v; + +#ifdef __cpp_exceptions +#ifdef __cpp_lib_constexpr_exceptions +#error remove the consteval check +#endif + if consteval { + return; + } + + try + { + (void)v.at(N + 2); + VERIFY(false); + } + catch (std::out_of_range const&) + { + } + + try + { + (void)as_const(v).at(N + 2); + VERIFY(false); + } + catch (std::out_of_range const&) + { + } +#endif +} + + +template<bool Const, typename T, size_t N> +using InplaceVector = std::conditional_t<Const, + const std::inplace_vector<T, N>, + std::inplace_vector<T, N>>; + +template<bool Const, typename T> +constexpr void +test_access() +{ + InplaceVector<Const, T, 10> v{1, 2, 3, 4, 5}; + + auto& e0a = v[0]; + auto& e0b = v.at(0); + auto& e0c = v.front(); + VERIFY( &e0a == &e0b ); + VERIFY( &e0a == &e0c ); + VERIFY( &e0a == &v.begin()[0] ); + VERIFY( &e0a == &v.cbegin()[0] ); + VERIFY( &e0a == v.data() ); + VERIFY( e0a == T(1) ); + + auto& e3a = v[2]; + auto& e3b = v.at(2); + VERIFY( &e3a == &e3b ); + VERIFY( &e3a == &v.begin()[2] ); + VERIFY( &e3a == &v.cbegin()[2] ); + VERIFY( &e3a == v.data() + 2 ); + VERIFY( e3a == T(3) ); + + auto& e4a = v[4]; + auto& e4b = v.at(4); + auto& e4c = v.back(); + VERIFY( &e4a == &e4b ); + VERIFY( &e4a == &e4c ); + VERIFY( &e4a == &v.begin()[4] ); + VERIFY( &e4a == &v.cbegin()[4] ); + VERIFY( &e4a == v.data() + 4 ); + VERIFY( e4a == T(5) ); + +#ifdef __cpp_exceptions +#ifdef __cpp_lib_constexpr_exceptions +#error remove the consteval check +#endif + if consteval { + return; + } + + try + { + (void)v.at(7); + VERIFY(false); + } + catch (std::out_of_range const&) + { + } +#endif +} + +int main() +{ + auto test_all = [] { + test_out_of_capacity<0, int>(); + test_out_of_capacity<4, int>(); + test_access<true, int>(); + test_access<false, int>(); + return true; + }; + + test_all(); + static_assert(test_all());; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/access/elem_neg.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/access/elem_neg.cc new file mode 100644 index 0000000..b2bff0d --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/access/elem_neg.cc @@ -0,0 +1,65 @@ +// { dg-do compile { target c++26 } } + +#include <inplace_vector> + +template<bool Const, typename T, size_t N> +using InplaceVector + = std::conditional_t<Const, + const std::inplace_vector<T, N>, + std::inplace_vector<T, N>>; + +template<bool Const, size_t N, typename T> +constexpr bool +test_front_on_empty() +{ + InplaceVector<Const, T, N> v; + (void)v.front(); // { dg-error "in 'constexpr' expansion of" } + return true; +} + +template<bool Const, size_t N, typename T> +constexpr bool +test_back_on_empty() +{ + InplaceVector<Const, T, N> v; + (void)v.back(); // { dg-error "in 'constexpr' expansion of" } + return true; +} + +template<bool Const, size_t N, typename T> +constexpr bool +test_out_of_capacity() +{ + InplaceVector<Const, T, N> v; + (void)v[N+2]; // { dg-error "in 'constexpr' expansion of" } + return true; +} + +template<bool Const, typename T> +constexpr bool +test_out_of_size() +{ + InplaceVector<Const, T, 10> v{1, 2, 3, 4, 5}; + (void)v[7]; // { dg-error "in 'constexpr' expansion of" } + return true; +} + +static_assert(test_front_on_empty<false, 0, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_front_on_empty<false, 4, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_back_on_empty<false, 0, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_back_on_empty<false, 4, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_out_of_capacity<false, 0, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_out_of_capacity<false, 4, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_out_of_size<false, int>()); // { dg-error "in 'constexpr' expansion of" } + +static_assert(test_front_on_empty<true, 0, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_front_on_empty<true, 4, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_back_on_empty<true, 0, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_back_on_empty<true, 4, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_out_of_capacity<true, 0, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_out_of_capacity<true, 4, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_out_of_size<true, int>()); // { dg-error "in 'constexpr' expansion of" } + +// { dg-prune-output "non-constant condition for static assertion" } +// { dg-prune-output "is not a constant expression" } +// { dg-prune-output "call to non-'constexpr' function" } diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/cons/1.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/cons/1.cc new file mode 100644 index 0000000..45685c2 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/cons/1.cc @@ -0,0 +1,387 @@ +// { dg-do run { target c++26 } } + +#include <inplace_vector> +#include <testsuite_hooks.h> + +struct X +{ + constexpr X() { } // not trivially default constructible +}; + +struct N +{ + constexpr N() noexcept { } // not trivially default constructible +}; + +struct D +{ + ~D() {} // not trivially destructible +}; + +struct U +{ + U() noexcept(false) = default; // lies about noexcept +}; + +// n5008 inplace.vector.overview says for inplace_vector<T, 0> +// provides trivial copy/move/default constructor regardless of T +struct Z +{ + constexpr Z(int) {} + Z() = delete; +}; + +static_assert(std::is_default_constructible_v<std::inplace_vector<int, 2>>); +static_assert(std::is_default_constructible_v<std::inplace_vector<X, 2>>); +static_assert(std::is_default_constructible_v<std::inplace_vector<N, 2>>); +static_assert(std::is_default_constructible_v<std::inplace_vector<D, 2>>); +static_assert(std::is_default_constructible_v<std::inplace_vector<U, 2>>); +// The operators are not constrained, as for any other container +static_assert(std::is_default_constructible_v<std::inplace_vector<Z, 2>>); + +static_assert(std::is_nothrow_default_constructible_v<std::inplace_vector<int, 2>>); +static_assert(std::is_nothrow_default_constructible_v<std::inplace_vector<X, 2>>); +static_assert(std::is_nothrow_default_constructible_v<std::inplace_vector<N, 2>>); +static_assert(std::is_nothrow_default_constructible_v<std::inplace_vector<D, 2>>); +static_assert(std::is_nothrow_default_constructible_v<std::inplace_vector<U, 2>>); + +// Needs to set size to zero, not trivial +static_assert(!std::is_trivially_default_constructible_v<std::inplace_vector<int, 2>>); +static_assert(!std::is_trivially_default_constructible_v<std::inplace_vector<X, 2>>); +static_assert(!std::is_trivially_default_constructible_v<std::inplace_vector<N, 2>>); +static_assert(!std::is_trivially_default_constructible_v<std::inplace_vector<D, 2>>); +static_assert(!std::is_trivially_default_constructible_v<std::inplace_vector<U, 2>>); + +#if !_GLIBCXX_DEBUG +static_assert(std::is_trivially_destructible_v<std::inplace_vector<int, 2>>); +static_assert(std::is_trivially_destructible_v<std::inplace_vector<X, 2>>); +static_assert(std::is_trivially_destructible_v<std::inplace_vector<N, 2>>); +static_assert(!std::is_trivially_destructible_v<std::inplace_vector<D, 2>>); +static_assert(std::is_trivially_destructible_v<std::inplace_vector<U, 2>>); +#endif + +static_assert(std::is_nothrow_default_constructible_v<std::inplace_vector<int, 0>>); +static_assert(std::is_nothrow_default_constructible_v<std::inplace_vector<X, 0>>); +static_assert(std::is_nothrow_default_constructible_v<std::inplace_vector<N, 0>>); +static_assert(std::is_nothrow_default_constructible_v<std::inplace_vector<D, 0>>); +static_assert(std::is_nothrow_default_constructible_v<std::inplace_vector<U, 0>>); + +// Size is always zero, so trivial +static_assert(std::is_trivially_default_constructible_v<std::inplace_vector<int, 0>>); +static_assert(std::is_trivially_default_constructible_v<std::inplace_vector<X, 0>>); +static_assert(std::is_trivially_default_constructible_v<std::inplace_vector<N, 0>>); +static_assert(std::is_trivially_default_constructible_v<std::inplace_vector<D, 0>>); +static_assert(std::is_trivially_default_constructible_v<std::inplace_vector<U, 0>>); +static_assert(std::is_trivially_default_constructible_v<std::inplace_vector<Z, 0>>); + +static_assert(std::is_trivially_destructible_v<std::inplace_vector<int, 0>>); +static_assert(std::is_trivially_destructible_v<std::inplace_vector<X, 0>>); +static_assert(std::is_trivially_destructible_v<std::inplace_vector<N, 0>>); +static_assert(std::is_trivially_destructible_v<std::inplace_vector<D, 0>>); +static_assert(std::is_trivially_destructible_v<std::inplace_vector<U, 0>>); + +static_assert(std::is_empty_v<std::inplace_vector<int, 0>>); +static_assert(std::is_empty_v<std::inplace_vector<X, 0>>); +static_assert(std::is_empty_v<std::inplace_vector<N, 0>>); +static_assert(std::is_empty_v<std::inplace_vector<D, 0>>); +static_assert(std::is_empty_v<std::inplace_vector<U, 0>>); +static_assert(std::is_empty_v<std::inplace_vector<Z, 0>>); + +constexpr void +test_default() +{ + std::inplace_vector<int, 5> c; + VERIFY( c.size() == 0 ); + VERIFY( c.capacity() == 5 ); + VERIFY( c.empty() ); + VERIFY( c.begin() == c.end() ); + + std::inplace_vector<int, 0> c0; + VERIFY( c0.size() == 0 ); + VERIFY( c0.capacity() == 0 ); + VERIFY( c0.empty() ); + VERIFY( c0.begin() == c0.end() ); + + std::inplace_vector<Z, 0> z0; + VERIFY( z0.size() == 0 ); + VERIFY( z0.capacity() == 0 ); + VERIFY( z0.empty() ); + VERIFY( z0.begin() == z0.end() ); + +#ifdef __cpp_lib_constexpr_inplace_vector +#error remove the consteval check +#endif + if consteval { + return; + } + + std::inplace_vector<X, 5> cx; + VERIFY( cx.size() == 0 ); + VERIFY( cx.capacity() == 5 ); + VERIFY( cx.empty() ); + VERIFY( cx.begin() == cx.end() ); + + std::inplace_vector<X, 0> cx0; + VERIFY( cx0.size() == 0 ); + VERIFY( cx0.capacity() == 0 ); + VERIFY( cx0.empty() ); + VERIFY( cx0.begin() == cx0.end() ); +} + +constexpr void +test_n() +{ + std::inplace_vector<int, 5> c(2); + VERIFY( c.size() == 2 ); + VERIFY( c.capacity() == 5 ); + VERIFY( not c.empty() ); + VERIFY( c.begin() + 2 == c.end() ); + VERIFY( c[0] == 0 ); + VERIFY( c[1] == 0 ); + + std::inplace_vector<int, 2> c2(2); + VERIFY( c2.size() == 2 ); + VERIFY( c2.capacity() == 2 ); + VERIFY( not c2.empty() ); + VERIFY( c2.begin() + 2 == c2.end() ); + VERIFY( c2[0] == 0 ); + VERIFY( c2[1] == 0 ); + + std::inplace_vector<int, 0> c0(0); + VERIFY( c0.size() == 0 ); + VERIFY( c0.capacity() == 0 ); + VERIFY( c0.empty() ); + VERIFY( c0.begin() == c0.end() ); + + std::inplace_vector<int, 2> c20(0); + VERIFY( c20.size() == 0 ); + VERIFY( c20.capacity() == 2 ); + VERIFY( c20.empty() ); + VERIFY( c20.begin() == c20.end() ); + + std::inplace_vector<Z, 0> z0(0); + VERIFY( z0.size() == 0 ); + VERIFY( z0.capacity() == 0 ); + VERIFY( z0.empty() ); + VERIFY( z0.begin() == z0.end() ); + +#ifdef __cpp_exceptions +#ifdef __cpp_lib_constexpr_exceptions +#error remove the consteval check +#endif + if not consteval { + try + { + std::inplace_vector<int, 2> ct(3); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + + try + { + std::inplace_vector<int, 0> ct(1); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + + } +#endif + +#ifdef __cpp_lib_constexpr_inplace_vector +#error remove the consteval check +#endif + if consteval { + return; + } + + std::inplace_vector<X, 5> cx(3); + VERIFY( cx.size() == 3 ); + VERIFY( cx.capacity() == 5 ); + VERIFY( not cx.empty() ); + VERIFY( cx.begin() + 3 == cx.end() ); + (void) cx[2]; +} + +constexpr void +test_n_val() +{ + std::inplace_vector<int, 5> c(2, 99); + VERIFY( c.size() == 2 ); + VERIFY( c.capacity() == 5 ); + VERIFY( not c.empty() ); + VERIFY( c.begin() + 2 == c.end() ); + VERIFY( c[0] == 99 ); + VERIFY( c[1] == 99 ); + + std::inplace_vector<int, 1> c1(1, 44); + VERIFY( c1.size() == 1 ); + VERIFY( c1.capacity() == 1 ); + VERIFY( not c1.empty() ); + VERIFY( c1.begin() + 1 == c1.end() ); + VERIFY( c1[0] == 44 ); + + std::inplace_vector<int, 0> c0(0, 33); + VERIFY( c0.size() == 0 ); + VERIFY( c0.capacity() == 0 ); + VERIFY( c0.empty() ); + VERIFY( c0.begin() == c0.end() ); + + std::inplace_vector<int, 2> c20(0, 22); + VERIFY( c20.size() == 0 ); + VERIFY( c20.capacity() == 2 ); + VERIFY( c20.empty() ); + VERIFY( c20.begin() == c20.end() ); + + std::inplace_vector<Z, 0> z0(0, 33); + VERIFY( z0.size() == 0 ); + VERIFY( z0.capacity() == 0 ); + VERIFY( z0.empty() ); + VERIFY( z0.begin() == z0.end() ); + +#ifdef __cpp_exceptions +#ifdef __cpp_lib_constexpr_exceptions +#error remove the consteval check +#endif + if not consteval { + try + { + std::inplace_vector<int, 2> ct(3, 11); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + + try + { + std::inplace_vector<int, 0> ct(2, 11); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + } +#endif + +#ifdef __cpp_lib_constexpr_inplace_vector +#error remove the consteval check +#endif + if consteval { + return; + } + + std::inplace_vector<X, 5> cx(4); + VERIFY( cx.size() == 4 ); + VERIFY( cx.capacity() == 5 ); + VERIFY( not cx.empty() ); + VERIFY( cx.begin() + 4 == cx.end() ); + (void) cx[3]; +} + +constexpr void +test_initializer_list() +{ + std::inplace_vector<int, 5> c{22, 33}; + VERIFY( c.size() == 2 ); + VERIFY( c.capacity() == 5 ); + VERIFY( not c.empty() ); + VERIFY( c.begin() + 2 == c.end() ); + VERIFY( c[0] == 22 ); + VERIFY( c[1] == 33 ); + + std::inplace_vector<int, 1> c1{44}; + VERIFY( c1.size() == 1 ); + VERIFY( c1.capacity() == 1 ); + VERIFY( not c1.empty() ); + VERIFY( c1.begin() + 1 == c1.end() ); + VERIFY( c1[0] == 44 ); + + std::inplace_vector<int, 0> c0({}); + VERIFY( c0.size() == 0 ); + VERIFY( c0.capacity() == 0 ); + VERIFY( c0.empty() ); + VERIFY( c0.begin() == c0.end() ); + + std::inplace_vector<int, 2> c20({}); + VERIFY( c20.size() == 0 ); + VERIFY( c20.capacity() == 2 ); + VERIFY( c20.empty() ); + VERIFY( c20.begin() == c20.end() ); + + std::inplace_vector<Z, 0> z0({}); + VERIFY( z0.size() == 0 ); + VERIFY( z0.capacity() == 0 ); + VERIFY( z0.empty() ); + VERIFY( z0.begin() == z0.end() ); + +#ifdef __cpp_exceptions +#ifdef __cpp_lib_constexpr_exceptions +#error remove the consteval check +#endif + if not consteval { + try + { + std::inplace_vector<int, 2> ct{11, 22, 33}; + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + + try + { + std::inplace_vector<int, 0> ct{11, 22}; + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + } +#endif + +#ifdef __cpp_lib_constexpr_inplace_vector +#error remove the consteval check +#endif + if consteval { + return; + } + + std::inplace_vector<X, 5> cx{X(), X(), X(), X()}; + VERIFY( cx.size() == 4 ); + VERIFY( cx.capacity() == 5 ); + VERIFY( not cx.empty() ); + VERIFY( cx.begin() + 4 == cx.end() ); + (void) cx[3]; +} + +constexpr std::inplace_vector<int, 0> e0; +constexpr std::inplace_vector<X, 0> e1; +constexpr std::inplace_vector<Z, 0> e2; + +constexpr std::inplace_vector<int, 5> g1; +constexpr std::inplace_vector<int, 5> g2(2, 100); +constexpr std::inplace_vector<int, 5> g3 = g2; +constexpr std::inplace_vector<int, 5> g4{1, 2, 3}; +constexpr std::inplace_vector<int, 5> g5 = [] { + std::inplace_vector<int, 5> res; + res = g3; + return res; +}(); + +int main() +{ + auto tests = [] { + test_default(); + test_n(); + test_n_val(); + test_initializer_list(); + return true; + }; + + tests(); + constexpr bool _ = tests(); +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/cons/from_range.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/cons/from_range.cc new file mode 100644 index 0000000..4a2f193 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/cons/from_range.cc @@ -0,0 +1,177 @@ +// { dg-do run { target c++26 } } + +#include <inplace_vector> + +#include <span> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> +#include <testsuite_allocator.h> + +template<typename T, typename V, size_t N> +constexpr bool +eq(const std::inplace_vector<V, N>& l, std::span<const T> r) { + if (l.size() != r.size()) + return false; + for (auto i = 0u; i < l.size(); ++i) + if (l[i] != r[i]) + return false; + return true; +}; + +template<typename T, template<class TT> class ItType> +constexpr void +do_test_it() +{ + // The vector's value_type. + using V = int; + + T a[]{1,2,3,4,5,6,7,8,9}; + using It = ItType<T>; + + auto bounds = typename It::ContainerType(a, a+9); + std::inplace_vector<V, 0> e0(It(a, &bounds), It(a, &bounds)); + VERIFY( e0.empty() ); + + bounds = typename It::ContainerType(a, a+9); + std::inplace_vector<V, 10> v0(It(a, &bounds), It(a, &bounds)); + VERIFY( v0.empty() ); + + bounds = typename It::ContainerType(a, a+9); + std::inplace_vector<V, 10> v4(It(a, &bounds), It(a+4, &bounds)); + VERIFY( eq<T>(v4, {a, 4}) ); + +#ifdef __cpp_exceptions +#ifdef __cpp_lib_constexpr_exceptions +#error remove the consteval check +#endif + if consteval { + return; + } + + bounds = typename It::ContainerType(a, a+9); + try + { + std::inplace_vector<int, 5> v9(It(a, &bounds), It(a+9, &bounds)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + + bounds = typename It::ContainerType(a, a+9); + try + { + std::inplace_vector<int, 0> v2(It(a, &bounds), It(a+2, &bounds)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } +#endif +} + +constexpr bool +test_iterators() +{ + using namespace __gnu_test; + + do_test_it<int, input_iterator_wrapper>(); + do_test_it<int, forward_iterator_wrapper>(); + do_test_it<int, random_access_iterator_wrapper>(); + + do_test_it<short, forward_iterator_wrapper>(); + return true; +} + +template<typename Range> +constexpr void +do_test_r() +{ + // The vector's value_type. + using V = int; + + // The range's value_type. + using T = std::ranges::range_value_t<Range>; + T a[]{1,2,3,4,5,6,7,8,9}; + + std::inplace_vector<V, 0> e0(std::from_range, Range(a, a+0)); + VERIFY( e0.empty() ); + + std::inplace_vector<V, 10> v0(std::from_range, Range(a, a+0)); + VERIFY( v0.empty() ); + + std::inplace_vector<V, 10> v4(std::from_range, Range(a, a+4)); + VERIFY( eq<T>(v4, {a, 4}) ); + +#ifdef __cpp_exceptions +#ifdef __cpp_lib_constexpr_exceptions +#error remove the consteval check +#endif + if consteval { + return; + } + + try + { + std::inplace_vector<V, 5> v9(std::from_range, Range(a, a+9)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + + try + { + std::inplace_vector<V, 0> v3(std::from_range, Range(a, a+3)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } +#endif +} + +constexpr bool +test_ranges() +{ + using namespace __gnu_test; + + do_test_r<test_forward_range<int>>(); + do_test_r<test_sized_range_sized_sent<int, forward_iterator_wrapper>>(); + + do_test_r<test_input_range<int>>(); + do_test_r<test_input_sized_range<int>>(); + do_test_r<test_sized_range_sized_sent<int, input_iterator_wrapper>>(); + + do_test_r<test_range<int, input_iterator_wrapper_nocopy>>(); + do_test_r<test_sized_range<int, input_iterator_wrapper_nocopy>>(); + do_test_r<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>(); + + do_test_r<test_forward_range<short>>(); + do_test_r<test_input_range<short>>(); + + // Not lvalue-convertible to int + struct C { + constexpr C(int v) : val(v) { } + constexpr operator int() && { return val; } + constexpr bool operator==(int b) const { return b == val; } + int val; + }; + using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; + do_test_r<rvalue_input_range>(); + + return true; +} + +int main() +{ + auto test_all = [] { + test_iterators(); + test_ranges(); + return true; + }; + + test_all(); + static_assert( test_all() ); +} + diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/cons/throws.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/cons/throws.cc new file mode 100644 index 0000000..4ce39d1 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/cons/throws.cc @@ -0,0 +1,129 @@ +// { dg-do run { target c++26 } } + +#include <inplace_vector> + +#include <span> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> +#include <testsuite_allocator.h> + +struct CopyFailed {}; + +struct Thrower +{ + static inline size_t throw_after = 0; + static inline size_t incontainer = 0; + + Thrower() {} + Thrower(int x) {} + Thrower(const Thrower&) + { + if (incontainer >= throw_after) + throw CopyFailed(); + ++incontainer; + } + + ~Thrower() + { --incontainer; } +}; + +template<template<class TT> class ItType> +void +do_test_it() +{ + // The vector's value_type. + using V = Thrower; + + V a[]{1,2,3,4,5,6,7,8,9}; + using It = ItType<V>; + + auto bounds = typename It::ContainerType(a, a+9); + Thrower::throw_after = 100; + Thrower::incontainer = 0; + try + { + std::inplace_vector<V, 5> v9(It(a, &bounds), It(a+9, &bounds)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( Thrower::incontainer == 0 ); + + bounds = typename It::ContainerType(a, a+9); + Thrower::throw_after = 2; + Thrower::incontainer = 0; + try + { + std::inplace_vector<V, 5> v2(It(a, &bounds), It(a+3, &bounds)); + VERIFY(false); + } + catch (CopyFailed const&) + { + } + VERIFY( Thrower::incontainer == 0 ); +} + +bool +test_iterators() +{ + using namespace __gnu_test; + do_test_it<input_iterator_wrapper>(); + do_test_it<forward_iterator_wrapper>(); + do_test_it<random_access_iterator_wrapper>(); + return true; +} + +template<typename Range> +void +do_test_r() +{ + // The vector's value_type. + using V = Thrower; + + V a[]{1,2,3,4,5,6,7,8,9}; + + Thrower::throw_after = 100; + Thrower::incontainer = 0; + try + { + std::inplace_vector<V, 5> v9(std::from_range, Range(a, a+9)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( Thrower::incontainer == 0 ); + + Thrower::throw_after = 2; + Thrower::incontainer = 0; + try + { + std::inplace_vector<V, 5> v9(std::from_range, Range(a, a+3)); + VERIFY(false); + } + catch (CopyFailed const&) + { + } + VERIFY( Thrower::incontainer == 0 ); +} + +bool +test_ranges() +{ + using namespace __gnu_test; + do_test_r<test_forward_range<Thrower>>(); + do_test_r<test_sized_range_sized_sent<Thrower, forward_iterator_wrapper>>(); + + do_test_r<test_input_range<Thrower>>(); + do_test_r<test_input_sized_range<Thrower>>(); + do_test_r<test_sized_range_sized_sent<Thrower, input_iterator_wrapper>>(); + return true; +} + +int main() +{ + test_iterators(); + test_ranges(); +} + diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/copy.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/copy.cc new file mode 100644 index 0000000..917eebd --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/copy.cc @@ -0,0 +1,251 @@ +// { dg-do run { target c++26 } } + +#include <inplace_vector> +#include <ranges> +#include <testsuite_hooks.h> + +struct X +{ + X() = default; + constexpr X(int p) : v(p) {} + constexpr X(const X& o) : v(o.v) { } // not trivial + constexpr X& operator=(const X& o) // not trivial + { v = o.v; return *this; } + + int v; + + friend auto operator<=>(const X&, const X&) = default; +}; + +template<bool CNoex, bool ANoex> +struct N +{ + N() = default; + constexpr N(const N&) noexcept(CNoex) { } // not trivial + constexpr N& operator=(const N& o) noexcept(ANoex) // not trivial + { return *this; } +}; + +struct D +{ + D() = default; + D(const D&) = default; + D& operator=(const D&) = default; + ~D() {} // not trivially destructible +}; + +struct U +{ + U() = default; + U(const U&) noexcept(false) = default; // lies about noexcept, is trivial but throwing + U& operator=(const U&) noexcept(false) = default; // lies about noexcept, is trivial but throwing +}; + +// n5008 inplace.vector.overview p5 says for inplace_vector<T, 0> +// provides trivial copy/move/default cosntructpr regardless of T +struct Z +{ + Z(Z&&) = delete; + Z& operator=(Z&&) = delete; +}; + +template<size_t N, typename T> + constexpr std::inplace_vector<T, N> const& + materialize(std::inplace_vector<T, N> const& r) + { return r; } + +static_assert(std::is_copy_constructible_v<std::inplace_vector<int, 2>>); +static_assert(std::is_copy_constructible_v<std::inplace_vector<X, 2>>); +static_assert(std::is_copy_constructible_v<std::inplace_vector<N<false, false>, 2>>); +static_assert(std::is_copy_constructible_v<std::inplace_vector<D, 2>>); +static_assert(std::is_copy_constructible_v<std::inplace_vector<U, 2>>); +// The operators are not constrained, as for any other container +static_assert(std::is_copy_constructible_v<std::inplace_vector<Z, 2>>); + +// conditional noexcept here is libstdc++ extension, +static_assert(std::is_nothrow_copy_constructible_v<std::inplace_vector<int, 2>>); +static_assert(!std::is_nothrow_copy_constructible_v<std::inplace_vector<X, 2>>); +static_assert(std::is_nothrow_copy_constructible_v<std::inplace_vector<N<true, true>, 2>>); +static_assert(std::is_nothrow_copy_constructible_v<std::inplace_vector<N<true, false>, 2>>); +static_assert(!std::is_nothrow_copy_constructible_v<std::inplace_vector<N<false, true>, 2>>); +static_assert(!std::is_nothrow_copy_constructible_v<std::inplace_vector<N<false, false>, 2>>); +static_assert(std::is_nothrow_copy_constructible_v<std::inplace_vector<D, 2>>); +static_assert(!std::is_nothrow_copy_constructible_v<std::inplace_vector<U, 2>>); + +#if !_GLIBCXX_DEBUG +static_assert(std::is_trivially_copy_constructible_v<std::inplace_vector<int, 2>>); +static_assert(!std::is_trivially_copy_constructible_v<std::inplace_vector<X, 2>>); +static_assert(!std::is_trivially_copy_constructible_v<std::inplace_vector<N<true, true>, 2>>); +// is_trivially_copy_constructible_v checks destructor +static_assert(!std::is_trivially_copy_constructible_v<std::inplace_vector<D, 2>>); +static_assert(std::is_trivially_copy_constructible_v<std::inplace_vector<U, 2>>); +#endif + +static_assert(std::is_copy_assignable_v<std::inplace_vector<int, 2>>); +static_assert(std::is_copy_assignable_v<std::inplace_vector<X, 2>>); +static_assert(std::is_copy_assignable_v<std::inplace_vector<N<false, false>, 2>>); +static_assert(std::is_copy_assignable_v<std::inplace_vector<D, 2>>); +static_assert(std::is_copy_assignable_v<std::inplace_vector<U, 2>>); +// The operators are not constrained, as for any other container +static_assert(std::is_copy_assignable_v<std::inplace_vector<Z, 2>>); + +static_assert(std::is_nothrow_copy_assignable_v<std::inplace_vector<int, 2>>); +static_assert(!std::is_nothrow_copy_assignable_v<std::inplace_vector<X, 2>>); +static_assert(std::is_nothrow_copy_assignable_v<std::inplace_vector<N<true, true>, 2>>); +static_assert(!std::is_nothrow_copy_assignable_v<std::inplace_vector<N<true, false>, 2>>); +static_assert(!std::is_nothrow_copy_assignable_v<std::inplace_vector<N<false, true>, 2>>); +static_assert(!std::is_nothrow_copy_assignable_v<std::inplace_vector<N<false, false>, 2>>); +static_assert(std::is_nothrow_copy_assignable_v<std::inplace_vector<D, 2>>); +static_assert(!std::is_nothrow_copy_assignable_v<std::inplace_vector<U, 2>>); + +#if !_GLIBCXX_DEBUG +// conditional noexcept here is libstdc++ extension, +static_assert(std::is_trivially_copy_assignable_v<std::inplace_vector<int, 2>>); +static_assert(!std::is_trivially_copy_assignable_v<std::inplace_vector<X, 2>>); +static_assert(!std::is_trivially_copy_assignable_v<std::inplace_vector<N<true, true>, 2>>); +// destructor is not trivial +static_assert(!std::is_trivially_copy_assignable_v<std::inplace_vector<D, 2>>); +static_assert(std::is_trivially_copy_assignable_v<std::inplace_vector<U, 2>>); +#endif + +static_assert(std::is_nothrow_copy_constructible_v<std::inplace_vector<int, 0>>); +static_assert(std::is_nothrow_copy_constructible_v<std::inplace_vector<X, 0>>); +static_assert(std::is_nothrow_copy_constructible_v<std::inplace_vector<N<false, false>, 0>>); +static_assert(std::is_nothrow_copy_constructible_v<std::inplace_vector<D, 0>>); +static_assert(std::is_nothrow_copy_constructible_v<std::inplace_vector<U, 0>>); +static_assert(std::is_nothrow_copy_constructible_v<std::inplace_vector<Z, 0>>); + +static_assert(std::is_trivially_copy_constructible_v<std::inplace_vector<int, 0>>); +static_assert(std::is_trivially_copy_constructible_v<std::inplace_vector<X, 0>>); +static_assert(std::is_trivially_copy_constructible_v<std::inplace_vector<D, 0>>); +static_assert(std::is_trivially_copy_constructible_v<std::inplace_vector<U, 0>>); +static_assert(std::is_trivially_copy_constructible_v<std::inplace_vector<Z, 0>>); + +static_assert(std::is_nothrow_copy_assignable_v<std::inplace_vector<int, 0>>); +static_assert(std::is_nothrow_copy_assignable_v<std::inplace_vector<X, 0>>); +static_assert(std::is_nothrow_copy_assignable_v<std::inplace_vector<N<false, false>, 0>>); +static_assert(std::is_nothrow_copy_assignable_v<std::inplace_vector<D, 0>>); +static_assert(std::is_nothrow_copy_assignable_v<std::inplace_vector<U, 0>>); +static_assert(std::is_nothrow_copy_assignable_v<std::inplace_vector<Z, 0>>); + +static_assert(std::is_trivially_copy_assignable_v<std::inplace_vector<int, 0>>); +static_assert(std::is_trivially_copy_assignable_v<std::inplace_vector<X, 0>>); +static_assert(std::is_trivially_copy_assignable_v<std::inplace_vector<N<false, false>, 0>>); +static_assert(std::is_trivially_copy_assignable_v<std::inplace_vector<D, 0>>); +static_assert(std::is_trivially_copy_assignable_v<std::inplace_vector<U, 0>>); +static_assert(std::is_trivially_copy_assignable_v<std::inplace_vector<Z, 0>>); + + +template<typename T, size_t N> +constexpr bool +eq(const std::inplace_vector<T, N>& s, std::span<const T> o) +{ return std::ranges::equal(s, o); } + +constexpr void +test_ctor() +{ + auto e0 = materialize<0, int>({}); + VERIFY( e0.empty() ); + auto e1 = materialize<0, X>({}); + VERIFY( e1.empty() ); + auto e2 = materialize<0, Z>({}); + VERIFY( e2.empty() ); + + auto c0 = materialize<5, int>({}); + VERIFY( c0.empty() ); + + auto c3 = materialize<5, int>({1, 2, 3}); + VERIFY( eq(c3, {1, 2, 3}) ); + + auto c5 = materialize<5, int>({1, 2, 3, 4, 5}); + VERIFY( eq(c5, {1, 2, 3, 4, 5}) ); + +#ifdef __cpp_lib_constexpr_inplace_vector +#error remove the consteval check +#endif + if consteval { + return; + } + + auto x0 = materialize<3, X>({}); + VERIFY( x0.empty() ); + + auto x2 = materialize<3, X>({1, 2}); + VERIFY( eq(x2, {1, 2}) ); + + auto x3 = materialize<3, X>({1, 2, 3}); + VERIFY( eq(x3, {1, 2, 3}) ); +} + +constexpr void +test_assign() +{ + std::inplace_vector<int, 0> e0; + e0 = materialize<0, int>({}); + VERIFY( e0.empty() ); + std::inplace_vector<X, 0> e1; + e1 = materialize<0, X>({}); + VERIFY( e1.empty() ); + std::inplace_vector<Z, 0> e2; + e2 = materialize<0, Z>({}); + VERIFY( e2.empty() ); + + std::inplace_vector<int, 5> c; + c = materialize<5, int>({}); + VERIFY( c.empty() ); + + c = materialize<5, int>({1, 2, 3}); + VERIFY( eq(c, {1, 2, 3}) ); + + c = materialize<5, int>({1, 2, 3, 4, 5}); + VERIFY( eq(c, {1, 2, 3, 4, 5}) ); + + c = materialize<5, int>({4, 5}); + VERIFY( eq(c, {4, 5}) ); + + c = materialize<5, int>({}); + VERIFY( c.empty() ); + +#ifdef __cpp_lib_constexpr_inplace_vector +#error remove the consteval check +#endif + if consteval { + return; + } + + std::inplace_vector<X, 5> x; + x = materialize<5, X>({}); + VERIFY( x.empty() ); + + x = materialize<5, X>({1, 2, 3}); + VERIFY( eq(x, {1, 2, 3}) ); + + x = materialize<5, X>({1, 2, 3, 4, 5}); + VERIFY( eq(x, {1, 2, 3, 4, 5}) ); + + x = materialize<5, X>({4, 5}); + VERIFY( eq(x, {4, 5}) ); + + x = materialize<5, X>({}); + VERIFY( x.empty() ); +} + +constexpr auto e0 = materialize<0, int>({}); +constexpr auto e1 = materialize<0, X>({}); +constexpr auto e2 = materialize<0, Z>({}); + +constexpr auto t1 = materialize<3, int>({}); +constexpr auto t2 = materialize<3, int>({1, 2}); +constexpr auto t3 = materialize<3, int>({11, 22, 33}); + +int main() +{ + auto tests = [] { + test_ctor(); + test_assign(); + return true; + }; + + tests(); + constexpr bool _ = tests(); +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/assign1_neg.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/assign1_neg.cc new file mode 100644 index 0000000..37c80c3 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/assign1_neg.cc @@ -0,0 +1,16 @@ +// { dg-do run { target c++26 xfail *-*-* } } +// { dg-require-debug-mode "" } + +#include <inplace_vector> +#include <debug/checks.h> + +void test01() +{ + __gnu_test::check_assign1<std::inplace_vector<int, 10> >(); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/assign2_neg.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/assign2_neg.cc new file mode 100644 index 0000000..ff6c9a7 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/assign2_neg.cc @@ -0,0 +1,16 @@ +// { dg-do run { target c++26 xfail *-*-* } } +// { dg-require-debug-mode "" } + +#include <inplace_vector> +#include <debug/checks.h> + +void test01() +{ + __gnu_test::check_assign2<std::inplace_vector<int, 10>>(); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/assign3_neg.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/assign3_neg.cc new file mode 100644 index 0000000..42f811d --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/assign3_neg.cc @@ -0,0 +1,16 @@ +// { dg-do run { target c++26 xfail *-*-* } } +// { dg-require-debug-mode "" } + +#include <inplace_vector> +#include <debug/checks.h> + +void test01() +{ + __gnu_test::check_assign3<std::inplace_vector<int, 10>>(); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/assign4_backtrace_neg.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/assign4_backtrace_neg.cc new file mode 100644 index 0000000..dec7228 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/assign4_backtrace_neg.cc @@ -0,0 +1,17 @@ +// { dg-do run { target c++26 xfail *-*-* } } +// { dg-options "-D_GLIBCXX_DEBUG_BACKTRACE -lstdc++exp" } +// { dg-require-cpp-feature-test __cpp_lib_stacktrace } + +#include <debug/inplace_vector> +#include <debug/checks.h> + +void test01() +{ + __gnu_test::check_assign1<__gnu_debug::inplace_vector<int, 10>>(); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/assign4_neg.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/assign4_neg.cc new file mode 100644 index 0000000..4bed345 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/assign4_neg.cc @@ -0,0 +1,15 @@ +// { dg-do run { target c++26 xfail *-*-* } } + +#include <debug/inplace_vector> +#include <debug/checks.h> + +void test01() +{ + __gnu_test::check_assign1<__gnu_debug::inplace_vector<int, 10>>(); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/construct1_neg.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/construct1_neg.cc new file mode 100644 index 0000000..28ede4c --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/construct1_neg.cc @@ -0,0 +1,16 @@ +// { dg-do run { target c++26 xfail *-*-* } } +// { dg-require-debug-mode "" } + +#include <inplace_vector> +#include <debug/checks.h> + +void test01() +{ + __gnu_test::check_construct1<std::inplace_vector<int, 10> >(); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/construct2_neg.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/construct2_neg.cc new file mode 100644 index 0000000..be37dde --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/construct2_neg.cc @@ -0,0 +1,16 @@ +// { dg-do run { target c++26 xfail *-*-* } } +// { dg-require-debug-mode "" } + +#include <inplace_vector> +#include <debug/checks.h> + +void test01() +{ + __gnu_test::check_construct2<std::inplace_vector<int, 10> >(); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/construct3_neg.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/construct3_neg.cc new file mode 100644 index 0000000..8086183 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/construct3_neg.cc @@ -0,0 +1,16 @@ +// { dg-do run { target c++26 xfail *-*-* } } +// { dg-require-debug-mode "" } + +#include <inplace_vector> +#include <debug/checks.h> + +void test01() +{ + __gnu_test::check_construct3<std::inplace_vector<int, 10> >(); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/construct4_neg.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/construct4_neg.cc new file mode 100644 index 0000000..099b357 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/construct4_neg.cc @@ -0,0 +1,15 @@ +// { dg-do run { target c++26 xfail *-*-* } } + +#include <debug/inplace_vector> +#include <debug/checks.h> + +void test01() +{ + __gnu_test::check_construct1<__gnu_debug::inplace_vector<int, 10> >(); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/debug_functions.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/debug_functions.cc new file mode 100644 index 0000000..bc5ed50 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/debug_functions.cc @@ -0,0 +1,34 @@ +// { dg-do run { target c++26 } } +// { dg-require-debug-mode "" } + +#include <inplace_vector> +#include <testsuite_hooks.h> + +void test02() +{ + using namespace __gnu_debug; + + std::inplace_vector<int, 10> v1(3, 1); + VERIFY( !__check_singular(v1.begin()) ); + auto it = v1.begin(); + VERIFY( !__check_singular(it) ); + + VERIFY( !__check_singular(v1.end()) ); + it = v1.end(); + VERIFY( !__check_singular(it) ); + + v1.clear(); + + VERIFY( it._M_singular() ); + VERIFY( __check_singular(it) ); + + it = v1.end(); + VERIFY( !it._M_singular() ); + VERIFY( !__check_singular(it) ); +} + +int main() +{ + test02(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/erase.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/erase.cc new file mode 100644 index 0000000..94da946 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/erase.cc @@ -0,0 +1,35 @@ +// { dg-do run { target c++26 } } +// { dg-require-debug-mode "" } + +#include <inplace_vector> +#include <testsuite_hooks.h> + +void test01() +{ + std::inplace_vector<int, 10> v; + + for (int i = 0; i != 10; ++i) + v.push_back(i); + + auto before = v.begin() + 4; + auto last = v.end() - 1; + + VERIFY( std::erase(v, 6) == 1 ); + + VERIFY(before._M_dereferenceable()); + VERIFY(last._M_singular()); +} + +void test02() +{ + std::inplace_vector<int, 0> v; + + VERIFY( std::erase(v, 6) == 0 ); +} + +int main() +{ + test01(); + test02(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/insert1_neg.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/insert1_neg.cc new file mode 100644 index 0000000..f85cfaa --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/insert1_neg.cc @@ -0,0 +1,16 @@ +// { dg-do run { target c++26 xfail *-*-* } } +// { dg-require-debug-mode "" } + +#include <inplace_vector> +#include <debug/checks.h> + +void test01() +{ + __gnu_test::check_insert1<std::inplace_vector<int, 10>>(); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/insert2_neg.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/insert2_neg.cc new file mode 100644 index 0000000..6a20369 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/insert2_neg.cc @@ -0,0 +1,16 @@ +// { dg-do run { target c++26 xfail *-*-* } } +// { dg-require-debug-mode "" } + +#include <inplace_vector> +#include <debug/checks.h> + +void test01() +{ + __gnu_test::check_insert2<std::inplace_vector<int, 10>>(); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/insert3_neg.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/insert3_neg.cc new file mode 100644 index 0000000..63f6e05 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/insert3_neg.cc @@ -0,0 +1,16 @@ +// { dg-do run { target c++26 xfail *-*-* } } +// { dg-require-debug-mode "" } + +#include <inplace_vector> +#include <debug/checks.h> + +void test01() +{ + __gnu_test::check_insert3<std::inplace_vector<int, 10>>(); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/insert4_neg.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/insert4_neg.cc new file mode 100644 index 0000000..59e9f4b --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/insert4_neg.cc @@ -0,0 +1,15 @@ +// { dg-do run { target c++26 xfail *-*-* } } + +#include <debug/inplace_vector> +#include <debug/checks.h> + +void test01() +{ + __gnu_test::check_insert1<__gnu_debug::inplace_vector<int, 10>>(); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/insert5_neg.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/insert5_neg.cc new file mode 100644 index 0000000..e1fbd65 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/insert5_neg.cc @@ -0,0 +1,16 @@ +// { dg-do run { target c++26 xfail *-*-* } } +// { dg-require-debug-mode "" } + +#include <inplace_vector> +#include <debug/checks.h> + +void test01() +{ + __gnu_test::check_insert4<std::inplace_vector<int, 10>>(); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/insert7_neg.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/insert7_neg.cc new file mode 100644 index 0000000..c59453c --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/insert7_neg.cc @@ -0,0 +1,24 @@ +// { dg-do run { target c++26 xfail *-*-* } } + +#include <memory> +#include <iterator> +#include <debug/inplace_vector> + +void +test01() +{ + __gnu_debug::inplace_vector<std::unique_ptr<int>, 10> v; + + v.emplace_back(new int(0)); + v.emplace_back(new int(1)); + + v.insert(begin(v) + 1, + make_move_iterator(begin(v)), + make_move_iterator(end(v))); +} + +int +main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/1.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/1.cc new file mode 100644 index 0000000..1a85273 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/1.cc @@ -0,0 +1,33 @@ +// { dg-do run { target c++26 } } + +#include <debug/inplace_vector> +#include <testsuite_hooks.h> + +using __gnu_debug::inplace_vector; + +// Assignment +void test01() +{ + inplace_vector<int, 30> v1; + inplace_vector<int, 30> v2; + + auto i = v1.end(); + VERIFY(!i._M_dereferenceable() && !i._M_singular()); + + v1 = v2; + VERIFY(i._M_singular()); + + i = v1.end(); + v1.assign(v2.begin(), v2.end()); + VERIFY( !i._M_singular() ); + + i = v1.end(); + v1.assign(17, 42); + VERIFY(i._M_singular()); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/2.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/2.cc new file mode 100644 index 0000000..22d512c --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/2.cc @@ -0,0 +1,34 @@ +// { dg-do run { target c++26 } } + +#include <debug/inplace_vector> +#include <testsuite_hooks.h> + +using __gnu_debug::inplace_vector; + +// Resize +void test01() +{ + inplace_vector<int, 50> v(10, 17); + v.reserve(20); + + auto before = v.begin() + 6; + auto at = before + 1; + auto after = at + 1; + + // Shrink. + v.resize(7); + VERIFY(before._M_dereferenceable()); + VERIFY(at._M_singular()); + VERIFY(after._M_singular()); + + // Grow. + before = v.begin() + 6; + v.resize(17); + VERIFY(before._M_dereferenceable()); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/3.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/3.cc new file mode 100644 index 0000000..7b74213 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/3.cc @@ -0,0 +1,43 @@ +// { dg-do run { target c++26 } } + +#include <debug/inplace_vector> +#include <testsuite_hooks.h> + +using __gnu_debug::inplace_vector; + +// Insert +void test01() +{ + inplace_vector<int, 100> v(10, 17); + v.reserve(30); + + // Insert a single element + auto before = v.begin() + 6; + auto at = before + 1; + auto after = at; + at = v.insert(at, 42); + VERIFY(before._M_dereferenceable()); + VERIFY(at._M_dereferenceable()); + VERIFY(after._M_singular()); + + // Insert multiple copies + before = v.begin() + 6; + at = before + 1; + v.insert(at, 3, 42); + VERIFY(before._M_dereferenceable()); + VERIFY(at._M_singular()); + + // Insert iterator range + static int data[] = { 2, 3, 5, 7 }; + before = v.begin() + 6; + at = before + 1; + v.insert(at, &data[0], &data[0] + 4); + VERIFY(before._M_dereferenceable()); + VERIFY(at._M_singular()); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/4.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/4.cc new file mode 100644 index 0000000..7393f1b --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/4.cc @@ -0,0 +1,40 @@ +// { dg-do run { target c++26 } } + +#include <debug/inplace_vector> +#include <testsuite_hooks.h> + +using __gnu_debug::inplace_vector; + +// Erase +void test04() +{ + inplace_vector<int, 30> v(20, 42); + + // Single element erase + auto before = v.begin(); + auto at = before + 3; + auto after = at; + at = v.erase(at); + VERIFY(before._M_dereferenceable()); + VERIFY(at._M_dereferenceable()); + VERIFY(after._M_singular()); + + // Multiple element erase + before = v.begin(); + at = before + 3; + v.erase(at, at + 3); + VERIFY(before._M_dereferenceable()); + VERIFY(at._M_singular()); + + // clear() + before = v.begin(); + VERIFY(before._M_dereferenceable()); + v.clear(); + VERIFY(before._M_singular()); +} + +int main() +{ + test04(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/append_range.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/append_range.cc new file mode 100644 index 0000000..8a793ec --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/append_range.cc @@ -0,0 +1,45 @@ +// { dg-do run { target c++26 } } + +#include <debug/inplace_vector> +#include <testsuite_hooks.h> + +using __gnu_debug::inplace_vector; + +void test01() +{ + inplace_vector<int, 100> v(10, 17); + inplace_vector<int, 10> v1(10, 19); + + auto before = v.begin() + 6; + auto last = v.end(); + auto end = last--; + + v.append_range(v1); + + VERIFY(before._M_dereferenceable()); + VERIFY(last._M_dereferenceable()); + VERIFY(end._M_singular()); +} + +void test02() +{ + inplace_vector<int, 100> v(10, 17); + inplace_vector<int, 0> v1; + + auto before = v.begin() + 6; + auto last = v.end(); + auto end = last--; + + v.append_range(v1); + + VERIFY(before._M_dereferenceable()); + VERIFY(last._M_dereferenceable()); + VERIFY(!end._M_singular()); +} + +int main() +{ + test01(); + test02(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/erase.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/erase.cc new file mode 100644 index 0000000..6e899d7 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/erase.cc @@ -0,0 +1,36 @@ +// { dg-do run { target c++26 } } + +#include <debug/inplace_vector> +#include <testsuite_hooks.h> + +using __gnu_debug::inplace_vector; + +void test01() +{ + inplace_vector<int, 10> v; + + for (int i = 0; i != 10; ++i) + v.push_back(i); + + auto before = v.begin() + 4; + auto last = v.end() - 1; + + VERIFY( std::erase(v, 6) == 1 ); + + VERIFY(before._M_dereferenceable()); + VERIFY(last._M_singular()); +} + +void test02() +{ + inplace_vector<int, 0> v; + + VERIFY( std::erase(v, 6) == 0 ); +} + +int main() +{ + test01(); + test02(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/pop_back.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/pop_back.cc new file mode 100644 index 0000000..4c1a043 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/pop_back.cc @@ -0,0 +1,27 @@ +// { dg-do run { target c++26 } } + +#include <debug/inplace_vector> +#include <testsuite_hooks.h> + +using __gnu_debug::inplace_vector; + +void test01() +{ + inplace_vector<int, 100> v(10, 17); + + auto before = v.begin() + 6; + auto last = v.end(); + auto end = last--; + + v.pop_back(); + + VERIFY(before._M_dereferenceable()); + VERIFY(last._M_singular()); + VERIFY(end._M_singular()); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/push_back.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/push_back.cc new file mode 100644 index 0000000..f269364 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/push_back.cc @@ -0,0 +1,53 @@ +// { dg-do run { target c++26 } } + +#include <debug/inplace_vector> +#include <testsuite_hooks.h> + +using __gnu_debug::inplace_vector; + +void test01() +{ + inplace_vector<int, 100> v(10, 17); + + auto before = v.begin() + 6; + auto last = v.end(); + auto end = last--; + + v.push_back(42); + + VERIFY(before._M_dereferenceable()); + VERIFY(last._M_dereferenceable()); + VERIFY(end._M_singular()); +} + +#if __cpp_exceptions +void test02() +{ + inplace_vector<int, 10> v(10, 17); + + auto before = v.begin() + 6; + auto last = v.end(); + auto end = last--; + try + { + v.push_back(42); + VERIFY( false ); + } + catch (std::bad_alloc&) + { + } + + VERIFY(before._M_dereferenceable()); + VERIFY(last._M_dereferenceable()); + VERIFY(!end._M_singular()); +} +#endif + +int main() +{ + test01(); +#if __cpp_exceptions + test02(); +#endif + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/swap.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/swap.cc new file mode 100644 index 0000000..3e5ab74 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/swap.cc @@ -0,0 +1,53 @@ +// { dg-do run { target c++26 } } + +#include <debug/inplace_vector> +#include <testsuite_hooks.h> + +using __gnu_debug::inplace_vector; + +void test01() +{ + inplace_vector<int, 10> v1; + inplace_vector<int, 10> v2; + + for (int i = 0; i != 10; ++i) + { + v1.push_back(i); + v2.push_back(i); + } + + auto it1 = v1.begin(); + auto it2 = v2.begin(); + + std::swap(v1, v2); + + VERIFY(it1._M_singular()); + VERIFY(it2._M_singular()); +} + +void test02() +{ + inplace_vector<int, 10> v1; + inplace_vector<int, 10> v2; + + for (int i = 0; i != 10; ++i) + { + v1.push_back(i); + v2.push_back(i); + } + + auto it1 = v1.begin(); + auto it2 = v2.begin(); + + swap(v1, v2); + + VERIFY(it1._M_singular()); + VERIFY(it2._M_singular()); +} + +int main() +{ + test01(); + test02(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/try_append_range.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/try_append_range.cc new file mode 100644 index 0000000..ae4ac41 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/try_append_range.cc @@ -0,0 +1,45 @@ +// { dg-do run { target c++26 } } + +#include <debug/inplace_vector> +#include <testsuite_hooks.h> + +using __gnu_debug::inplace_vector; + +void test01() +{ + inplace_vector<int, 100> v(10, 17); + inplace_vector<int, 10> v1(10, 19); + + auto before = v.begin() + 6; + auto last = v.end(); + auto end = last--; + + v.try_append_range(v1); + + VERIFY(before._M_dereferenceable()); + VERIFY(last._M_dereferenceable()); + VERIFY(end._M_singular()); +} + +void test02() +{ + inplace_vector<int, 100> v(10, 17); + inplace_vector<int, 0> v1; + + auto before = v.begin() + 6; + auto last = v.end(); + auto end = last--; + + v.try_append_range(v1); + + VERIFY(before._M_dereferenceable()); + VERIFY(last._M_dereferenceable()); + VERIFY(!end._M_singular()); +} + +int main() +{ + test01(); + test02(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/try_emplace_back.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/try_emplace_back.cc new file mode 100644 index 0000000..f7c8c7a --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/try_emplace_back.cc @@ -0,0 +1,27 @@ +// { dg-do run { target c++26 } } + +#include <debug/inplace_vector> +#include <testsuite_hooks.h> + +using __gnu_debug::inplace_vector; + +void test01() +{ + inplace_vector<int, 100> v(10, 17); + + auto before = v.begin() + 6; + auto last = v.end(); + auto end = last--; + + VERIFY( v.try_emplace_back(42) != nullptr ); + + VERIFY(before._M_dereferenceable()); + VERIFY(last._M_dereferenceable()); + VERIFY(end._M_singular()); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/try_push_back.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/try_push_back.cc new file mode 100644 index 0000000..04fc010 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/try_push_back.cc @@ -0,0 +1,45 @@ +// { dg-do run { target c++26 } } + +#include <debug/inplace_vector> +#include <vector> +#include <testsuite_hooks.h> + +using __gnu_debug::inplace_vector; + +void test01() +{ + inplace_vector<int, 100> v(10, 17); + + auto before = v.begin() + 6; + auto last = v.end(); + auto end = last--; + + VERIFY( v.try_push_back(42) != nullptr ); + + VERIFY(before._M_dereferenceable()); + VERIFY(last._M_dereferenceable()); + VERIFY(end._M_singular()); +} + +void test02() +{ + std::vector<int> vv { 0, 1, 2, 3, 4, 5 }; + inplace_vector<std::vector<int>, 100> v(10, vv); + + auto before = v.begin() + 6; + auto last = v.end(); + auto end = last--; + + VERIFY( v.try_push_back(std::move(vv)) != nullptr ); + + VERIFY(before._M_dereferenceable()); + VERIFY(last._M_dereferenceable()); + VERIFY(end._M_singular()); +} + +int main() +{ + test01(); + test02(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/unchecked_emplace_back.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/unchecked_emplace_back.cc new file mode 100644 index 0000000..0d173d5 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/invalidation/unchecked_emplace_back.cc @@ -0,0 +1,27 @@ +// { dg-do run { target c++26 } } + +#include <debug/inplace_vector> +#include <testsuite_hooks.h> + +using __gnu_debug::inplace_vector; + +void test01() +{ + inplace_vector<int, 100> v(10, 17); + + auto before = v.begin() + 6; + auto last = v.end(); + auto end = last--; + + v.unchecked_emplace_back(42); + + VERIFY(before._M_dereferenceable()); + VERIFY(last._M_dereferenceable()); + VERIFY(end._M_singular()); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/erasure.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/erasure.cc new file mode 100644 index 0000000..8fb56e9 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/erasure.cc @@ -0,0 +1,69 @@ +// { dg-do run { target c++26 } } + +#include <inplace_vector> +#include <testsuite_hooks.h> +#include <span> + +template<typename T, size_t N> +constexpr bool +eq(const std::inplace_vector<T, N>& l, std::span<const T> r) { + if (l.size() != r.size()) + return false; + for (auto i = 0u; i < l.size(); ++i) + if (l[i] != r[i]) + return false; + return true; +}; + +constexpr void +test_erase() +{ + std::inplace_vector<int, 15> c{1, 0, 3, 4, 5, 6, 5, 4, 3, 0, 1, 4, 4, 9}; + std::erase(c, 4); + VERIFY( c.size() == 10 ); + std::erase(c, 1); + VERIFY( c.size() == 8 ); + std::erase(c, 9); + VERIFY( c.size() == 7 ); + VERIFY( eq(c, {0, 3, 5, 6, 5, 3, 0}) ); + + std::erase(c, {}); + VERIFY( c.size() == 5 ); + VERIFY( eq(c, {3, 5, 6, 5, 3}) ); + + std::erase(c, {5}); + VERIFY( c.size() == 3 ); + VERIFY( eq(c, {3, 6, 3}) ); + + std::inplace_vector<int, 0> e; + std::erase(e, 10); + VERIFY( e.empty() ); +} + +constexpr void +test_erase_if() +{ + std::inplace_vector<int, 15> c{1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1, 4, 4, 9}; + std::erase_if(c, [](int i) { return i > 5; }); + VERIFY( c.size() == 12 ); + std::erase_if(c, [](int i) { return i == 4; }); + VERIFY( c.size() == 8 ); + std::erase_if(c, [](int i) { return i & 1; }); + VERIFY( eq(c, {2, 2}) ); + + std::inplace_vector<int, 0> e; + std::erase_if(e, [](int i) { return i > 5; }); + VERIFY( e.empty() ); +} + +int main() +{ + test_erase(); + test_erase_if(); + + constexpr bool _ = [] { + test_erase(); + test_erase_if(); + return true; + }(); +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/assign.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/assign.cc new file mode 100644 index 0000000..65b505e --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/assign.cc @@ -0,0 +1,379 @@ +// { dg-do run { target c++26 } } + +#include <inplace_vector> + +#include <span> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> + +struct X +{ + X() = default; + constexpr X(int p) : v(p) {} + constexpr X(const X& o) : v(o.v) { } // not trivial + constexpr X& operator=(const X& o) // not trivial + { v = o.v; return *this; } + + int v; + + friend auto operator<=>(const X&, const X&) = default; +}; + +template<typename T, typename V, size_t N> +constexpr bool +eq(const std::inplace_vector<V, N>& l, std::span<const T> r) { + if (l.size() != r.size()) + return false; + for (auto i = 0u; i < l.size(); ++i) + if (l[i] != r[i]) + return false; + return true; +}; + +template<size_t N, typename T, template<class TT> class ItType> +constexpr void +test_assign_empty_it() +{ + using namespace __gnu_test; + + T a[]{1,2,3,4,5,6,7,8,9,10}; + using It = ItType<T>; + using Range = test_range<T, ItType>; + using SizedRange = test_sized_range<T, ItType>; + + const std::inplace_vector<T, N> src(std::from_range, std::span(a, N)); + std::inplace_vector<T, N> v; + + v = src; + v.assign_range(Range(a, a)); + VERIFY( v.empty() ); + v.assign_range(Range(a, a)); + VERIFY( v.empty() ); + + v = src; + v.assign_range(SizedRange(a, a)); + VERIFY( v.empty() ); + + v = src; + auto bounds = typename It::ContainerType(a, a+9); + v.assign(It(a, &bounds), It(a, &bounds)); + VERIFY( v.empty() ); + +#ifdef __cpp_exceptions +#ifdef __cpp_lib_constexpr_exceptions +#error remove the consteval check +#endif + if consteval { + return; + } + + static_assert(N < 9); + + v = src; + try + { + v.assign_range(Range(a, a+9)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + if constexpr (std::ranges::sized_range<Range> || std::ranges::forward_range<Range>) + VERIFY( eq<T>(v, {a, N}) ); + + v = src; + try + { + v.assign_range(SizedRange(a, a+9)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, N}) ); + + v = src; + bounds = typename It::ContainerType(a, a+9); + try + { + v.assign(It(a, &bounds), It(a+9, &bounds)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + if constexpr(std::forward_iterator<It>) + VERIFY( eq<T>(v, {a, N}) ); +#endif +} + +template<size_t N, typename T> +constexpr void +test_assign_empty_other() +{ + T a[]{1,2,3,4,5,6,7,8,9,10}; + const std::inplace_vector<T, N> src(std::from_range, std::span(a, N)); + std::inplace_vector<T, N> v; + + v = src; + v.assign(0, T(4)); + VERIFY( v.empty() ); + + v = src; + v.assign({}); + VERIFY( v.empty() ); + + v = src; + v = {}; + VERIFY( v.empty() ); + + v = src; + v.resize(0, T(3)); + VERIFY( v.empty() ); + + v = src; + v.resize(0); + VERIFY( v.empty() ); + +#ifdef __cpp_exceptions +#ifdef __cpp_lib_constexpr_exceptions +#error remove the consteval check +#endif + if consteval { + return; + } + + static_assert(N < 9); + + v = src; + try + { + v.assign(9, T(4)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, N}) ); + + std::initializer_list<T> il = + {T(0), T(1), T(2), T(3), T(4), T(5), T(6), T(7), T(8), T(9), T(10)}; + try + { + v.assign(il); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, N}) ); + + try + { + v = il; + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, N}) ); + + try + { + v.resize(9, T(3)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, N}) ); + + try + { + v.resize(9); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, N}) ); +#endif +} + +template<size_t N, typename T> +constexpr void +test_assign_empty() +{ + using namespace __gnu_test; + test_assign_empty_it<N, T, input_iterator_wrapper>(); + test_assign_empty_it<N, T, forward_iterator_wrapper>(); + test_assign_empty_it<N, T, random_access_iterator_wrapper>(); + + test_assign_empty_other<N, T>; +} + +template<typename Range> +constexpr void +test_assign_range() +{ + using T = std::ranges::range_value_t<Range>; + T a[]{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + + std::inplace_vector<T, 10> v; + v.assign_range(Range(a,a+5)); + VERIFY( eq<T>(v, {a, 5}) ); + + v.assign_range(Range(a,a+7)); + VERIFY( eq<T>(v, {a, 7}) ); + + v.assign_range(Range(a,a+3)); + VERIFY( eq<T>(v, {a, 3}) ); + + v.assign_range(Range(a,a+10)); + VERIFY( eq<T>(v, {a, 10}) ); +} + +template<typename T, template<class TT> class ItType> +constexpr void +test_assign_iterators() +{ + T a[]{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + using It = ItType<T>; + + std::inplace_vector<T, 10> v; + + auto bounds = typename It::ContainerType(a, a+15); + v.assign(It(a, &bounds), It(a+5, &bounds)); + VERIFY( eq<T>(v, {a, 5}) ); + + bounds = typename It::ContainerType(a, a+15); + v.assign(It(a, &bounds), It(a+7, &bounds)); + VERIFY( eq<T>(v, {a, 7}) ); + + bounds = typename It::ContainerType(a, a+15); + v.assign(It(a, &bounds), It(a+3, &bounds)); + VERIFY( eq<T>(v, {a, 3}) ); + + bounds = typename It::ContainerType(a, a+15); + v.assign(It(a, &bounds), It(a+10, &bounds)); + VERIFY( eq<T>(v, {a, 10}) ); +} + +template<typename T> +constexpr void +test_assign_initializer_list() +{ + T a[]{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + + std::inplace_vector<T, 10> v; + + v.assign({T(1), T(2), T(3), T(4), T(5)}); + VERIFY( eq<T>(v, {a, 5}) ); + + v = {T(1), T(2), T(3), T(4), T(5), T(6), T(7)}; + VERIFY( eq<T>(v, {a, 7}) ); + + v.assign({T(1), T(2), T(3)}); + VERIFY( eq<T>(v, {a, 3}) ); + + v = {T(1), T(2), T(3), T(4), T(5), T(6), T(7), T(8), T(9), T(10)}; + VERIFY( eq<T>(v, {a, 10}) ); +} + +template<typename T> +constexpr void +test_assign_repeated() +{ + auto rep = [](const std::inplace_vector<T, 10>& v, size_t c, const T& t) + { + if (v.size() != c) + return false; + for (const T& o : v) + if (o != t) + return false; + return true; + }; + + std::inplace_vector<T, 10> v; + + v.assign(5, T(1)); + VERIFY( rep(v, 5, T(1)) ); + + v.assign(7, T(2)); + VERIFY( rep(v, 7, T(2)) ); + + v.assign(3, T(4)); + VERIFY( rep(v, 3, T(4)) ); + + v.assign(10, T(8)); + VERIFY( rep(v, 10, T(8)) ); +} + +template<typename T> +constexpr void +test_resize() +{ + T a[]{1,1,1,1,2,2,2,0,0,0}; + + std::inplace_vector<T, 10> v; + + v.resize(4, T(1)); + VERIFY( eq<T>(v, {a, 4}) ); + + v.resize(7, T(2)); + VERIFY( eq<T>(v, {a, 7}) ); + + v.resize(10); + VERIFY( eq<T>(v, {a, 10}) ); + + v.resize(6, T(1)); + VERIFY( eq<T>(v, {a, 6}) ); +} + +template<typename T> +constexpr void +test_assigns() +{ + using namespace __gnu_test; + test_assign_range<test_forward_range<int>>(); + test_assign_range<test_sized_range_sized_sent<int, forward_iterator_wrapper>>(); + + test_assign_range<test_input_range<int>>(); + test_assign_range<test_input_sized_range<int>>(); + test_assign_range<test_sized_range_sized_sent<int, input_iterator_wrapper>>(); + + test_assign_range<test_range<int, input_iterator_wrapper_nocopy>>(); + test_assign_range<test_sized_range<int, input_iterator_wrapper_nocopy>>(); + test_assign_range<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>(); + + test_assign_iterators<T, input_iterator_wrapper>(); + test_assign_iterators<T, forward_iterator_wrapper>(); + test_assign_iterators<T, random_access_iterator_wrapper>(); + + test_assign_initializer_list<T>(); + test_assign_repeated<T>(); + test_resize<T>(); +} + +int main() +{ + auto test_all = [] { + test_assign_empty<0, int>(); + test_assign_empty<0, X>(); + test_assign_empty<2, int>(); + + test_assigns<int>(); +#ifdef __cpp_lib_constexpr_inplace_vector +#error uncomemnt test_inserts<X>() +#endif + if !consteval { + test_assign_empty<2, X>(); + test_assigns<X>(); + } + return true; + }; + + + test_all(); + static_assert(test_all()); +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/erase.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/erase.cc new file mode 100644 index 0000000..8b82ab4 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/erase.cc @@ -0,0 +1,117 @@ +// { dg-do run { target c++26 } } + +#include <inplace_vector> + +#include <span> +#include <testsuite_hooks.h> + +struct X +{ + X() = default; + constexpr X(int p) : v(p) {} + constexpr X(const X& o) : v(o.v) { } // not trivial + constexpr X& operator=(const X& o) // not trivial + { v = o.v; return *this; } + + int v; + + friend auto operator<=>(const X&, const X&) = default; +}; + +template<typename T, typename V, size_t N> +constexpr bool +eq(const std::inplace_vector<V, N>& l, std::span<const T> r) { + if (l.size() != r.size()) + return false; + for (auto i = 0u; i < l.size(); ++i) + if (l[i] != r[i]) + return false; + return true; +}; + +template<typename T, typename V, size_t N> +constexpr bool +eq(const std::inplace_vector<V, N>& l, std::initializer_list<T> r) +{ return eq<T>(l, std::span<const T>(r)); } + +template<size_t N, typename T> +constexpr void +test_erase_all_or_none() +{ + using namespace __gnu_test; + + T a[]{1,2,3,4,5,6,7,8,9}; + const T c(10); + + std::inplace_vector<T, N> src(std::from_range, std::span(a, a+N)); + std::inplace_vector<T, N> v; + + v = src; + auto it = v.erase(v.begin(), v.begin()); + VERIFY( it == v.begin() ); + VERIFY( eq<T>(v, {a, N}) ); + + it = v.erase(v.end(), v.end()); + VERIFY( it == v.end() ); + VERIFY( eq<T>(v, {a, N}) ); + + it = v.erase(v.begin(), v.end()); + VERIFY( it == v.begin() ); + VERIFY( v.empty() ); + + v = src; + v.clear(); + VERIFY( v.empty() ); +} + +template<typename T> +constexpr void +test_erase() +{ + std::inplace_vector<T, 10> v{T(1), T(2), T(3), T(4), T(5), T(6), T(7)}; + + auto it = v.erase(v.begin()); + VERIFY( eq<T>(v, {T(2), T(3), T(4), T(5), T(6), T(7)}) ); + VERIFY( it == v.begin() ); + + it = v.erase(v.end()-1); + VERIFY( eq<T>(v, {T(2), T(3), T(4), T(5), T(6)}) ); + VERIFY( it == v.end() ); + + it = v.erase(v.begin()+2, v.begin()+4); + VERIFY( eq<T>(v, {T(2), T(3), T(6)}) ); + VERIFY( it == v.begin()+2 ); + + it = v.erase(v.end()-1, v.end()); + VERIFY( eq<T>(v, {T(2), T(3)}) ); + VERIFY( it == v.end() ); + + it = v.erase(v.begin(), v.begin()+1); + VERIFY( eq<T>(v, {T(3)}) ); + VERIFY( it == v.begin() ); + + v.pop_back(); + VERIFY( v.empty() ); +} + +int main() +{ + auto test_all = [] { + test_erase_all_or_none<0, int>(); + test_erase_all_or_none<0, X>(); + + test_erase_all_or_none<4, int>(); + + test_erase<int>(); +#ifdef __cpp_lib_constexpr_inplace_vector +#error uncomemnt test_inserts<X>() +#endif + if ! consteval { + test_erase_all_or_none<4, X>(); + } + return true; + }; + + test_all(); + static_assert(test_all());; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/erase_neg.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/erase_neg.cc new file mode 100644 index 0000000..a1f43b3 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/erase_neg.cc @@ -0,0 +1,43 @@ +// { dg-do compile { target c++26 } } + +#include <inplace_vector> + +template<size_t N, typename T> +constexpr bool +test_pop_back_on_empty() +{ + std::inplace_vector<T, N> v; + v.pop_back(); // { dg-error "in 'constexpr' expansion of" } + return true; +} + +template<size_t N, typename T> +constexpr bool +test_erase_begin_on_empty() +{ + std::inplace_vector<T, N> v; + v.erase(v.begin()); // { dg-error "in 'constexpr' expansion of" } + return true; +} + +template<size_t N, typename T> +constexpr bool +test_erase_end(size_t size = 0) +{ + std::inplace_vector<T, N> v(size, T()); + v.erase(v.end()); // { dg-error "in 'constexpr' expansion of" } + return true; +} + +static_assert(test_pop_back_on_empty<0, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_pop_back_on_empty<4, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_erase_begin_on_empty<0, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_erase_begin_on_empty<4, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_erase_end<0, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_erase_end<4, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_erase_end<4, int>(2)); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_erase_end<4, int>(4)); // { dg-error "in 'constexpr' expansion of" } + +// { dg-prune-output "non-constant condition for static assertion" } +// { dg-prune-output "is not a constant expression" } +// { dg-prune-output "call to non-'constexpr' function" } diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/multi_insert.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/multi_insert.cc new file mode 100644 index 0000000..6a5b62f --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/multi_insert.cc @@ -0,0 +1,606 @@ +// { dg-do run { target c++26 } } + +#include <inplace_vector> + +#include <span> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> + +struct X +{ + X() = default; + constexpr X(int p) : v(p) {} + constexpr X(const X& o) : v(o.v) { } // not trivial + constexpr X& operator=(const X& o) // not trivial + { v = o.v; return *this; } + + int v; + + friend auto operator<=>(const X&, const X&) = default; +}; + +template<typename T, typename V, size_t N> +constexpr bool +eq(const std::inplace_vector<V, N>& l, std::span<const T> r) { + if (l.size() != r.size()) + return false; + for (auto i = 0u; i < l.size(); ++i) + if (l[i] != r[i]) + return false; + return true; +}; + +template<typename T, typename V, size_t N> +constexpr bool +prefix(const std::inplace_vector<V, N>& l, std::span<const T> r) { + if (l.size() < r.size()) + return false; + for (auto i = 0u; i < r.size(); ++i) + if (l[i] != r[i]) + return false; + return true; +}; + +template<size_t N, typename T, template<class TT> class ItType> +constexpr void +test_add_to_full_it() +{ + using namespace __gnu_test; + + T a[]{1,2,3,4,5,6,7,8,9}; + using It = ItType<T>; + using Range = test_range<T, ItType>; + using SizedRange = test_sized_range<T, ItType>; + + std::inplace_vector<T, N> v(std::from_range, std::span(a, a+N)); + + Range r1(a, a); + auto rit1 = v.try_append_range(r1); + VERIFY( eq<T>(v, {a, N}) ); + VERIFY( rit1.base() == a ); + + SizedRange r2(a, a); + auto rit2 = v.try_append_range(r2); + VERIFY( eq<T>(v, {a, N}) ); + VERIFY( rit2.base() == a ); + + v.append_range(Range(a, a)); + VERIFY( eq<T>(v, {a, N}) ); + v.append_range(SizedRange(a, a)); + VERIFY( eq<T>(v, {a, N}) ); + + auto it = v.insert_range(v.end(), Range(a, a)); + VERIFY( eq<T>(v, {a, N}) ); + VERIFY( it == v.end() ); + it = v.insert_range(v.end(), SizedRange(a, a)); + VERIFY( eq<T>(v, {a, N}) ); + VERIFY( it == v.end() ); + + auto bounds = typename It::ContainerType(a, a+9); + it = v.insert(v.end(), It(a, &bounds), It(a, &bounds)); + VERIFY( eq<T>(v, {a, N}) ); + VERIFY( it == v.end() ); + + it = v.insert_range(v.begin(), SizedRange(a, a)); + VERIFY( eq<T>(v, {a, N}) ); + VERIFY( it == v.begin() ); + it = v.insert_range(v.begin(), Range(a, a)); + VERIFY( eq<T>(v, {a, N}) ); + VERIFY( it == v.begin() ); + + bounds = typename It::ContainerType(a, a+9); + it = v.insert(v.begin(), It(a, &bounds), It(a, &bounds)); + VERIFY( eq<T>(v, {a, N}) ); + VERIFY( it == v.begin() ); + + // Inserting non-empty range + Range r3(a+3, a+5); + auto rit3 = v.try_append_range(r3); + VERIFY( eq<T>(v, {a, N}) ); + VERIFY( rit3.base() == a+3 ); + + SizedRange r4(a+2, a+5); + auto rit4 = v.try_append_range(r4); + VERIFY( eq<T>(v, {a, N}) ); + VERIFY( rit4.base() == a+2 ); + +#ifdef __cpp_exceptions +#ifdef __cpp_lib_constexpr_exceptions +#error remove the consteval check +#endif + if consteval { + return; + } + + try + { + v.append_range(Range(a, a + 5)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, N}) ); + + try + { + v.append_range(SizedRange(a, a + 5)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, N}) ); + + try + { + v.insert_range(v.begin(), SizedRange(a, a+5)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, N}) ); + + try + { + v.insert_range(v.begin(), Range(a, a+5)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + auto gn = std::ranges::sized_range<Range> || std::ranges::forward_range<Range> ? N : 0; + VERIFY( prefix<T>(v, {a, gn}) ); + + v = std::inplace_vector<T, N>(std::from_range, std::span(a, a+N)); + try + { + v.insert_range(v.begin(), Range(a, a+5)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + gn = std::forward_iterator<It> ? N : 0; + VERIFY( prefix<T>(v, {a, gn}) ); +#endif +} + +template<size_t N, typename T> +constexpr void +test_add_to_full_other() +{ + using namespace __gnu_test; + + T a[]{1,2,3,4,5,6,7,8,9}; + std::inplace_vector<T, N> v(std::from_range, std::span(a, a+N)); + + auto it = v.insert(v.end(), {}); + VERIFY( eq<T>(v, {a, N}) ); + VERIFY( it == v.end() ); + it = v.insert(v.end(), 0u, T(2)); + VERIFY( eq<T>(v, {a, N}) ); + VERIFY( it == v.end() ); + + it = v.insert(v.begin(), {}); + VERIFY( eq<T>(v, {a, N}) ); + VERIFY( it == v.begin() ); + it = v.insert(v.begin(), 0u, T(2)); + VERIFY( eq<T>(v, {a, N}) ); + VERIFY( it == v.begin() ); + +#ifdef __cpp_exceptions +#ifdef __cpp_lib_constexpr_exceptions +#error remove the consteval check +#endif + if consteval { + return; + } + + v = std::inplace_vector<T, N>(std::from_range, std::span(a, a+N)); + try + { + v.insert(v.begin(), {T(1), T(2), T(3)}); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, N}) ); + + try + { + v.insert(v.begin(), 4u, T(3)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, N}) ); +#endif +} + + +template<size_t N, typename T> +constexpr void +test_add_to_full() +{ + using namespace __gnu_test; + test_add_to_full_it<N, T, input_iterator_wrapper>(); + test_add_to_full_it<N, T, forward_iterator_wrapper>(); + test_add_to_full_it<N, T, random_access_iterator_wrapper>(); + + test_add_to_full_other<N, T>(); +} + +template<typename Range> +constexpr void +test_append_range() +{ + using T = std::ranges::range_value_t<Range>; + T a[]{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + + std::inplace_vector<T, 20> v; + v.append_range(Range(a,a+10)); + VERIFY( eq<T>(v, {a, 10}) ); + + v.append_range(Range(a+10, a+15)); + VERIFY( eq<T>(v, {a, 15}) ); + +#ifdef __cpp_exceptions +#ifdef __cpp_lib_constexpr_exceptions +#error remove the consteval check +#endif + if consteval { + return; + } + + try + { + v.append_range(Range(a, a+10)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( prefix<T>(v, {a, 15}) ); +#endif +} + +template<typename Range> +constexpr void +test_try_append_range() +{ + using T = std::ranges::range_value_t<Range>; + T a[]{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25}; + + std::inplace_vector<T, 20> v; + Range r1 = Range(a, a+10); + auto it1 = v.try_append_range(r1); + VERIFY( eq<T>(v, {a, 10}) ); + VERIFY( it1.base() == a+10 ); + + Range r2 = Range(a+10, a+15); + auto it2 = v.try_append_range(r2); + VERIFY( eq<T>(v, {a, 15}) ); + VERIFY( it2.base() == a+15 ); + + Range r3 = Range(a+15, a+25); + auto it3 = v.try_append_range(r3); + VERIFY( eq<T>(v, {a, 20}) ); + VERIFY( it3.base() == a+20 ); +} + +template<typename Range> +constexpr void +test_insert_range() +{ + using T = std::ranges::range_value_t<Range>; + T a[]{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + + std::inplace_vector<T, 20> v; + auto it = v.insert_range(v.begin(), Range(a+10,a+15)); + VERIFY( eq<T>(v, {a+10, 5}) ); + VERIFY( it == v.begin() ); + + it = v.insert_range(v.begin(), Range(a, a+5)); + VERIFY( prefix<T>(v, {a, 5}) ); + VERIFY( it == v.begin() ); + + it = v.insert_range(v.begin() + 5, Range(a+5, a+10)); + VERIFY( eq<T>(v, {a, 15}) ); + VERIFY( it == v.begin() + 5 ); + +#ifdef __cpp_exceptions +#ifdef __cpp_lib_constexpr_exceptions +#error remove the consteval check +#endif + if consteval { + return; + } + + const bool seg = std::ranges::sized_range<Range> || std::ranges::forward_range<Range>; + auto vc = v; + try + { + vc.insert_range(vc.begin(), Range(a, a+10)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( prefix<T>(vc, {a, seg ? 15 : 0}) ); + + vc = v; + try + { + vc.insert_range(vc.begin()+5, Range(a, a+10)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( prefix<T>(vc, {a, seg ? 15 : 5}) ); + + vc = v; + try + { + vc.insert_range(vc.end(), Range(a, a+10)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( prefix<T>(vc, {a, 15}) ); +#endif +} + +template<typename Range> +constexpr void +do_test_ranges() +{ + test_append_range<Range>(); + test_try_append_range<Range>(); + test_insert_range<Range>(); +} + +template<typename T, template<class TT> class ItType> +constexpr void +test_insert_iterators() +{ + T a[]{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + using It = ItType<T>; + + std::inplace_vector<T, 20> v; + + auto bounds = typename It::ContainerType(a, a+15); + auto it = v.insert(v.begin(), It(a+10, &bounds), It(a+15, &bounds)); + VERIFY( eq<T>(v, {a+10, 5}) ); + VERIFY( it == v.begin() ); + + bounds = typename It::ContainerType(a, a+15); + it = v.insert(v.begin(), It(a, &bounds), It(a+5, &bounds)); + VERIFY( prefix<T>(v, {a, 5}) ); + VERIFY( it == v.begin() ); + + bounds = typename It::ContainerType(a, a+15); + it = v.insert(v.begin() + 5, It(a+5, &bounds), It(a+10, &bounds)); + VERIFY( eq<T>(v, {a, 15}) ); + VERIFY( it == v.begin() + 5 ); + +#ifdef __cpp_exceptions +#ifdef __cpp_lib_constexpr_exceptions +#error remove the consteval check +#endif + if consteval { + return; + } + + const bool seg = std::forward_iterator<It>; + auto vc = v; + bounds = typename It::ContainerType(a, a+15); + try + { + vc.insert(vc.begin(), It(a, &bounds), It(a+10, &bounds)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( prefix<T>(vc, {a, seg ? 15 : 0}) ); + + vc = v; + bounds = typename It::ContainerType(a, a+15); + try + { + vc.insert(vc.begin()+5, It(a, &bounds), It(a+10, &bounds)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( prefix<T>(vc, {a, seg ? 15 : 5}) ); + + vc = v; + bounds = typename It::ContainerType(a, a+15); + try + { + vc.insert(vc.end(), It(a, &bounds), It(a+10, &bounds)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( prefix<T>(vc, {a, 15}) ); +#endif +} + +template<typename T> +constexpr void +test_insert_initializer_list() +{ + T a[]{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + + std::inplace_vector<T, 20> v; + + auto it = v.insert(v.begin(), {T(11), T(12), T(13), T(14), T(15)}); + VERIFY( eq<T>(v, {a+10, 5}) ); + VERIFY( it == v.begin() ); + + it = v.insert(v.begin(), {T(1), T(2), T(3), T(4), T(5)}); + VERIFY( prefix<T>(v, {a, 5}) ); + VERIFY( it == v.begin() ); + + it = v.insert(v.begin() + 5, {T(6), T(7), T(8), T(9), T(10)}); + VERIFY( eq<T>(v, {a, 15}) ); + VERIFY( it == v.begin() + 5 ); + +#ifdef __cpp_exceptions +#ifdef __cpp_lib_constexpr_exceptions +#error remove the consteval check +#endif + if consteval { + return; + } + + std::initializer_list<T> il + = {T(0), T(1), T(2), T(3), T(4), T(5), T(6), T(7), T(8), T(9)}; + + try + { + v.insert(v.begin(), il); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, 15}) ); + + try + { + v.insert(v.begin()+5, il); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, 15}) ); + + try + { + v.insert(v.end(), il); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, 15}) ); +#endif +} + +template<typename T> +constexpr void +test_insert_repeated() +{ + T a[]{5,5,5,5,5,6,6,6,6,6,7,7,7,7,7}; + + std::inplace_vector<T, 20> v; + + auto it = v.insert(v.begin(), 5, T(7)); + VERIFY( eq<T>(v, {a+10, 5}) ); + VERIFY( it == v.begin() ); + + it = v.insert(v.begin(), 5, T(5)); + VERIFY( prefix<T>(v, {a, 5}) ); + VERIFY( it == v.begin() ); + + it = v.insert(v.begin() + 5, 5, T(6)); + VERIFY( eq<T>(v, {a, 15}) ); + VERIFY( it == v.begin() + 5 ); + +#ifdef __cpp_exceptions +#ifdef __cpp_lib_constexpr_exceptions +#error remove the consteval check +#endif + if consteval { + return; + } + + try + { + v.insert(v.begin(), 10u, T(6)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, 15}) ); + + try + { + v.insert(v.begin()+5, 10u, T(6)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, 15}) ); + + try + { + v.insert(v.end(), 10u, T(6)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, 15}) ); +#endif +} + +template<typename T> +constexpr void +test_inserts() +{ + using namespace __gnu_test; + do_test_ranges<test_forward_range<int>>(); + do_test_ranges<test_sized_range_sized_sent<int, forward_iterator_wrapper>>(); + + do_test_ranges<test_input_range<int>>(); + do_test_ranges<test_input_sized_range<int>>(); + do_test_ranges<test_sized_range_sized_sent<int, input_iterator_wrapper>>(); + + do_test_ranges<test_range<int, input_iterator_wrapper_nocopy>>(); + do_test_ranges<test_sized_range<int, input_iterator_wrapper_nocopy>>(); + do_test_ranges<test_sized_range_sized_sent<int, input_iterator_wrapper_nocopy>>(); + + test_insert_iterators<T, input_iterator_wrapper>(); + test_insert_iterators<T, forward_iterator_wrapper>(); + test_insert_iterators<T, random_access_iterator_wrapper>(); + +test_insert_initializer_list<T>(); +test_insert_repeated<T>(); +} + +int main() +{ +auto test_all = []{ + test_add_to_full<0, int>(); + test_add_to_full<0, X>(); + test_add_to_full<4, int>(); + + test_inserts<int>(); +#ifdef __cpp_lib_constexpr_inplace_vector +#error uncomemnt test_inserts<X>() +#endif + if !consteval { + test_add_to_full<4, X>(); + test_inserts<X>(); + } + return true; + }; + + test_all(); + static_assert(test_all()); +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/single_insert.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/single_insert.cc new file mode 100644 index 0000000..d5e893c --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/single_insert.cc @@ -0,0 +1,215 @@ +// { dg-do run { target c++26 } } + +#include <inplace_vector> + +#include <span> +#include <testsuite_hooks.h> + +struct X +{ + X() = default; + constexpr X(int p) : v(p) {} + constexpr X(const X& o) : v(o.v) { } // not trivial + constexpr X& operator=(const X& o) // not trivial + { v = o.v; return *this; } + + int v; + + friend auto operator<=>(const X&, const X&) = default; +}; + +template<typename T, typename V, size_t N> +constexpr bool +eq(const std::inplace_vector<V, N>& l, std::span<const T> r) { + if (l.size() != r.size()) + return false; + for (auto i = 0u; i < l.size(); ++i) + if (l[i] != r[i]) + return false; + return true; +}; + +template<size_t N, typename T> +constexpr void +test_add_to_full() +{ + using namespace __gnu_test; + + T a[]{1,2,3,4,5,6,7,8,9}; + const T c(10); + + std::inplace_vector<T, N> v(std::from_range, std::span(a, a+N)); + + VERIFY( v.try_emplace_back(1) == nullptr ); + VERIFY( eq<T>(v, {a, N}) ); + VERIFY( v.try_push_back(T(1)) == nullptr ); + VERIFY( eq<T>(v, {a, N}) ); + VERIFY( v.try_push_back(c) == nullptr ); + VERIFY( eq<T>(v, {a, N}) ); + +#ifdef __cpp_exceptions +#ifdef __cpp_lib_constexpr_exceptions +#error remove the consteval check +#endif + if consteval { + return; + } + + try + { + v.emplace_back(1); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, N}) ); + + try + { + v.push_back(T(1)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, N}) ); + + try + { + v.push_back(c); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, N}) ); + + try + { + v.insert(v.end(), T(1)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, N}) ); + + try + { + v.insert(v.begin(), c); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, N}) ); + + try + { + v.emplace(v.end(), c); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, N}) ); + + try + { + v.emplace(v.begin(), T(2)); + VERIFY(false); + } + catch (std::bad_alloc const&) + { + } + VERIFY( eq<T>(v, {a, N}) ); +#endif +} + +template<typename T> +constexpr void +test_inserts() +{ + T a[]{3,14,13,1,2,3,4,5,3,7,8,3,10,11,3}; + const T c(3); + + std::inplace_vector<T, 20> v; + + v.emplace_back(1); + VERIFY( eq<T>(v, {a+3, 1}) ); + v.push_back(T(2)); + VERIFY( eq<T>(v, {a+3, 2}) ); + v.push_back(c); + VERIFY( eq<T>(v, {a+3, 3}) ); + + v.unchecked_emplace_back(4); + VERIFY( eq<T>(v, {a+3, 4}) ); + v.unchecked_push_back(T(5)); + VERIFY( eq<T>(v, {a+3, 5}) ); + v.unchecked_push_back(c); + VERIFY( eq<T>(v, {a+3, 6}) ); + + T* ptr = v.try_emplace_back(7); + VERIFY( eq<T>(v, {a+3, 7}) ); + VERIFY( ptr = &v.back() ); + ptr = v.try_push_back(T(8)); + VERIFY( eq<T>(v, {a+3, 8}) ); + VERIFY( ptr = &v.back() ); + ptr = v.try_push_back(c); + VERIFY( eq<T>(v, {a+3, 9}) ); + VERIFY( ptr = &v.back() ); + + auto it = v.emplace(v.end(), 10); + VERIFY( eq<T>(v, {a+3, 10}) ); + VERIFY( it == v.end()-1 ); + it = v.insert(v.end(), T(11)); + VERIFY( eq<T>(v, {a+3, 11}) ); + VERIFY( it == v.end()-1 ); + it = v.insert(v.end(), c); + VERIFY( eq<T>(v, {a+3, 12}) ); + VERIFY( it == v.end()-1 ); + + it = v.emplace(v.begin(), 13); + VERIFY( eq<T>(v, {a+2, 13}) ); + VERIFY( it == v.begin() ); + it = v.insert(v.begin(), T(14)); + VERIFY( eq<T>(v, {a+1, 14}) ); + VERIFY( it == v.begin() ); + it = v.insert(v.begin(), c); + VERIFY( eq<T>(v, {a+0, 15}) ); + VERIFY( it == v.begin() ); + + it = v.emplace(v.begin()+2, 22); + VERIFY( *it == 22 ); + VERIFY( it == v.begin()+2 ); + it = v.insert(v.begin()+6, T(24)); + VERIFY( *it == 24 ); + VERIFY( it == v.begin()+6 ); + it = v.insert(v.begin()+13, c); + VERIFY( *it == 3 ); + VERIFY( it == v.begin()+13 ); +} + +int main() +{ + auto test_all = [] { + test_add_to_full<0, int>(); + test_add_to_full<0, X>(); + + test_add_to_full<4, int>(); + + test_inserts<int>(); +#ifdef __cpp_lib_constexpr_inplace_vector +#error uncomemnt test_inserts<X>() +#endif + if ! consteval { + test_add_to_full<4, X>(); + test_inserts<X>(); + } + return true; + }; + + test_all(); + static_assert(test_all());; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/single_insert_neg.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/single_insert_neg.cc new file mode 100644 index 0000000..0552b8c --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/modifiers/single_insert_neg.cc @@ -0,0 +1,42 @@ +// { dg-do compile { target c++26 } } + +#include <inplace_vector> + +template<size_t N, typename T> +constexpr bool +test_unchecked_emplace_back() +{ + std::inplace_vector<T, N> v(N, T(1)); + v.unchecked_emplace_back(); // { dg-error "in 'constexpr' expansion of" } + return true; +} + +template<size_t N, typename T> +constexpr bool +test_push_back_lvalue() +{ + auto val = T();; + std::inplace_vector<T, N> v(N, T(1)); + v.unchecked_push_back(val); // { dg-error "in 'constexpr' expansion of" } + return true; +} + +template<size_t N, typename T> +constexpr bool +test_push_back_rvalue() +{ + std::inplace_vector<T, N> v(N, T(1)); + v.unchecked_push_back(T()); // { dg-error "in 'constexpr' expansion of" } + return true; +} + +static_assert(test_unchecked_emplace_back<0, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_unchecked_emplace_back<4, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_push_back_lvalue<0, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_push_back_lvalue<4, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_push_back_rvalue<0, int>()); // { dg-error "in 'constexpr' expansion of" } +static_assert(test_push_back_rvalue<4, int>()); // { dg-error "in 'constexpr' expansion of" } + +// { dg-prune-output "non-constant condition for static assertion" } +// { dg-prune-output "is not a constant expression" } +// { dg-prune-output "call to non-'constexpr' function" } diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/move.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/move.cc new file mode 100644 index 0000000..e8703e0 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/move.cc @@ -0,0 +1,362 @@ +// { dg-do run { target c++26 } } + +#include <inplace_vector> +#include <ranges> +#include <testsuite_hooks.h> + +struct X +{ + X() = default; + constexpr X(int p) : v(p) {} + constexpr X(const X& o) : v(o.v) { } // not trivial + constexpr X(X&& o) : v(o.v) { } // not trivial + + constexpr X& operator=(const X& o) // not trivial + { v = o.v; return *this; } + constexpr X& operator=(X&& o) // not trivial + { v = o.v; return *this; } + + int v; + + friend auto operator<=>(const X&, const X&) = default; +}; + +template<bool CNoex, bool ANoex> +struct N +{ + N() = default; + constexpr N(N&&) noexcept(CNoex) { } // not trivial + constexpr N& operator=(N&& o) noexcept(ANoex) // not trivial + { return *this; } +}; + +struct D +{ + D() = default; + D(D&&) = default; + D& operator=(D&&) = default; + ~D() {} // not trivially destructible +}; + +struct U +{ + U() = default; + U(U&&) noexcept(false) = default; // lies about noexcept, is trivial but throwing + U& operator=(U&&) noexcept(false) = default; // lies about noexcept, is trivial but throwing +}; + +template<bool SNoex, bool CNoex, bool ANoex> +struct S { + S() = default; + constexpr S(S&&) noexcept(CNoex) { } // not trivial + constexpr S& operator=(S&& o) noexcept(ANoex) // not trivial + { return *this; } + + friend constexpr + void swap(S&, S&) noexcept(SNoex) {} +}; + +// n5008 inplace.vector.overview says for inplace_vector<T, 0> +// provides trivial copy/move/default cosntructpr regardless of T +struct Z +{ + Z(const Z&) = delete; + Z& operator=(const Z&) = delete; +}; + +template<size_t N, typename T> + constexpr std::inplace_vector<T, N> + materialize(std::initializer_list<int> il) + { + std::inplace_vector<T, N> res; + for (int x : il) + res.emplace_back(x); + return res; + } + +static_assert(std::is_move_constructible_v<std::inplace_vector<int, 2>>); +static_assert(std::is_move_constructible_v<std::inplace_vector<X, 2>>); +static_assert(std::is_move_constructible_v<std::inplace_vector<N<false, false>, 2>>); +static_assert(std::is_move_constructible_v<std::inplace_vector<D, 2>>); +static_assert(std::is_move_constructible_v<std::inplace_vector<U, 2>>); +// The operators are not constrained, as for any other container +static_assert(std::is_move_constructible_v<std::inplace_vector<Z, 2>>); + +static_assert(std::is_nothrow_move_constructible_v<std::inplace_vector<int, 2>>); +static_assert(!std::is_nothrow_move_constructible_v<std::inplace_vector<X, 2>>); +static_assert(std::is_nothrow_move_constructible_v<std::inplace_vector<N<true, true>, 2>>); +static_assert(std::is_nothrow_move_constructible_v<std::inplace_vector<N<true, false>, 2>>); +static_assert(!std::is_nothrow_move_constructible_v<std::inplace_vector<N<false, true>, 2>>); +static_assert(!std::is_nothrow_move_constructible_v<std::inplace_vector<N<false, false>, 2>>); +static_assert(std::is_nothrow_move_constructible_v<std::inplace_vector<D, 2>>); +static_assert(!std::is_nothrow_move_constructible_v<std::inplace_vector<U, 2>>); + +#if !_GLIBCXX_DEBUG +static_assert(std::is_trivially_move_constructible_v<std::inplace_vector<int, 2>>); +static_assert(!std::is_trivially_move_constructible_v<std::inplace_vector<X, 2>>); +static_assert(!std::is_trivially_move_constructible_v<std::inplace_vector<N<true, true>, 2>>); +// is_trivially_move_constructible_v checks destructor +static_assert(!std::is_trivially_move_constructible_v<std::inplace_vector<D, 2>>); +static_assert(std::is_trivially_move_constructible_v<std::inplace_vector<U, 2>>); +#endif + +static_assert(std::is_move_assignable_v<std::inplace_vector<int, 2>>); +static_assert(std::is_move_assignable_v<std::inplace_vector<X, 2>>); +static_assert(std::is_move_assignable_v<std::inplace_vector<N<false, false>, 2>>); +static_assert(std::is_move_assignable_v<std::inplace_vector<D, 2>>); +static_assert(std::is_move_assignable_v<std::inplace_vector<U, 2>>); +// The operators are not constrained, as for any other container +static_assert(std::is_move_assignable_v<std::inplace_vector<Z, 2>>); + +static_assert(std::is_nothrow_move_assignable_v<std::inplace_vector<int, 2>>); +static_assert(!std::is_nothrow_move_assignable_v<std::inplace_vector<X, 2>>); +static_assert(std::is_nothrow_move_assignable_v<std::inplace_vector<N<true, true>, 2>>); +static_assert(!std::is_nothrow_move_assignable_v<std::inplace_vector<N<true, false>, 2>>); +static_assert(!std::is_nothrow_move_assignable_v<std::inplace_vector<N<false, true>, 2>>); +static_assert(!std::is_nothrow_move_assignable_v<std::inplace_vector<N<false, false>, 2>>); +static_assert(std::is_nothrow_move_assignable_v<std::inplace_vector<D, 2>>); +static_assert(!std::is_nothrow_move_assignable_v<std::inplace_vector<U, 2>>); + +#if !_GLIBCXX_DEBUG +static_assert(std::is_trivially_move_assignable_v<std::inplace_vector<int, 2>>); +static_assert(!std::is_trivially_move_assignable_v<std::inplace_vector<X, 2>>); +static_assert(!std::is_trivially_move_assignable_v<std::inplace_vector<N<true, true>, 2>>); +// destructor is not trivial +static_assert(!std::is_trivially_move_assignable_v<std::inplace_vector<D, 2>>); +static_assert(std::is_trivially_move_assignable_v<std::inplace_vector<U, 2>>); +#endif + +static_assert(std::is_nothrow_swappable_v<std::inplace_vector<int, 2>>); +static_assert(!std::is_nothrow_swappable_v<std::inplace_vector<X, 2>>); +static_assert(std::is_nothrow_swappable_v<std::inplace_vector<N<true, true>, 2>>); +static_assert(!std::is_nothrow_swappable_v<std::inplace_vector<N<true, false>, 2>>); +static_assert(!std::is_nothrow_swappable_v<std::inplace_vector<N<false, true>, 2>>); +static_assert(!std::is_nothrow_swappable_v<std::inplace_vector<N<false, false>, 2>>); +static_assert(std::is_nothrow_swappable_v<std::inplace_vector<S<true, true, true>, 2>>); +static_assert(std::is_nothrow_swappable_v<std::inplace_vector<S<true, true, false>, 2>>); +static_assert(!std::is_nothrow_swappable_v<std::inplace_vector<S<true, false, true>, 2>>); +static_assert(!std::is_nothrow_swappable_v<std::inplace_vector<S<true, false, false>, 2>>); +static_assert(!std::is_nothrow_swappable_v<std::inplace_vector<S<false, true, false>, 2>>); +static_assert(!std::is_nothrow_swappable_v<std::inplace_vector<S<false, false, false>, 2>>); +static_assert(std::is_nothrow_swappable_v<std::inplace_vector<D, 2>>); +static_assert(!std::is_nothrow_swappable_v<std::inplace_vector<U, 2>>); + +static_assert(std::is_nothrow_move_constructible_v<std::inplace_vector<int, 0>>); +static_assert(std::is_nothrow_move_constructible_v<std::inplace_vector<X, 0>>); +static_assert(std::is_nothrow_move_constructible_v<std::inplace_vector<N<false, false>, 0>>); +static_assert(std::is_nothrow_move_constructible_v<std::inplace_vector<D, 0>>); +static_assert(std::is_nothrow_move_constructible_v<std::inplace_vector<U, 0>>); +static_assert(std::is_nothrow_move_constructible_v<std::inplace_vector<Z, 0>>); + +static_assert(std::is_trivially_move_constructible_v<std::inplace_vector<int, 0>>); +static_assert(std::is_trivially_move_constructible_v<std::inplace_vector<X, 0>>); +static_assert(std::is_trivially_move_constructible_v<std::inplace_vector<D, 0>>); +static_assert(std::is_trivially_move_constructible_v<std::inplace_vector<U, 0>>); +static_assert(std::is_trivially_move_constructible_v<std::inplace_vector<Z, 0>>); + +static_assert(std::is_nothrow_move_assignable_v<std::inplace_vector<int, 0>>); +static_assert(std::is_nothrow_move_assignable_v<std::inplace_vector<X, 0>>); +static_assert(std::is_nothrow_move_assignable_v<std::inplace_vector<N<false, false>, 0>>); +static_assert(std::is_nothrow_move_assignable_v<std::inplace_vector<D, 0>>); +static_assert(std::is_nothrow_move_assignable_v<std::inplace_vector<U, 0>>); +static_assert(std::is_nothrow_move_assignable_v<std::inplace_vector<Z, 0>>); + +static_assert(std::is_trivially_move_assignable_v<std::inplace_vector<int, 0>>); +static_assert(std::is_trivially_move_assignable_v<std::inplace_vector<X, 0>>); +static_assert(std::is_trivially_move_assignable_v<std::inplace_vector<N<false, false>, 0>>); +static_assert(std::is_trivially_move_assignable_v<std::inplace_vector<D, 0>>); +static_assert(std::is_trivially_move_assignable_v<std::inplace_vector<U, 0>>); +static_assert(std::is_trivially_move_assignable_v<std::inplace_vector<Z, 0>>); + +static_assert(std::is_nothrow_swappable_v<std::inplace_vector<int, 0>>); +static_assert(std::is_nothrow_swappable_v<std::inplace_vector<X, 0>>); +static_assert(std::is_nothrow_swappable_v<std::inplace_vector<N<false, false>, 0>>); +static_assert(std::is_nothrow_swappable_v<std::inplace_vector<D, 0>>); +static_assert(std::is_nothrow_swappable_v<std::inplace_vector<U, 0>>); +static_assert(std::is_nothrow_swappable_v<std::inplace_vector<Z, 0>>); + +template<typename T, size_t N> +constexpr bool +eq(const std::inplace_vector<T, N>& s, std::span<const T> o) +{ return std::ranges::equal(s, o); } + +constexpr void +test_ctor() +{ + auto e0 = materialize<0, int>({}); + VERIFY( e0.empty() ); + auto e1 = materialize<0, X>({}); + VERIFY( e1.empty() ); + auto e2 = materialize<0, Z>({}); + VERIFY( e2.empty() ); + + auto c0 = materialize<5, int>({}); + VERIFY( c0.empty() ); + + auto c3 = materialize<5, int>({1, 2, 3}); + VERIFY( eq(c3, {1, 2, 3}) ); + + auto c5 = materialize<5, int>({1, 2, 3, 4, 5}); + VERIFY( eq(c5, {1, 2, 3, 4, 5}) ); + +#ifdef __cpp_lib_constexpr_inplace_vector +#error remove the consteval check +#endif + if consteval { + return; + } + + auto x0 = materialize<3, X>({}); + VERIFY( x0.empty() ); + + auto x2 = materialize<3, X>({1, 2}); + VERIFY( eq(x2, {1, 2}) ); + + auto x3 = materialize<3, X>({1, 2, 3}); + VERIFY( eq(x3, {1, 2, 3}) ); +} + +constexpr void +test_assign() +{ + std::inplace_vector<int, 0> e0; + e0 = materialize<0, int>({}); + VERIFY( e0.empty() ); + std::inplace_vector<X, 0> e1; + e1 = materialize<0, X>({}); + VERIFY( e1.empty() ); + std::inplace_vector<Z, 0> e2; + e2 = materialize<0, Z>({}); + VERIFY( e2.empty() ); + + std::inplace_vector<int, 5> c; + c = materialize<5, int>({}); + VERIFY( c.empty() ); + + c = materialize<5, int>({1, 2, 3}); + VERIFY( eq(c, {1, 2, 3}) ); + + c = materialize<5, int>({1, 2, 3, 4, 5}); + VERIFY( eq(c, {1, 2, 3, 4, 5}) ); + + c = materialize<5, int>({4, 5}); + VERIFY( eq(c, {4, 5}) ); + + c = materialize<5, int>({}); + VERIFY( c.empty() ); + +#ifdef __cpp_lib_constexpr_inplace_vector +#error remove the consteval check +#endif + if consteval { + return; + } + + std::inplace_vector<X, 5> x; + x = materialize<5, X>({}); + VERIFY( x.empty() ); + + x = materialize<5, X>({1, 2, 3}); + VERIFY( eq(x, {1, 2, 3}) ); + + x = materialize<5, X>({1, 2, 3, 4, 5}); + VERIFY( eq(x, {1, 2, 3, 4, 5}) ); + + x = materialize<5, X>({4, 5}); + VERIFY( eq(x, {4, 5}) ); + + x = materialize<5, X>({}); + VERIFY( x.empty() ); +} + +constexpr void +test_swap() +{ + std::inplace_vector<int, 0> e0a, e0b; + swap(e0a, e0b); + VERIFY( e0a.empty() ); + VERIFY( e0b.empty() ); + e0a.swap(e0b); + VERIFY( e0a.empty() ); + VERIFY( e0b.empty() ); + + std::inplace_vector<X, 0> e1a, e1b; + swap(e1a, e1b); + VERIFY( e1a.empty() ); + VERIFY( e1b.empty() ); + e1a.swap(e1b); + VERIFY( e1a.empty() ); + VERIFY( e1b.empty() ); + + std::inplace_vector<Z, 0> e2a, e2b; + swap(e2a, e2b); + VERIFY( e2a.empty() ); + VERIFY( e2b.empty() ); + e2a.swap(e2b); + VERIFY( e2a.empty() ); + VERIFY( e2b.empty() ); + + std::inplace_vector<int, 5> c0; + std::inplace_vector<int, 5> c3{1, 2, 3}; + std::inplace_vector<int, 5> c5{1, 2, 3, 4, 5}; + + swap(c0, c3); + VERIFY( c3.empty() ); + VERIFY( eq(c0, {1, 2, 3}) ); + c0.swap(c3); + VERIFY( c0.empty() ); + VERIFY( eq(c3, {1, 2, 3}) ); + + swap(c3, c5); + VERIFY( eq(c5, {1, 2, 3}) ); + VERIFY( eq(c3, {1, 2, 3, 4, 5}) ); + c5.swap(c3); + VERIFY( eq(c3, {1, 2, 3}) ); + VERIFY( eq(c5, {1, 2, 3, 4, 5}) ); + +#ifdef __cpp_lib_constexpr_inplace_vector +#error remove the consteval check +#endif + if consteval { + return; + } + + std::inplace_vector<X, 5> x0; + std::inplace_vector<X, 5> x3 = {1, 2, 3}; + std::inplace_vector<X, 5> x5 = {1, 2, 3, 4, 5}; + + swap(x0, x3); + VERIFY( x3.empty() ); + VERIFY( eq(x0, {1, 2, 3}) ); + x0.swap(x3); + VERIFY( x0.empty() ); + VERIFY( eq(x3, {1, 2, 3}) ); + + swap(x3, x5); + VERIFY( eq(x5, {1, 2, 3}) ); + VERIFY( eq(x3, {1, 2, 3, 4, 5}) ); + x5.swap(x3); + VERIFY( eq(x3, {1, 2, 3}) ); + VERIFY( eq(x5, {1, 2, 3, 4, 5}) ); +} + +constexpr auto e0 = materialize<0, int>({}); +constexpr auto e1 = materialize<0, X>({}); +constexpr auto e2 = materialize<0, Z>({}); + +constexpr auto t1 = materialize<3, int>({}); +constexpr auto t2 = materialize<3, int>({1, 2}); +constexpr auto t3 = materialize<3, int>({11, 22, 33}); + +int main() +{ + auto tests = [] { + test_ctor(); + test_assign(); + test_swap(); + return true; + }; + + tests(); + constexpr bool _ = tests(); +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/relops.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/relops.cc new file mode 100644 index 0000000..f9c5ce9 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/relops.cc @@ -0,0 +1,60 @@ +// { dg-do run { target c++26 } } + +#include <inplace_vector> + +#include <span> +#include <testsuite_hooks.h> + +template<size_t N, typename T> +constexpr void +test_equal(size_t c) +{ + T a[]{1, 2, 3, 4, 5, 6, 7, 8, 9}; + std::inplace_vector<T, N> v(a, a+c); + VERIFY( v == v ); + VERIFY( !(v != v) ); + VERIFY( !(v < v) ); + VERIFY( !(v > v) ); + VERIFY( v <= v ); + VERIFY( v >= v ); + VERIFY( (v <=> v) == 0 ); +} + +template<typename T> +constexpr void +test_not_equal() +{ + std::inplace_vector<T, 10> v3l{T{1}, T{2}, T{3}}; + std::inplace_vector<T, 10> v3g{T{1}, T{3}, T{3}}; + VERIFY( !(v3l == v3g) ); + VERIFY( v3l != v3g ); + VERIFY( v3l < v3g ); + VERIFY( !(v3l > v3g) ); + VERIFY( v3l <= v3g ); + VERIFY( !(v3l >= v3g) ); + VERIFY( (v3l <=> v3g) < 0 ); + + std::inplace_vector<T, 10> v2{T{1}, T{2}}; + VERIFY( !(v2 == v3l) ); + VERIFY( v2 != v3l ); + VERIFY( v2 < v3l ); + VERIFY( !(v2 > v3l) ); + VERIFY( v2 <= v3l ); + VERIFY( !(v2 >= v3l) ); + VERIFY( (v2 <=> v3l) < 0 ); +} + +int main() +{ + auto test_all = [] { + test_equal<0, int>(0); + test_equal<4, int>(0); + test_equal<4, int>(2); + test_equal<4, int>(4); + test_not_equal<int>(); + return true; + }; + + test_all(); + static_assert(test_all());; +} diff --git a/libstdc++-v3/testsuite/23_containers/inplace_vector/version.cc b/libstdc++-v3/testsuite/23_containers/inplace_vector/version.cc new file mode 100644 index 0000000..a5bbdb8 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/inplace_vector/version.cc @@ -0,0 +1,20 @@ +// { dg-do preprocess { target c++26 } } +// { dg-add-options no_pch } + +#include <inplace_vector> + +#ifndef __cpp_lib_inplace_vector +# error "Feature-test macro for inplace_vector missing in <inplace_vector>" +#elif __cpp_lib_inplace_vector != 202406L +# error "Feature-test macro for inplace_vector has wrong value in <inplace_vector>" +#endif + +#undef __cpp_lib_inplace_vector + +#include <version> + +#ifndef __cpp_lib_inplace_vector +# error "Feature-test macro for inplace_vector missing in <version>" +#elif __cpp_lib_inplace_vector != 202406L +# error "Feature-test macro for inplace_vector has wrong value in <version>" +#endif diff --git a/libstdc++-v3/testsuite/23_containers/list/48101_neg.cc b/libstdc++-v3/testsuite/23_containers/list/48101_neg.cc index 8b2e075..cc51705 100644 --- a/libstdc++-v3/testsuite/23_containers/list/48101_neg.cc +++ b/libstdc++-v3/testsuite/23_containers/list/48101_neg.cc @@ -26,5 +26,4 @@ test01() } // { dg-error "non-const, non-volatile value_type" "" { target *-*-* } 0 } -// { dg-prune-output "std::allocator<.* has no member named " } // { dg-prune-output "must have the same value_type as its allocator" } diff --git a/libstdc++-v3/testsuite/23_containers/map/48101_neg.cc b/libstdc++-v3/testsuite/23_containers/map/48101_neg.cc index 0661eeb..251beee 100644 --- a/libstdc++-v3/testsuite/23_containers/map/48101_neg.cc +++ b/libstdc++-v3/testsuite/23_containers/map/48101_neg.cc @@ -28,8 +28,8 @@ test01() c2.find(2); // { dg-error "here" } } -// { dg-error "_Compare = std::(__8::)?less<int.>" "" { target *-*-* } 0 } -// { dg-error "_Compare = std::(__8::)?allocator<int>" "" { target *-*-* } 0 } +// { dg-error "_Compare = std::less<int.>" "" { target *-*-* } 0 } +// { dg-error "_Compare = std::allocator<int>" "" { target *-*-* } 0 } // { dg-error "comparison object must be invocable" "" { target *-*-* } 0 } // { dg-prune-output "no match for call" } // { dg-prune-output "invalid conversion" } diff --git a/libstdc++-v3/testsuite/23_containers/map/cons/deduction.cc b/libstdc++-v3/testsuite/23_containers/map/cons/deduction.cc index f8e6e6e..8645988 100644 --- a/libstdc++-v3/testsuite/23_containers/map/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/map/cons/deduction.cc @@ -258,3 +258,49 @@ test_p1518r2() std::map s2(std::move(m), p); check_type<Map>(s2); } + +struct MyPred +{ + template<typename T, typename U> + bool operator()(T const&, U const&) const; +}; + +template<typename K, typename V> +constexpr bool test_lwg4223() +{ + using KD = std::remove_cv_t<std::remove_reference_t<K>>; + using VD = std::remove_cv_t<std::remove_reference_t<V>>; + using Alloc = __gnu_test::SimpleAllocator<std::pair<const KD, VD>>; + + std::initializer_list<std::pair<K, V>> il = {}; + Alloc a; + MyPred p; + + // The remove_cvref_t is not applied here. + // static_assert(std::is_same_v< + // decltype(std::map(il)), + // std::map<KD, VD>>); + + static_assert(std::is_same_v< + decltype(std::map(il.begin(), il.end())), + std::map<KD, VD>>); + + static_assert(std::is_same_v< + decltype(std::map(il.begin(), il.end(), p)), + std::map<KD, VD, MyPred>>); + + static_assert(std::is_same_v< + decltype(std::map(il.begin(), il.end(), a)), + std::map<KD, VD, std::less<KD>, Alloc>>); + + static_assert(std::is_same_v< + decltype(std::map(il.begin(), il.end(), p, a)), + std::map<KD, VD, MyPred, Alloc>>); + + return true; +} + +static_assert(test_lwg4223<const int, const float>()); +static_assert(test_lwg4223<int&, float&>()); +static_assert(test_lwg4223<int&&, float&&>()); +static_assert(test_lwg4223<const int&, const float&>()); diff --git a/libstdc++-v3/testsuite/23_containers/map/cons/from_range.cc b/libstdc++-v3/testsuite/23_containers/map/cons/from_range.cc index 9935f44..1453900 100644 --- a/libstdc++-v3/testsuite/23_containers/map/cons/from_range.cc +++ b/libstdc++-v3/testsuite/23_containers/map/cons/from_range.cc @@ -43,11 +43,11 @@ test_deduction_guide() __gnu_test::test_input_range<std::pair<const long, const float>> r2(0, 0); std::map m5(std::from_range, r2); - static_assert(std::is_same_v<decltype(m5), std::map<long, const float>>); + static_assert(std::is_same_v<decltype(m5), std::map<long, float>>); - // LWG4223: deduces map<const long&, float&> - //__gnu_test::test_input_range<std::pair<const long&, float&>> r3(0, 0); - // std::map m6(std::from_range, r3); + __gnu_test::test_input_range<std::pair<const long&, float&>> r3(0, 0); + std::map m6(std::from_range, r3); + static_assert(std::is_same_v<decltype(m6), std::map<long, float>>); __gnu_test::test_input_range<std::tuple<long, float>> r4(0, 0); std::map m7(std::from_range, r4); @@ -61,7 +61,7 @@ constexpr bool is_equal(std::less<T>, std::less<U>) { return true; } constexpr bool is_equal(StateCmp lhs, StateCmp rhs) -{ return lhs.state = rhs.state; } +{ return lhs.state == rhs.state; } constexpr auto get0 = [](auto const& t) { using std::get; @@ -103,12 +103,12 @@ do_test(Alloc alloc, Cmp cmp) std::map<K, V, Cmp, Alloc> m4(std::from_range, Range(a, a+4), cmp); VERIFY( eq(m4, {a, 4}) ); VERIFY( m4.get_allocator() == Alloc() ); - VERIFY( is_equal(m4.key_comp(), Cmp()) ); + VERIFY( is_equal(m4.key_comp(), cmp) ); std::map<K, V, Cmp, Alloc> m9(std::from_range, Range(a, a+9), alloc); VERIFY( eq(m9, {a, 9}) ); VERIFY( m9.get_allocator() == alloc ); - VERIFY( is_equal(m9.key_comp(), cmp) ); + VERIFY( is_equal(m9.key_comp(), Cmp()) ); std::map<K, V, Cmp, Alloc> mr(std::from_range, Range(a, a+14), cmp, alloc); VERIFY( eq(mr, {a, 9}) ); diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned_neg.cc new file mode 100644 index 0000000..f36ebb1 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/aligned_neg.cc @@ -0,0 +1,33 @@ +// { dg-do compile { target c++26 } } +#include<mdspan> + +#include <cstdint> + +std::aligned_accessor<uint32_t, 0> a; // { dg-error "required from here" } +std::aligned_accessor<uint32_t, 7> b; // { dg-error "required from here" } +std::aligned_accessor<uint32_t, size_t(-1)> c; // { dg-error "required from here" } + +std::aligned_accessor<uint32_t, 2> d; // { dg-error "required from here" } + +std::aligned_accessor<int[2], 32> e; // { dg-error "required from here" } + +class Abstract +{ + virtual void + foo() const = 0; +}; + +class Derived : public Abstract +{ + void + foo() const override + { } +}; + +std::aligned_accessor<Derived, alignof(int)> f_ok; +std::aligned_accessor<Abstract, alignof(int)> f_err; // { dg-error "required from here" } + +// { dg-prune-output "ByteAlignment must be a power of two" } +// { dg-prune-output "ElementType must not be an array type" } +// { dg-prune-output "ElementType must not be an abstract" } +// { dg-prune-output "static assertion failed" } // no message for alignment being too small diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_access_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_access_neg.cc new file mode 100644 index 0000000..3511cef --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_access_neg.cc @@ -0,0 +1,23 @@ +// { dg-do run { target c++26 xfail *-*-* } } +// { dg-require-debug-mode "" } + +#include <mdspan> +#include <array> + +void +test_unaligned_access() +{ + constexpr size_t N = 4; + alignas(N) std::array<char, 128> buffer{}; + auto* unaligned = buffer.data() + 1; + auto a = std::aligned_accessor<char, N>{}; + + [[maybe_unused]] char x = a.access(unaligned, 0); +} + +int +main() +{ + test_unaligned_access(); + return 0; +}; diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_offset_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_offset_neg.cc new file mode 100644 index 0000000..319da5f --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/debug/aligned_offset_neg.cc @@ -0,0 +1,23 @@ +// { dg-do run { target c++26 xfail *-*-* } } +// { dg-require-debug-mode "" } + +#include <mdspan> +#include <array> + +void +test_unaligned_offset() +{ + constexpr size_t N = 4; + alignas(N) std::array<char, 128> buffer{}; + auto* unaligned = buffer.data() + 1; + auto a = std::aligned_accessor<char, N>{}; + + [[maybe_unused]] char* x = a.offset(unaligned, 0); +} + +int +main() +{ + test_unaligned_offset(); + return 0; +}; diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/accessors/default_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/default_neg.cc new file mode 100644 index 0000000..f8da2b5 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/default_neg.cc @@ -0,0 +1,23 @@ +// { dg-do compile { target c++23 } } +#include<mdspan> + +std::default_accessor<int[3]> a; // { dg-error "required from here" } + +class AbstractBase +{ + virtual void + foo() const = 0; +}; + +class Derived : public AbstractBase +{ + void + foo() const override + { } +}; + +std::default_accessor<Derived> b_ok; +std::default_accessor<AbstractBase> b_err; // { dg-error "required from here"} + +// { dg-prune-output "ElementType must not be an array type" } +// { dg-prune-output "ElementType must not be an abstract" } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/accessors/generic.cc b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/generic.cc new file mode 100644 index 0000000..31cb13e --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/accessors/generic.cc @@ -0,0 +1,171 @@ +// { dg-do run { target c++23 } } +#include <mdspan> + +#include <testsuite_hooks.h> + +template<typename Accessor> + constexpr bool + test_class_properties() + { + static_assert(std::is_trivially_copyable_v<Accessor>); + static_assert(std::semiregular<Accessor>); + return true; + } + +template<typename Accessor> + constexpr bool + test_accessor_policy() + { + static_assert(std::copyable<Accessor>); + static_assert(std::is_nothrow_move_constructible_v<Accessor>); + static_assert(std::is_nothrow_move_assignable_v<Accessor>); + static_assert(std::is_nothrow_swappable_v<Accessor>); + return true; + } + +class Base +{ }; + +class Derived : public Base +{ }; + +template<typename RhsAccessor, typename LhsAccessor, bool ExpectConvertible> + constexpr void + check_convertible() + { + RhsAccessor rhs; + [[maybe_unused]] LhsAccessor lhs(rhs); + static_assert(std::is_nothrow_constructible_v<LhsAccessor, RhsAccessor>); + static_assert(std::is_convertible_v<RhsAccessor, LhsAccessor> == ExpectConvertible); + } + +template<template<typename T> typename LhsAccessor, + template<typename T> typename RhsAccessor = LhsAccessor, + bool ExpectConvertible = true> + constexpr bool + test_ctor() + { + // T -> T + check_convertible<RhsAccessor<double>, LhsAccessor<double>, + ExpectConvertible>(); + + // T -> const T + check_convertible<RhsAccessor<double>, LhsAccessor<const double>, + ExpectConvertible>(); + check_convertible<RhsAccessor<Derived>, LhsAccessor<const Derived>, + ExpectConvertible>(); + + // const T -> T + static_assert(!std::is_constructible_v<LhsAccessor<double>, + RhsAccessor<const double>>); + static_assert(!std::is_constructible_v<LhsAccessor<Derived>, + RhsAccessor<const Derived>>); + + // T <-> volatile T + check_convertible<RhsAccessor<int>, LhsAccessor<volatile int>, + ExpectConvertible>(); + static_assert(!std::is_constructible_v<LhsAccessor<int>, + RhsAccessor<volatile int>>); + + // size difference + static_assert(!std::is_constructible_v<LhsAccessor<char>, + RhsAccessor<int>>); + + // signedness + static_assert(!std::is_constructible_v<LhsAccessor<int>, + RhsAccessor<unsigned int>>); + static_assert(!std::is_constructible_v<LhsAccessor<unsigned int>, + RhsAccessor<int>>); + + // Derived <-> Base + static_assert(!std::is_constructible_v<LhsAccessor<Base>, + RhsAccessor<Derived>>); + static_assert(!std::is_constructible_v<LhsAccessor<Derived>, + RhsAccessor<Base>>); + return true; + } + +template<template<typename T> typename Accessor> + constexpr bool + test_properties() + { + test_class_properties<Accessor<double>>(); + test_accessor_policy<Accessor<double>>(); + test_ctor<Accessor>(); + return true; + } + +static_assert(test_properties<std::default_accessor>()); + +#ifdef __glibcxx_aligned_accessor +template<size_t Mult> +struct OverAlignedAccessorTrait +{ + template<typename T> + using type = std::aligned_accessor<T, Mult*alignof(T)>; +}; + +static_assert(test_properties<OverAlignedAccessorTrait<1>::type>()); +static_assert(test_properties<OverAlignedAccessorTrait<2>::type>()); +static_assert(test_ctor<OverAlignedAccessorTrait<2>::type, + std::default_accessor, false>()); +static_assert(test_ctor<OverAlignedAccessorTrait<2>::type, + OverAlignedAccessorTrait<4>::type>()); +static_assert(test_ctor<std::default_accessor, + OverAlignedAccessorTrait<2>::type>()); +static_assert(!std::is_constructible_v<std::aligned_accessor<char, 4>, + std::aligned_accessor<char, 2>>); +#endif + +template<typename A> + constexpr size_t + accessor_alignment = alignof(typename A::element_type); + +#ifdef __glibcxx_aligned_accessor +template<typename T, size_t N> + constexpr size_t + accessor_alignment<std::aligned_accessor<T, N>> = N; +#endif + +template<typename Accessor> + constexpr void + test_access(Accessor accessor) + { + constexpr size_t N = accessor_alignment<Accessor>; + alignas(N) std::array<double, 5> a{10, 11, 12, 13, 14}; + for (size_t i = 0; i < a.size(); ++i) + VERIFY(accessor.access(a.data(), i) == 10 + i); + } + +template<typename Accessor> + constexpr void + test_offset(Accessor accessor) + { + constexpr size_t N = accessor_alignment<Accessor>; + alignas(N) std::array<double, 5> a{10, 11, 12, 13, 14}; + for (size_t i = 0; i < a.size(); ++i) + VERIFY(accessor.offset(a.data(), i) == a.data() + i); + } + +template<typename Accessor> + constexpr bool + test_all() + { + auto accessor = Accessor{}; + test_offset(accessor); + test_access(accessor); + return true; + } + +int +main() +{ + test_all<std::default_accessor<double>>(); + static_assert(test_all<std::default_accessor<double>>()); + +#ifdef __glibcxx_aligned_accessor + test_all<typename OverAlignedAccessorTrait<4>::type<double>>(); + static_assert(test_all<typename OverAlignedAccessorTrait<4>::type<double>>()); +#endif + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/class_mandate_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/class_mandate_neg.cc new file mode 100644 index 0000000..172f149 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/class_mandate_neg.cc @@ -0,0 +1,41 @@ +// { dg-do compile { target c++23 } } +#include<mdspan> + +#include <cstdint> +#include "layout_like.h" + +struct ExtentsLike +{ + using index_type = int; + using size_type = unsigned int; + using rank_type = size_t; + + static constexpr size_t rank() { return 1; } + static constexpr size_t rank_dynamic() { return 0; } +}; + +constexpr bool +test_custom_extents_type() +{ + std::mdspan<double, ExtentsLike> md1; // { dg-error "required from here" } + return true; +} +static_assert(test_custom_extents_type()); + +constexpr bool +test_element_type_mismatch() +{ + using E = std::extents<int, 1>; + using L = std::layout_right; + using A = std::default_accessor<double>; + + [[maybe_unused]] std::mdspan<float, E, L, A> md2; // { dg-error "required from here" } + return true; +}; +static_assert(test_element_type_mismatch()); + +// { dg-prune-output "Extents must be a specialization of std::extents" } +// { dg-prune-output "no type named '_Storage'" } +// { dg-prune-output "non-constant condition" } +// { dg-prune-output "static assertion failed" } +// { dg-prune-output "__glibcxx_assert" } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/extents/class_mandates_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/extents/class_mandates_neg.cc new file mode 100644 index 0000000..db5cad2 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/class_mandates_neg.cc @@ -0,0 +1,17 @@ +// { dg-do compile { target c++23 } } +#include<mdspan> + +#include <cstdint> + +std::extents<uint8_t, size_t(1) << 9> e1; // { dg-error "from here" } +std::extents<char, 1> e2; // { dg-error "from here" } +std::extents<bool, 1> e3; // { dg-error "from here" } +std::extents<double, 1> e4; // { dg-error "from here" } + +// { dg-prune-output "dynamic or representable as IndexType" } +// { dg-prune-output "signed or unsigned integer" } +// { dg-prune-output "invalid use of incomplete type" } +// { dg-prune-output "non-constant condition for static assertion" } +// { dg-prune-output "integer constants in boolean context" } +// { dg-prune-output "__gnu_cxx::__numeric_traits_integer" } +// { dg-prune-output "static assertion failed" } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_copy.cc b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_copy.cc new file mode 100644 index 0000000..a7b3a169 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_copy.cc @@ -0,0 +1,82 @@ +// { dg-do run { target c++23 } } +#include <mdspan> + +#include <testsuite_hooks.h> + +// Test the copy ctor and the ctor from other extents. + +constexpr auto dyn = std::dynamic_extent; + +// Not constructible +static_assert(!std::is_constructible_v<std::extents<int>, + std::extents<int, 1>>); + +static_assert(!std::is_constructible_v<std::extents<int, 1, 1>, + std::extents<int, 1>>); + +static_assert(!std::is_constructible_v<std::extents<int, dyn>, + std::extents<int, dyn, dyn>>); + +static_assert(!std::is_constructible_v<std::extents<int, 2, 2>, + std::extents<int, 1, 2>>); + +// Nothrow constructible +static_assert(std::is_nothrow_constructible_v<std::extents<int, 1>, + std::extents<unsigned int, dyn>>); +static_assert(std::is_nothrow_constructible_v<std::extents<unsigned int, dyn>, + std::extents<int, 1>>); + +// Implicit conversion +static_assert(!std::is_convertible_v<std::extents<unsigned int>, + std::extents<int>>); +static_assert(std::is_convertible_v<std::extents<int>, + std::extents<unsigned int>>); + +static_assert(!std::is_convertible_v<std::extents<unsigned int, 1>, + std::extents<int, 1>>); +static_assert(std::is_convertible_v<std::extents<int, 1>, + std::extents<unsigned int, 1>>); + +static_assert(!std::is_convertible_v<std::extents<int, dyn>, + std::extents<int, 1>>); +static_assert(std::is_convertible_v<std::extents<int, 1>, + std::extents<int, dyn>>); + +static_assert(!std::is_convertible_v<std::extents<unsigned int, 1>, + std::extents<int, dyn>>); +static_assert(std::is_convertible_v<std::extents<int, 1>, + std::extents<unsigned int, dyn>>); + +template<typename T, size_t... Extents, typename Other> + constexpr void + test_ctor(const Other& other) + { + auto e = std::extents<T, Extents...>(other); + VERIFY(e == other); + } + +constexpr int +test_all() +{ + auto e0 = std::extents<int>(); + test_ctor<int>(e0); + + auto e1 = std::extents<int, 1, 2, 3>(); + test_ctor<int, 1, 2, 3>(e1); + test_ctor<int, 1, dyn, 3>(e1); + test_ctor<unsigned int, 1, dyn, 3>(e1); + + auto e2 = std::extents<unsigned int, 1, dyn, 3>{1, 2, 3}; + test_ctor<int, 1, 2, 3>(e2); + test_ctor<int, 1, dyn, 3>(e2); + test_ctor<int, 1, dyn, dyn>(e2); + return true; +} + +int +main() +{ + test_all(); + static_assert(test_all()); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_default.cc b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_default.cc new file mode 100644 index 0000000..f45d3e5 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_default.cc @@ -0,0 +1,41 @@ +// { dg-do run { target c++23 } } +#include <mdspan> + +#include <cstdint> +#include <testsuite_hooks.h> + +constexpr auto dyn = std::dynamic_extent; + +template<typename Extents> + constexpr void + test_default_ctor() + { + Extents exts; + for(size_t i = 0; i < Extents::rank(); ++i) + if(exts.static_extent(i) == std::dynamic_extent) + VERIFY(exts.extent(i) == 0); + else + VERIFY(std::cmp_equal(exts.extent(i), Extents::static_extent(i))); + } + +constexpr bool +test_default_ctor_all() +{ + test_default_ctor<std::extents<int, 1>>(); + test_default_ctor<std::extents<int, dyn>>(); + test_default_ctor<std::extents<int, 1, 2>>(); + test_default_ctor<std::extents<int, dyn, 2>>(); + test_default_ctor<std::extents<int, dyn, dyn>>(); + test_default_ctor<std::extents<int, 1, 2, 3>>(); + test_default_ctor<std::extents<int, dyn, 2, dyn>>(); + test_default_ctor<std::extents<int, dyn, dyn, dyn>>(); + return true; +} + +int +main() +{ + test_default_ctor_all(); + static_assert(test_default_ctor_all()); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_ints.cc b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_ints.cc new file mode 100644 index 0000000..fdbcb70 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_ints.cc @@ -0,0 +1,69 @@ +// { dg-do run { target c++23 } } +#include <mdspan> + +#include <testsuite_hooks.h> + +constexpr auto dyn = std::dynamic_extent; + +class A {}; + +// Not constructible if the number of integer-like arguments isn't either +// rank() or rank_dynamic(). +static_assert(!std::is_constructible_v<std::extents<int>, int>); +static_assert(!std::is_constructible_v<std::extents<int, dyn, dyn>, int>); +static_assert(!std::is_constructible_v<std::extents<int, 1, dyn, 3>, int, int>); + +// Not constructible from non integer-like objects. +static_assert(!std::is_constructible_v<std::extents<int, 1>, int, A>); + +#ifdef __SIZEOF_INT128__ +static_assert(std::is_constructible_v<std::extents<__int128, 1, 2>, + __int128, unsigned __int128>); +static_assert(std::is_constructible_v<std::extents<unsigned __int128, 1, 2>, + unsigned int, int>); +#endif + +// No implicit conversion from integer-like objects. +template<typename Extent, typename... OExtents> + constexpr bool + is_explicit() + { + return std::is_nothrow_constructible_v<Extent, OExtents...> + && !std::is_convertible_v<Extent, OExtents...>; + } + +static_assert(is_explicit<std::extents<int, 1>, int>()); +static_assert(is_explicit<std::extents<int, 1>, unsigned int>()); +static_assert(is_explicit<std::extents<unsigned int, 1>, int>()); + +constexpr bool +test_all() +{ + auto expected = std::extents<int, 1, 2, 3>(1, 2, 3); + + // From all extents. + VERIFY(std::extents<int, 1, 2, 3>(1, 2, 3) == expected); + VERIFY(std::extents<int, dyn, 2, 3>(1, 2, 3) == expected); + VERIFY(std::extents<int, dyn, 2, dyn>(1, 2, 3) == expected); + + VERIFY(std::extents<int, 1, 2, 3>{1, 2, 3} == expected); + VERIFY(std::extents<int, dyn, 2, 3>{1, 2, 3} == expected); + VERIFY(std::extents<int, dyn, 2, dyn>{1, 2, 3} == expected); + + // From only dynamic extents. + VERIFY(std::extents<int, dyn, 2, 3>(1) == expected); + VERIFY(std::extents<int, dyn, 2, dyn>(1, 3) == expected); + + VERIFY(std::extents<int, dyn, 2, 3>{1} == expected); + VERIFY(std::extents<int, dyn, 2, dyn>{1, 3} == expected); + + return true; +} + +int +main() +{ + test_all(); + static_assert(test_all()); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_shape.cc b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_shape.cc new file mode 100644 index 0000000..1682cc5 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/ctor_shape.cc @@ -0,0 +1,160 @@ +// { dg-do run { target c++23 } } +#include <mdspan> + +#include <testsuite_hooks.h> + +constexpr auto dyn = std::dynamic_extent; + +template<typename Extent, typename T, size_t N> + constexpr bool + constructible() + { + return std::is_nothrow_constructible_v<Extent, std::array<T, N>> + && std::is_nothrow_constructible_v<Extent, std::span<T, N>>; + } + +template<typename Extent, typename T, size_t N> + constexpr bool + not_constructible() + { + return !std::is_constructible_v<Extent, std::array<T, N>> + && !std::is_constructible_v<Extent, std::span<T, N>>; + } + +template<typename Extent, typename T, size_t N> + constexpr bool + convertible() + { + return std::is_convertible_v<std::array<T, N>, Extent> + && std::is_convertible_v<std::span<T, N>, Extent>; + } + +template<typename Extent, typename T, size_t N> + constexpr bool + not_convertible() + { + return !std::is_convertible_v<std::array<T, N>, Extent> + && !std::is_convertible_v<std::span<T, N>, Extent>; + } + +static_assert(constructible<std::extents<int, 1, 2>, int, 2>()); +static_assert(not_constructible<std::extents<int, 1, 2>, int, 1>()); + +static_assert(constructible<std::extents<int>, int, 0>()); +static_assert(convertible<std::extents<int>, int, 0>()); +static_assert(convertible<std::extents<unsigned int>, int, 0>()); +static_assert(convertible<std::extents<int>, unsigned int, 0>()); + +static_assert(constructible<std::extents<int, 1, dyn>, int, 1>()); +static_assert(convertible<std::extents<int, 1, dyn>, int, 1>()); +static_assert(convertible<std::extents<unsigned int, 1, dyn>, int, 1>()); +static_assert(convertible<std::extents<int, 1, dyn>, unsigned int, 1>()); + +static_assert(constructible<std::extents<int, 1, dyn>, int, 2>()); +static_assert(not_convertible<std::extents<int, 1, dyn>, int, 2>()); +static_assert(not_convertible<std::extents<unsigned int, 1, dyn>, int, 2>()); +static_assert(not_convertible<std::extents<int, 1, dyn>, unsigned int, 2>()); + +// Non-integer, but convertible. +static_assert(constructible<std::extents<int, dyn>, double, 1>()); +static_assert(convertible<std::extents<int, dyn>, double, 1>()); + +namespace all_extents +{ + template<typename Shape> + constexpr void + test_ctor(Shape shape) + { + auto expected = std::extents<int, 1, 2, 3>(); + VERIFY(std::extents<int, 1, dyn, 3>(shape) == expected); + VERIFY(std::extents<int, dyn, dyn, dyn>(shape) == expected); + VERIFY(std::extents<int, 1, 2, 3>(shape) == expected); + } + + constexpr void + test_common_shapes() + { + auto array = std::array<int, 3>{1, 2, 3}; + auto span_const = std::span<const int, 3>(array); + auto span = std::span<int, 3>(array); + + test_ctor(array); + test_ctor(span); + test_ctor(span_const); + } + + constexpr void + test_empty_shapes() + { + auto shape = std::array<int, 0>(); + auto span = std::span<int, 0>(shape); + + auto expected = std::extents<int>(); + VERIFY(std::extents<int>(shape) == expected); + VERIFY(std::extents<int>(span) == expected); + } + + constexpr bool + test_all() + { + test_common_shapes(); + test_empty_shapes(); + return true; + } +} + +namespace only_dynamic_extents +{ + template<typename Extents, typename Shape> + constexpr void + test_ctor(const Shape& shape) + { + Extents e = shape; + + VERIFY(e.rank_dynamic() == shape.size()); + + size_t di = 0; + for(size_t i = 0; i < e.rank(); ++i) + if(e.static_extent(i) == dyn) + VERIFY(e.extent(i) == shape[di++]); + } + + template<typename Extents, typename T, size_t N> + constexpr void + test_all_shape_types(std::array<T, N> shape) + { + test_ctor<Extents>(shape); + test_ctor<Extents>(std::span<T, N>(shape)); + test_ctor<Extents>(std::span<const T, N>(shape)); + } + + constexpr void + test_common_shapes() + { + auto s = std::array<int, 0>{}; + auto s2 = std::array<int, 1>{2}; + auto s123 = std::array<int, 3>{1, 2, 3}; + + test_all_shape_types<std::extents<int, 1, dyn, 3>>(s2); + test_all_shape_types<std::extents<int, dyn, dyn, dyn>>(s123); + test_all_shape_types<std::extents<int, 1, 2, 3>>(s); + } + + constexpr bool + test_all() + { + test_common_shapes(); + return true; + } +} + +int +main() +{ + all_extents::test_all(); + static_assert(all_extents::test_all()); + + only_dynamic_extents::test_all(); + static_assert(only_dynamic_extents::test_all()); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/extents/custom_integer.cc b/libstdc++-v3/testsuite/23_containers/mdspan/extents/custom_integer.cc new file mode 100644 index 0000000..99de401 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/custom_integer.cc @@ -0,0 +1,96 @@ +// { dg-do run { target c++23 } } +#include <mdspan> + +#include <testsuite_hooks.h> +#include "../int_like.h" + +// Test construction from a custom integer-like object, that has +// no copy/move ctor or copy/move assignment operator. + +constexpr size_t dyn = std::dynamic_extent; + +static_assert(std::is_convertible_v<IntLike, int>); +static_assert(std::is_nothrow_constructible_v<int, IntLike>); + +template<typename Extents, bool Valid> + void + test_shape(const auto& shape) + { + static_assert(std::is_constructible_v<Extents, decltype(shape)> == Valid); + + if constexpr (Valid) + { + std::extents<int, 2, 3> expected; + Extents actual(shape); + VERIFY(actual == expected); + } + } + +template<typename Int, bool Valid> + void + test_shape_all() + { + auto a2 = std::array<Int, 1>{Int(2)}; + auto s2 = std::span<Int, 1>(a2); + + auto a23 = std::array<Int, 2>{Int(2), Int(3)}; + auto s23 = std::span<Int, 2>(a23); + + auto check = [](const auto& dyn_exts, const auto& full_exts) + { + test_shape<std::extents<int, 2, 3>, Valid>(full_exts); + test_shape<std::extents<int, dyn, 3>, Valid>(dyn_exts); + test_shape<std::extents<int, dyn, 3>, Valid>(full_exts); + test_shape<std::extents<int, dyn, dyn>, Valid>(full_exts); + }; + + check(a2, a23); + check(s2, s23); + } + +// Needed because is_constructible requires that Ints are move constructible. +template<typename Extents, typename... Ints> + concept has_ctor = requires + { + { Extents(Ints(0)...) } -> std::same_as<Extents>; + }; + +template<typename Int, bool Valid> + void + test_pack_all() + { + static_assert(has_ctor<std::extents<int, dyn, 3>, Int> == Valid); + static_assert(has_ctor<std::extents<int, dyn, 3>, Int, Int> == Valid); + static_assert(has_ctor<std::extents<int, dyn, dyn>, Int, Int> == Valid); + + if constexpr (Valid) + { + std::extents<int, 2, 3> expected; + + std::extents<int, dyn, 3> e1(Int(2)); + VERIFY(e1 == expected); + + std::extents<int, dyn, 3> e2(Int(2), Int(3)); + VERIFY(e2 == expected); + + std::extents<int, dyn, dyn> e3(Int(2), Int(3)); + VERIFY(e3 == expected); + } + } + +int +main() +{ + test_shape_all<int, true>(); + test_shape_all<IntLike, true>(); + test_shape_all<ThrowingInt, false>(); + test_shape_all<MutatingInt, false>(); + test_shape_all<RValueInt, false>(); + + test_pack_all<int, true>(); + test_pack_all<IntLike, true>(); + test_pack_all<ThrowingInt, false>(); + test_pack_all<MutatingInt, true>(); + test_pack_all<RValueInt, true>(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/extents/extents_mismatch_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/extents/extents_mismatch_neg.cc new file mode 100644 index 0000000..b35e531 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/extents_mismatch_neg.cc @@ -0,0 +1,35 @@ +// { dg-do compile { target c++23 } } +#include<mdspan> + +#include <cstdint> + +constexpr size_t dyn = std::dynamic_extent; + +constexpr bool +test_dyn2sta_extents_mismatch_00() +{ + auto e0 = std::extents<int, dyn>{1}; + [[maybe_unused]] auto e1 = std::extents<int, 2>{e0}; // { dg-error "expansion of" } + return true; +} +static_assert(test_dyn2sta_extents_mismatch_00()); // { dg-error "expansion of" } + +constexpr bool +test_dyn2sta_extents_mismatch_01() +{ + [[maybe_unused]] auto e = std::extents<int, 1, dyn>{2, 2}; // { dg-error "expansion of" } + return true; +} +static_assert(test_dyn2sta_extents_mismatch_01()); // { dg-error "expansion of" } + +constexpr bool +test_dyn2sta_extents_mismatch_02() +{ + std::array<int, 2> exts{2, 2}; + [[maybe_unused]] auto e = std::extents<int, 1, dyn>{exts}; // { dg-error "expansion of" } + return true; +} +static_assert(test_dyn2sta_extents_mismatch_02()); // { dg-error "expansion of" } + +// { dg-prune-output "non-constant condition for static assertion" } +// { dg-prune-output "__glibcxx_assert" } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/extents/misc.cc b/libstdc++-v3/testsuite/23_containers/mdspan/extents/misc.cc new file mode 100644 index 0000000..33d6f94 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/extents/misc.cc @@ -0,0 +1,261 @@ +// { dg-do run { target c++23 } } +#include <mdspan> + +#include <cstdint> +#include <testsuite_hooks.h> + +constexpr size_t dyn = std::dynamic_extent; + +// Check class traits. +static_assert(std::regular<std::extents<int>>); +static_assert(std::regular<std::extents<int, 1>>); +static_assert(std::regular<std::extents<int, dyn>>); + +static_assert(std::is_trivially_copyable_v<std::extents<int>>); +static_assert(std::is_trivially_copyable_v<std::extents<int, 1>>); +static_assert(std::is_trivially_copyable_v<std::extents<int, dyn>>); + +// Check member typedefs. +static_assert(std::is_same_v<std::extents<int, 1, 2>::rank_type, size_t>); + +static_assert(std::is_unsigned_v<std::extents<int, 2>::size_type>); +static_assert(std::is_unsigned_v<std::extents<unsigned int, 2>::size_type>); + +static_assert(std::is_same_v<std::extents<int, 2>::index_type, int>); +static_assert(std::is_same_v<std::extents<unsigned int, 2>::index_type, + unsigned int>); + +// Check `rank`. +static_assert(std::extents<int, 1>::rank() == 1); +static_assert(std::extents<int, dyn>::rank() == 1); +static_assert(std::extents<int, 2, dyn>::rank() == 2); + +// Check `rank_dynamic`. +static_assert(std::extents<int, 1>::rank_dynamic() == 0); +static_assert(std::extents<int, dyn>::rank_dynamic() == 1); +static_assert(std::extents<int, 2, dyn>::rank_dynamic() == 1); +static_assert(std::extents<int, dyn, dyn>::rank_dynamic() == 2); + +template<typename T, size_t... Extents> + constexpr bool + check_rank_return_types() + { + auto e = std::extents<T, Extents...>(); + return std::is_same_v<decltype(e.rank()), size_t> + && std::is_same_v<decltype(e.rank_dynamic()), size_t>; + } + +static_assert(check_rank_return_types<int, 1>()); + +// Check that the static extents don't take up space. +static_assert(sizeof(std::extents<int, 1, dyn>) == sizeof(int)); +static_assert(sizeof(std::extents<short, 1, dyn>) == sizeof(short)); + +template<typename Extents> +class Container +{ + int dummy; + [[no_unique_address]] std::extents<size_t> b0; +}; + +static_assert(sizeof(Container<std::extents<short, 1, 2>>) == sizeof(int)); +static_assert(sizeof(Container<std::extents<size_t, 1, 2>>) == sizeof(int)); + +// operator= +static_assert(std::is_nothrow_assignable_v<std::extents<int, dyn, 2>, + std::extents<int, 1, 2>>); + +constexpr bool +test_assign() +{ + auto e1 = std::extents<int, 1, 2>(); + auto e2 = std::extents<int, 1, 2>(); + + e2 = e1; + VERIFY(e2 == e1); + + auto e5 = std::extents<int, 1, dyn>(); + e5 = e1; + VERIFY(e5 == e1); + + auto e3 = std::extents<int, dyn, dyn>(1, 2); + auto e4 = std::extents<int, dyn, dyn>(3, 4); + e3 = e4; + VERIFY(e3 == e4); + return true; +} + +// Deduction guide +template<size_t Rank, typename... Extents> +constexpr void +test_deduction(Extents... exts) +{ + std::array<size_t, sizeof...(exts)> shape{static_cast<size_t>(exts)...}; + std::dextents<size_t, Rank> expected(shape); + std::extents e(exts...); + static_assert(std::is_same_v<decltype(e), std::dextents<size_t, Rank>>); + VERIFY(e == expected); +} + +constexpr bool +test_deduction_from_constant() +{ + auto verify = [](auto actual, auto expected) + { + static_assert(std::same_as<decltype(actual), decltype(expected)>); + VERIFY(actual == expected); + }; + + constexpr auto i1 = std::integral_constant<size_t, 1>{}; + constexpr auto i2 = std::integral_constant<int, 2>{}; + + verify(std::extents(1), std::extents<size_t, dyn>{1}); + verify(std::extents(i1), std::extents<size_t, 1>{}); + verify(std::extents(i2), std::extents<size_t, 2>{}); + verify(std::extents(std::true_type{}, 2), std::dextents<size_t, 2>{1, 2}); + verify(std::extents(std::false_type{}, 2), std::dextents<size_t, 2>{0, 2}); + +#if __glibcxx_constant_wrapper + constexpr auto c2 = std::constant_wrapper<2>{}; + verify(std::extents(c2), std::extents<size_t, 2>{}); + verify(std::extents(1, c2), std::extents<size_t, dyn, 2>{1}); + verify(std::extents(c2), std::extents<size_t, 2>{}); + verify(std::extents(1, c2), std::extents<size_t, dyn, 2>{1}); + verify(std::extents(std::cw<true>, c2), std::extents<size_t, dyn, 2>{1}); +#endif + return true; +} + +constexpr bool +test_deduction_all() +{ + test_deduction<0>(); + test_deduction<1>(1); + test_deduction<2>(1.0, 2.0f); + test_deduction<3>(int(1), short(2), size_t(3)); + test_deduction_from_constant(); + return true; +} + +class A {}; + +template<typename... Extents> + concept deducible = requires + { + { std::extents(Extents{}...) } + -> std::convertible_to<std::dextents<size_t, sizeof...(Extents)>>; + }; + +static_assert(deducible<int>); +static_assert(!deducible<A, A>); + +// dextents +static_assert(std::is_same_v<std::dextents<int, 0>, std::extents<int>>); +static_assert(std::is_same_v<std::dextents<int, 1>, std::extents<int, dyn>>); +static_assert(std::is_same_v<std::dextents<int, 5>, + std::extents<int, dyn, dyn, dyn, dyn, dyn>>); + +static_assert(std::dextents<int, 5>::rank() == 5); +static_assert(std::dextents<int, 5>::rank_dynamic() == 5); +static_assert(std::is_same_v<typename std::dextents<int, 5>::index_type, int>); + +// static_extent +static_assert(std::extents<int, 1, 2>::static_extent(0) == 1); +static_assert(std::extents<int, 1, 2>::static_extent(1) == 2); + +static_assert(std::extents<int, 1, dyn>::static_extent(0) == 1); +static_assert(std::extents<int, 1, dyn>::static_extent(1) == dyn); + +static_assert(std::extents<int, dyn, dyn>::static_extent(0) == dyn); +static_assert(std::extents<int, dyn, dyn>::static_extent(1) == dyn); + +// dims +#if __glibcxx_mdspan >= 202406L +static_assert(std::is_same_v<std::dims<0>, std::dextents<size_t, 0>>); +static_assert(std::is_same_v<std::dims<3>, std::dextents<size_t, 3>>); +static_assert(std::is_same_v<std::dims<3, int>, std::dextents<int, 3>>); +#endif + +// extent +template<typename Extent> + constexpr void + test_extent(const Extent& e, + const std::array<typename Extent::index_type, Extent::rank()>& shape) + { + for(size_t i = 0; i < e.rank(); ++i) + VERIFY(e.extent(i) == shape[i]); + } + +constexpr bool +test_extent_all() +{ + test_extent(std::extents<int, 1, 2>{}, {1, 2}); + test_extent(std::extents<int, 1, dyn>{2}, {1, 2}); + test_extent(std::extents<int, dyn, dyn>{1, 2}, {1, 2}); + return true; +} + +// operator== +template<typename Lhs, typename Rhs> + constexpr void + test_ops_eq(const Lhs& lhs, const Rhs& rhs, bool expected) + { + VERIFY((lhs == rhs) == expected); + VERIFY((lhs != rhs) == !expected); + } + +constexpr void +test_op_eq_rank_zero() +{ + auto e1 = std::extents<int>(); + auto e2 = std::extents<int>(); + auto e3 = std::extents<unsigned int>(); + + test_ops_eq(e1, e2, true); + test_ops_eq(e1, e3, true); +} + +constexpr void +test_op_eq_common() +{ + auto e1 = std::extents<int, 1, 2, 3>(); + auto e2 = std::extents<int, 1, 2, 3>(); + auto e3 = std::extents<int, 1, dyn, 3>(2); + auto e4 = std::extents<int, 1, dyn, 3>(3); + + auto e5 = std::extents<int, 1>(); + auto e6 = std::extents<int, 1, 3, 3>(); + + test_ops_eq(e1, e2, true); + test_ops_eq(e1, e3, true); + test_ops_eq(e1, e4, false); + + test_ops_eq(e1, e5, false); + test_ops_eq(e1, e6, false); + test_ops_eq(e3, e6, false); +} + +constexpr bool +test_op_eq_all() +{ + test_op_eq_rank_zero(); + test_op_eq_common(); + return true; +} + +int +main() +{ + test_assign(); + static_assert(test_assign()); + + test_deduction_all(); + static_assert(test_deduction_all()); + + test_extent_all(); + static_assert(test_extent_all()); + + test_op_eq_all(); + static_assert(test_op_eq_all()); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h b/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h new file mode 100644 index 0000000..b839be7 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h @@ -0,0 +1,81 @@ +#ifndef TEST_MDSPAN_INT_LIKE_H +#define TEST_MDSPAN_INT_LIKE_H + +enum class CustomIndexKind +{ + Const, + Throwing, + Mutating, + RValue, +}; + +template<CustomIndexKind Kind, bool Copyable = false> + class CustomIndexType + { + public: + explicit + CustomIndexType(int i) + : value(i) + { } + + CustomIndexType() requires(Copyable) = default; + CustomIndexType() requires(!Copyable) = delete; + + CustomIndexType(const CustomIndexType&) requires(Copyable) = default; + CustomIndexType(const CustomIndexType&) requires(!Copyable) = delete; + + CustomIndexType(CustomIndexType&&) requires(Copyable) = default; + CustomIndexType(CustomIndexType&&) requires(!Copyable) = delete; + + CustomIndexType& + operator=(const CustomIndexType&) requires(Copyable) = default; + CustomIndexType& + operator=(const CustomIndexType&) requires(!Copyable) = delete; + + CustomIndexType& + operator=(CustomIndexType&&) requires(Copyable) = default; + CustomIndexType& + operator=(CustomIndexType&&) requires(!Copyable) = delete; + + constexpr + operator int() const noexcept + requires (Kind == CustomIndexKind::Const) + { return value; } + + constexpr + operator int() const + requires (Kind == CustomIndexKind::Throwing) + { return value; } + + constexpr + operator int() noexcept + requires (Kind == CustomIndexKind::Mutating) + { return value; } + + constexpr + operator int() && noexcept + requires (Kind == CustomIndexKind::RValue) + { return value; } + + private: + int value; + }; + +using IntLike = CustomIndexType<CustomIndexKind::Const>; +using ThrowingInt = CustomIndexType<CustomIndexKind::Throwing>; +using MutatingInt = CustomIndexType<CustomIndexKind::Mutating>; +using RValueInt = CustomIndexType<CustomIndexKind::RValue>; + +struct NotIntLike +{ }; + +struct StructuralInt +{ + constexpr + operator int() const noexcept + { return value; } + + int value; +}; + +#endif // TEST_MDSPAN_INT_LIKE_H diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layout_like.h b/libstdc++-v3/testsuite/23_containers/mdspan/layout_like.h new file mode 100644 index 0000000..ded6e9d --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layout_like.h @@ -0,0 +1,87 @@ +#ifndef TEST_MDSPAN_LAYOUT_LIKE_H +#define TEST_MDSPAN_LAYOUT_LIKE_H 1 + +template<bool Noexcept> + struct CustomLayout + { + template<typename Extents> + class mapping + { + public: + using extents_type = Extents; + using index_type = typename extents_type::index_type; + using size_type = typename extents_type::size_type; + using rank_type = typename extents_type::rank_type; + using layout_type = CustomLayout; + + constexpr + mapping() noexcept = default; + + constexpr + mapping(Extents exts) + : m_exts(exts) + { } + + constexpr const extents_type& + extents() const noexcept(Noexcept) { return m_exts; } + + constexpr index_type + required_span_size() const noexcept(Noexcept) + { + for (size_t i = 0; i < extents_type::rank(); ++i) + if (m_exts.extent(i) == 0) + return 0; + return 1; + } + + template<typename... Indices> + requires (sizeof...(Indices) == extents_type::rank()) + constexpr index_type + operator()(Indices...) const noexcept(Noexcept) + { return 0; } + + static constexpr index_type + stride(rank_type) noexcept(Noexcept) + { return 0; } + + static constexpr bool + is_always_unique() noexcept(Noexcept) + { return false; } + + static constexpr bool + is_always_exhaustive() noexcept(Noexcept) + { return true; } + + static constexpr bool + is_always_strided() noexcept(Noexcept) + { return true; } + + constexpr bool + is_unique() const noexcept(Noexcept) + { + if (required_span_size() == 0) + return true; + + for (size_t i = 0; i < extents_type::rank(); ++i) + if (m_exts.extent(i) > 1) + return false; + return true; + } + + static constexpr bool + is_exhaustive() noexcept(Noexcept) + { return true; } + + static constexpr bool + is_strided() noexcept(Noexcept) + { return true; } + + private: + Extents m_exts; + }; + }; + +using LayoutLike = CustomLayout<true>; +using ThrowingLayout = CustomLayout<false>; + +#endif diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layout_traits.h b/libstdc++-v3/testsuite/23_containers/mdspan/layout_traits.h new file mode 100644 index 0000000..f0aeac3 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layout_traits.h @@ -0,0 +1,196 @@ +#ifndef TEST_MDSPAN_LAYOUT_TRAITS_H +#define TEST_MDSPAN_LAYOUT_TRAITS_H + +#include <algorithm> + +enum class PaddingSide +{ + Left, + Right +}; + +template<typename Layout> + constexpr static bool is_left_padded = false; + +#if __cplusplus > 202302L +template<size_t PaddingValue> + constexpr static bool is_left_padded<std::layout_left_padded<PaddingValue>> + = true; +#endif + +template<typename Layout> + constexpr static bool is_right_padded = false; + +#if __cplusplus > 202302L +template<size_t PaddingValue> + constexpr static bool is_right_padded<std::layout_right_padded<PaddingValue>> + = true; +#endif + +template<typename Layout> + constexpr bool + is_padded_layout = is_left_padded<Layout> || is_right_padded<Layout>; + +#if __cplusplus > 202302L +template<PaddingSide Side, typename Layout> + constexpr bool + is_same_padded; + +template<typename Layout> + constexpr bool + is_same_padded<PaddingSide::Left, Layout> = is_left_padded<Layout>; + +template<typename Layout> + constexpr bool + is_same_padded<PaddingSide::Right, Layout> = is_right_padded<Layout>; + +template<typename Extents> + constexpr auto + dynamic_extents_array(const Extents& exts) + { + std::array<typename Extents::index_type, Extents::rank()> ret; + for(size_t i = 0; i < Extents::rank(); ++i) + ret[i] = exts.extent(i); + return ret; + } + +struct DeducePaddingSide +{ + template<template<size_t> typename Layout> + constexpr static PaddingSide + from_template() + { + if constexpr (std::same_as<Layout<0>, std::layout_left_padded<0>>) + return PaddingSide::Left; + else + return PaddingSide::Right; + } + + template<typename Layout> + constexpr static PaddingSide + from_typename() + { + if constexpr (std::same_as<Layout, std::layout_left>) + return PaddingSide::Left; + else if constexpr (is_left_padded<Layout>) + return PaddingSide::Left; + else + return PaddingSide::Right; + } +}; + +template<PaddingSide Side> + struct LayoutTraits; + +template<> + struct LayoutTraits<PaddingSide::Left> + { + using layout_same = std::layout_left; + using layout_other = std::layout_right; + template<size_t PaddingValue> + using layout_same_padded = std::layout_left_padded<PaddingValue>; + + template<typename Extents> + using extents_type = Extents; + + template<typename Extents> + constexpr static extents_type<Extents> + make_extents(const Extents& exts) + { return exts; } + + template<typename T, size_t N> + constexpr static std::array<T, N> + make_array(const std::array<T, N>& a) + { return a; } + + template<typename... Indices> + constexpr static auto + make_indices(Indices... indices) + { return std::array{indices...}; } + + template<typename... Ts> + constexpr static std::tuple<Ts...> + make_tuple(const std::tuple<Ts...>& tup) + { return tup; } + + template<typename Mapping> + constexpr static auto + padded_stride(const Mapping& m) + { return m.stride(1); } + + template<typename Extents> + constexpr static auto + padded_extent(const Extents& exts) + { return exts.extent(0); } + }; + +template<> + struct LayoutTraits<PaddingSide::Right> + { + using layout_same = std::layout_right; + template<size_t PaddingValue> + using layout_same_padded = std::layout_right_padded<PaddingValue>; + using layout_other = std::layout_left; + + template<typename IndexType, size_t... Extents> + constexpr static auto + make_extents(const std::extents<IndexType, Extents...>& exts) + { + constexpr size_t rank = sizeof...(Extents); + auto impl = [&]<size_t... I>(std::index_sequence<I...>) + { + auto dyn_exts = make_array(dynamic_extents_array(exts)); + return std::extents<IndexType, Extents...[rank - 1 - I]...>(dyn_exts); + }; + return impl(std::make_index_sequence<rank>()); + } + + template<typename Extents> + using extents_type = decltype(make_extents(std::declval<Extents>())); + + template<typename T, size_t N> + constexpr static std::array<T, N> + make_array(std::array<T, N> a) + { + std::ranges::reverse(a); + return a; + } + + template<typename... Indices> + constexpr static auto + make_indices(Indices... indices) + { return make_array(std::array{indices...}); } + + template<typename... Ts> + constexpr static auto + make_tuple(const std::tuple<Ts...>& tup) + { + constexpr size_t rank = sizeof...(Ts); + auto impl = [&]<size_t... I>(std::index_sequence<I...>) + { + auto idx = [rank](size_t i) consteval + { return rank - 1 - i; }; + return std::tuple<Ts...[idx(I)]...>{get<idx(I)>(tup)...}; + }; + return impl(std::make_index_sequence<rank>()); + } + + template<typename Mapping> + constexpr static auto + padded_stride(const Mapping& m) + { + auto rank = Mapping::extents_type::rank(); + return m.stride(rank - 2); + } + + template<typename Extents> + constexpr static auto + padded_extent(const Extents& exts) + { + auto rank = Extents::rank(); + return exts.extent(rank - 1); + } + }; + +#endif +#endif diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc new file mode 100644 index 0000000..edf07c9 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc @@ -0,0 +1,49 @@ +// { dg-do compile { target c++23 } } +#include<mdspan> + +#include <cstdint> + +constexpr size_t dyn = std::dynamic_extent; +static constexpr size_t n = std::numeric_limits<uint8_t>::max() / 2; + +template<typename Layout> + struct A + { + typename Layout::mapping<std::extents<uint8_t, n, 2>> m0; + typename Layout::mapping<std::extents<uint8_t, n, 2, dyn>> m1; + typename Layout::mapping<std::extents<uint8_t, n, 2, 0>> m2; + + using extents_type = std::extents<uint8_t, n, 4>; + typename Layout::mapping<extents_type> m3; // { dg-error "required from" } + }; + +template<size_t Count, typename Layout, typename OLayout> + bool + B() + { + using Extents = std::extents<uint8_t, dyn, dyn, Count>; + using OExtents = std::extents<uint16_t, n, 4, Count>; + + using Mapping = typename Layout::mapping<Extents>; + using OMapping = typename OLayout::mapping<OExtents>; + + Mapping m{OMapping{}}; + return true; + }; + +A<std::layout_left> a_left; // { dg-error "required from" } +A<std::layout_right> a_right; // { dg-error "required from" } +A<std::layout_stride> a_stride; // { dg-error "required from" } + +auto b1 = B<1, std::layout_left, std::layout_left>(); // { dg-error "required from" } +auto b2 = B<2, std::layout_left, std::layout_stride>(); // { dg-error "required from" } + +auto b3 = B<3, std::layout_right, std::layout_right>(); // { dg-error "required from" } +auto b4 = B<4, std::layout_right, std::layout_stride>(); // { dg-error "required from" } + +auto b5 = B<5, std::layout_stride, std::layout_right>(); // { dg-error "required from" } +auto b6 = B<6, std::layout_stride, std::layout_left>(); // { dg-error "required from" } +auto b7 = B<7, std::layout_stride, std::layout_stride>(); // { dg-error "required from" } + +// { dg-prune-output "must be representable as index_type" } +// { dg-prune-output "static assertion failed" } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc new file mode 100644 index 0000000..b6e4138 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc @@ -0,0 +1,532 @@ +// { dg-do run { target c++23 } } +#include <mdspan> + +#include "../layout_traits.h" +#include <cstdint> +#include <testsuite_hooks.h> + +constexpr size_t dyn = std::dynamic_extent; + +template<typename Mapping, typename IndexType, size_t... Extents> + constexpr void + verify(std::extents<IndexType, Extents...> oexts) + { + auto m = Mapping(oexts); + VERIFY(m.extents() == oexts); + } + +template<typename Mapping, typename OMapping> + requires (requires { typename OMapping::layout_type; }) + constexpr void + verify(OMapping other) + { + constexpr auto rank = Mapping::extents_type::rank(); + auto m = Mapping(other); + VERIFY(m.extents() == other.extents()); + if constexpr (rank > 0) + for(size_t i = 0; i < rank; ++i) + VERIFY(std::cmp_equal(m.stride(i), other.stride(i))); + } + +template<typename To, typename From> + constexpr void + verify_convertible(From from) + { + static_assert(std::is_convertible_v<From, To>); + verify<To>(from); + } + +template<typename To, typename From> + constexpr void + verify_nothrow_convertible(From from) + { + if constexpr (is_padded_layout<typename To::layout_type>) + static_assert(std::is_constructible_v<To, From>); + else + static_assert(std::is_nothrow_constructible_v<To, From>); + verify_convertible<To>(from); + } + +template<typename To, typename From> + constexpr void + verify_constructible(From from) + { + static_assert(!std::is_convertible_v<From, To>); + static_assert(std::is_constructible_v<To, From>); + verify<To>(from); + } + +template<typename To, typename From> + constexpr void + verify_nothrow_constructible(From from) + { + if constexpr (is_padded_layout<typename To::layout_type>) + static_assert(std::is_constructible_v<To, From>); + else + static_assert(std::is_nothrow_constructible_v<To, From>); + verify_constructible<To>(from); + } + +template<typename Mapping, typename OExtents> + constexpr void + assert_not_constructible() + { + static_assert(!std::is_constructible_v<Mapping, OExtents>); + } + +// ctor: mapping() +namespace default_ctor +{ + template<typename Layout, typename Extents> + constexpr void + test_default_ctor() + { + using Mapping = typename Layout::mapping<Extents>; + + Mapping m; + for(size_t i = 0; i < Extents::rank(); ++i) + if (Extents::static_extent(i) == std::dynamic_extent) + VERIFY(m.extents().extent(i) == 0); + else + VERIFY(m.extents().static_extent(i) == Extents::static_extent(i)); + } + + template<typename Layout> + constexpr bool + test_default_ctor_all() + { + test_default_ctor<Layout, std::extents<int, dyn>>(); + test_default_ctor<Layout, std::extents<int, 1, 2>>(); + test_default_ctor<Layout, std::extents<int, dyn, 2>>(); + test_default_ctor<Layout, std::extents<int, dyn, dyn>>(); + test_default_ctor<Layout, std::extents<int, dyn, 2, dyn>>(); + test_default_ctor<Layout, std::extents<int, dyn, dyn, dyn>>(); + return true; + } + + template<typename Layout> + constexpr void + test_all() + { + test_default_ctor_all<Layout>(); + static_assert(test_default_ctor_all<Layout>()); + } +} + +// ctor: mapping(const extents&) +namespace from_extents +{ + template<typename Layout, typename Extents, typename OExtents> + constexpr void + verify_nothrow_convertible(OExtents oexts) + { + using Mapping = typename Layout::mapping<Extents>; + ::verify_nothrow_convertible<Mapping>(oexts); + } + + template<typename Layout, typename Extents, typename OExtents> + constexpr void + verify_nothrow_constructible(OExtents oexts) + { + using Mapping = typename Layout::mapping<Extents>; + ::verify_nothrow_constructible<Mapping>(oexts); + } + + template<typename Layout, typename Extents, typename OExtents> + constexpr void + assert_not_constructible() + { + using Mapping = typename Layout::mapping<Extents>; + ::assert_not_constructible<Mapping, OExtents>(); + } + + template<typename Layout> + constexpr bool + test_ctor() + { + verify_nothrow_convertible<Layout, std::extents<int>>( + std::extents<int>{}); + + verify_nothrow_convertible<Layout, std::extents<int, 2>>( + std::extents<int, 2>{}); + + verify_nothrow_convertible<Layout, std::extents<int, dyn, 3>>( + std::extents<int, dyn, 3>{2}); + + verify_nothrow_constructible<Layout, std::extents<unsigned int>>( + std::extents<int>{}); + + verify_nothrow_constructible<Layout, std::extents<int, dyn>>( + std::extents<int, 2>{}); + + verify_nothrow_constructible<Layout, std::extents<int, dyn, 3>>( + std::extents<int, 2, 3>{}); + + assert_not_constructible<Layout, std::extents<int>, + std::extents<unsigned int>>(); + assert_not_constructible<Layout, std::extents<int, 2>, + std::extents<int, dyn>>(); + assert_not_constructible<Layout, std::extents<int, 2, 3>, + std::extents<int, dyn, 3>>(); + return true; + } + + template<typename Layout, typename Extents> + constexpr void + assert_deducible(Extents exts) + { + typename Layout::mapping m(exts); + static_assert(std::same_as<decltype(m), + typename Layout::mapping<Extents>>); + } + + template<typename Layout> + constexpr void + test_deducible() + { + assert_deducible<Layout>(std::extents<int>()); + assert_deducible<Layout>(std::extents<int, 1>()); + assert_deducible<Layout>(std::extents<int, 1, 2, dyn>(3)); + } + + template<typename Layout> + constexpr void + test_all() + { + test_ctor<Layout>(); + static_assert(test_ctor<Layout>()); + test_deducible<Layout>(); + } +} + +// ctor: mapping(mapping<OExtents>) +namespace from_same_layout +{ + template<typename Layout, typename Extents, typename OExtents> + constexpr void + verify_convertible(OExtents exts) + { + using Mapping = typename Layout::mapping<Extents>; + using OMapping = typename Layout::mapping<OExtents>; + + ::verify_convertible<Mapping>(OMapping(exts)); + } + + template<typename Layout, typename Extents, typename OExtents> + constexpr void + verify_nothrow_convertible(OExtents exts) + { + using Mapping = typename Layout::mapping<Extents>; + using OMapping = typename Layout::mapping<OExtents>; + + ::verify_nothrow_convertible<Mapping>(OMapping(exts)); + } + + template<typename Layout, typename Extents, typename OExtents> + constexpr void + verify_nothrow_constructible(OExtents exts) + { + using Mapping = typename Layout::mapping<Extents>; + using OMapping = typename Layout::mapping<OExtents>; + + ::verify_nothrow_constructible<Mapping>(OMapping(exts)); + } + + template<typename Layout> + constexpr bool + test_ctor() + { + verify_nothrow_convertible<Layout, std::extents<unsigned int>>( + std::extents<int>{}); + + verify_nothrow_constructible<Layout, std::extents<int>>( + std::extents<unsigned int>{}); + + assert_not_constructible< + typename Layout::mapping<std::extents<int>>, + typename Layout::mapping<std::extents<int, 1>>>(); + + assert_not_constructible< + typename Layout::mapping<std::extents<int, 1>>, + typename Layout::mapping<std::extents<int>>>(); + + verify_nothrow_constructible<Layout, std::extents<int, 1>>( + std::extents<int, dyn>{1}); + + verify_nothrow_convertible<Layout, std::extents<int, dyn>>( + std::extents<int, 1>{}); + + assert_not_constructible< + typename Layout::mapping<std::extents<int, 1, 2>>, + typename Layout::mapping<std::extents<int, 1>>>(); + + verify_nothrow_constructible<Layout, std::extents<int, 1, 2>>( + std::extents<int, dyn, 2>{1}); + + if constexpr (!is_padded_layout<Layout>) + verify_nothrow_convertible<Layout, std::extents<int, dyn, 2>>( + std::extents<int, 1, 2>{}); + else + verify_nothrow_constructible<Layout, std::extents<int, dyn, 2>>( + std::extents<int, 1, 2>{}); + return true; + } + + template<typename Layout> + constexpr void + test_all() + { + test_ctor<Layout>(); + static_assert(test_ctor<Layout>()); + } +} + +// ctor: mapping(layout_{right,left}::mapping<OExtents>) +namespace from_left_or_right +{ + template<typename SLayout, typename OLayout, typename SExtents, + typename OExtents> + constexpr void + verify_ctor(OExtents oexts) + { + using SMapping = typename SLayout::mapping<SExtents>; + using OMapping = typename OLayout::mapping<OExtents>; + + constexpr bool expected = std::is_convertible_v<OExtents, SExtents>; + if constexpr (expected) + verify_nothrow_convertible<SMapping>(OMapping(oexts)); + else + verify_nothrow_constructible<SMapping>(OMapping(oexts)); + } + + template<typename SLayout, typename OLayout> + constexpr bool + test_ctor() + { + assert_not_constructible< + typename SLayout::mapping<std::extents<int>>, + typename OLayout::mapping<std::extents<int, 1>>>(); + + verify_ctor<OLayout, SLayout, std::extents<int>>( + std::extents<unsigned int>{}); + + verify_ctor<OLayout, SLayout, std::extents<unsigned int>>( + std::extents<int>{}); + + assert_not_constructible< + typename SLayout::mapping<std::extents<int, 1>>, + typename OLayout::mapping<std::extents<int>>>(); + + verify_ctor<OLayout, SLayout, std::extents<int, 1>>( + std::extents<int, 1>{}); + + verify_ctor<OLayout, SLayout, std::extents<int, 1>>( + std::extents<unsigned int, 1>{}); + + verify_ctor<OLayout, SLayout, std::extents<unsigned int, 1>>( + std::extents<int, 1>{}); + + assert_not_constructible< + typename SLayout::mapping<std::extents<int, 1, 2>>, + typename OLayout::mapping<std::extents<int, 1, 2>>>(); + return true; + } + + template<typename SLayout, typename OLayout> + constexpr void + test_all() + { + test_ctor<SLayout, OLayout>(); + static_assert(test_ctor<SLayout, OLayout>()); + } +} + +// checks: convertibility of rank == 0 mappings. +namespace from_rank0 +{ + template<typename SLayout, typename OLayout, typename SExtents, + typename OExtents> + constexpr void + verify_ctor() + { + using SMapping = typename SLayout::mapping<SExtents>; + using OMapping = typename OLayout::mapping<OExtents>; + + constexpr bool expected = std::is_convertible_v<OExtents, SExtents>; + if constexpr (expected) + verify_nothrow_convertible<SMapping>(OMapping{}); + else + verify_nothrow_constructible<SMapping>(OMapping{}); + } + + template<typename Layout, typename OLayout> + constexpr void + test_rank0_convertibility() + { + using E1 = std::extents<int>; + using E2 = std::extents<unsigned int>; + + verify_ctor<Layout, OLayout, E1, E2>(); + verify_ctor<Layout, OLayout, E2, E1>(); + + verify_ctor<Layout, OLayout, E2, E2>(); + verify_ctor<Layout, OLayout, E1, E1>(); + } + + constexpr void + test_all() + { + auto run = []<typename Layout>(Layout) + { + test_rank0_convertibility<Layout, std::layout_left>(); + test_rank0_convertibility<Layout, std::layout_right>(); + test_rank0_convertibility<Layout, std::layout_stride>(); + }; + + auto run_all = [run]() + { + run(std::layout_left{}); + run(std::layout_right{}); + run(std::layout_stride{}); +#if __cplusplus > 202302L + run(std::layout_left_padded<0>{}); + run(std::layout_left_padded<1>{}); + run(std::layout_left_padded<6>{}); + run(std::layout_left_padded<dyn>{}); +#endif + return true; + }; + + run_all(); + static_assert(run_all()); + } +} + +// ctor: mapping(layout_stride::mapping<OExtents>) +namespace from_stride +{ + template<typename Mapping> + constexpr auto + strides(Mapping m) + { + constexpr auto rank = Mapping::extents_type::rank(); + std::array<typename Mapping::index_type, rank> s; + + if constexpr (rank > 0) + for(size_t i = 0; i < rank; ++i) + s[i] = m.stride(i); + return s; + } + + template<typename Layout, typename Extents, typename OExtents> + constexpr void + verify_nothrow_convertible(OExtents oexts) + { + using Mapping = typename Layout::mapping<Extents>; + using OMapping = std::layout_stride::mapping<OExtents>; + + constexpr auto other = OMapping(oexts, strides(Mapping(Extents(oexts)))); + ::verify_nothrow_convertible<Mapping>(other); + } + + template<typename Layout, typename Extents, typename OExtents> + constexpr void + verify_nothrow_constructible(OExtents oexts) + { + using Mapping = typename Layout::mapping<Extents>; + using OMapping = std::layout_stride::mapping<OExtents>; + + constexpr auto other = OMapping(oexts, strides(Mapping(Extents(oexts)))); + ::verify_nothrow_constructible<Mapping>(other); + } + + template<typename Layout> + constexpr bool + test_ctor() + { + assert_not_constructible< + typename Layout::mapping<std::extents<int>>, + std::layout_stride::mapping<std::extents<int, 1>>>(); + + assert_not_constructible< + typename Layout::mapping<std::extents<int, 1>>, + std::layout_stride::mapping<std::extents<int>>>(); + + assert_not_constructible< + typename Layout::mapping<std::extents<int, 2>>, + std::layout_stride::mapping<std::extents<int, 1>>>(); + + verify_nothrow_convertible<Layout, std::extents<int>>( + std::extents<int>{}); + + verify_nothrow_convertible<Layout, std::extents<unsigned int>>( + std::extents<int>{}); + + verify_nothrow_constructible<Layout, std::extents<int>>( + std::extents<unsigned int>{}); + + verify_nothrow_constructible<Layout, std::extents<int, 3>>( + std::extents<int, 3>{}); + + verify_nothrow_constructible<Layout, std::extents<unsigned int, 3>>( + std::extents<int, 3>{}); + + verify_nothrow_constructible<Layout, std::extents<int, 3>>( + std::extents<unsigned int, 3>{}); + + verify_nothrow_constructible<Layout, std::extents<int, 3, 5>>( + std::extents<int, 3, 5>{}); + + verify_nothrow_constructible<Layout, std::extents<unsigned int, 3, 5>>( + std::extents<int, 3, 5>{}); + + verify_nothrow_constructible<Layout, std::extents<int, 3, 5>>( + std::extents<unsigned int, 3, 5>{}); + return true; + } + + template<typename Layout> + constexpr void + test_all() + { + test_ctor<Layout>(); + static_assert(test_ctor<Layout>()); + } +} + +template<typename Layout> + constexpr void + test_all() + { + default_ctor::test_all<Layout>(); + from_extents::test_all<Layout>(); + from_same_layout::test_all<Layout>(); + from_stride::test_all<Layout>(); + } + +template<template<size_t> typename Layout> + constexpr void + test_padded_all() + { + test_all<Layout<0>>(); + test_all<Layout<1>>(); + test_all<Layout<2>>(); + test_all<Layout<dyn>>(); + } + +int +main() +{ + test_all<std::layout_left>(); + test_all<std::layout_right>(); +#if __cplusplus > 202302L + test_padded_all<std::layout_left_padded>(); + test_padded_all<std::layout_right_padded>(); +#endif + + from_left_or_right::test_all<std::layout_left, std::layout_right>(); + from_left_or_right::test_all<std::layout_right, std::layout_left>(); + + from_rank0::test_all(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/debug/out_of_bounds_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/debug/out_of_bounds_neg.cc new file mode 100644 index 0000000..fb8ff01 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/debug/out_of_bounds_neg.cc @@ -0,0 +1,30 @@ +// { dg-do compile { target c++23 } } +// { dg-require-debug-mode "" } +#include<mdspan> + +template<typename Layout> + constexpr bool + test_out_of_bounds_1d() + { + auto m = typename Layout::mapping<std::extents<int, 0>>{}; + (void) m(0); // { dg-error "expansion of" } + return true; + } +static_assert(test_out_of_bounds_1d<std::layout_left>()); // { dg-error "expansion of" } +static_assert(test_out_of_bounds_1d<std::layout_right>()); // { dg-error "expansion of" } +static_assert(test_out_of_bounds_1d<std::layout_stride>()); // { dg-error "expansion of" } + +template<typename Layout> + constexpr bool + test_out_of_bounds_3d() + { + auto m = typename Layout::mapping<std::extents<int, 3, 5, 7>>{}; + (void) m(2, 5, 5); // { dg-error "expansion of" } + return true; + } +static_assert(test_out_of_bounds_3d<std::layout_left>()); // { dg-error "expansion of" } +static_assert(test_out_of_bounds_3d<std::layout_right>()); // { dg-error "expansion of" } +static_assert(test_out_of_bounds_3d<std::layout_stride>()); // { dg-error "expansion of" } + +// { dg-prune-output "non-constant condition for static assertion" } +// { dg-prune-output "__glibcxx_assert" } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc new file mode 100644 index 0000000..8840d07 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc @@ -0,0 +1,148 @@ +// { dg-do run { target c++23 } } +#include <mdspan> + +#include <cstdint> +#include <algorithm> +#include <testsuite_hooks.h> + +constexpr size_t dyn = std::dynamic_extent; + +template<typename Mapping> + constexpr void + invoke_stride(Mapping m) + { + // Only checking for UB, e.g. signed overflow. + for(size_t i = 0; i < Mapping::extents_type::rank(); ++i) + m.stride(i); + } + +template<typename Mapping> + constexpr void + verify_required_span_size(Mapping m) + { VERIFY(m.required_span_size() == 0); } + +template<typename Mapping> + constexpr void + verify_all(Mapping m) + { + verify_required_span_size(m); + invoke_stride(m); + } + +template<typename Layout, typename Int> + constexpr void + test_static_overflow() + { + constexpr Int n1 = std::numeric_limits<Int>::max(); + constexpr size_t n2 = std::dynamic_extent - 1; + // Allow some room for padding. + constexpr size_t n = (std::cmp_less(n1, n2) ? size_t(n1) : n2) - 4; + + verify_all(typename Layout::mapping<std::extents<Int, n, n, 0, n, n>>{}); + verify_all(typename Layout::mapping<std::extents<Int, 0, n, n, n>>{}); + verify_all(typename Layout::mapping<std::extents<Int, dyn, n, n, n>>{}); + verify_all(typename Layout::mapping<std::extents<Int, n, n, n, 0>>{}); + verify_all(typename Layout::mapping<std::extents<Int, n, n, n, dyn>>{}); + } + +template<typename Int, size_t N> + constexpr std::array<Int, N> + make_strides() + { + std::array<Int, N> strides; + std::ranges::fill(strides, Int(1)); + return strides; + } + +template<typename Layout, typename Extents> + constexpr typename Layout::mapping<Extents> + make_mapping(Extents exts) + { + using IndexType = typename Extents::index_type; + constexpr auto rank = Extents::rank(); + constexpr auto strides = make_strides<IndexType, rank>(); + + if constexpr (std::same_as<Layout, std::layout_stride>) + return typename Layout::mapping(exts, strides); + else + return typename Layout::mapping(exts); + } + +template<typename Layout, typename Int> + constexpr void + test_dynamic_overflow() + { + constexpr Int n1 = std::numeric_limits<Int>::max(); + constexpr size_t n2 = std::dynamic_extent - 1; + // Allow some room for padding. + constexpr Int n = (std::cmp_less(n1, n2) ? n1 : Int(n2)) - 4; + + verify_all(make_mapping<Layout>( + std::extents<Int, dyn, dyn, 0, dyn, dyn>{n, n, n, n})); + + verify_all(make_mapping<Layout>( + std::extents<Int, dyn, dyn, dyn, dyn, dyn>{n, n, 0, n, n})); + + verify_all(make_mapping<Layout>( + std::extents<Int, dyn, dyn, dyn, 0>{n, n, n})); + + verify_all(make_mapping<Layout>( + std::extents<Int, dyn, dyn, dyn, dyn>{n, n, n, 0})); + + verify_all(make_mapping<Layout>( + std::extents<Int, 0, dyn, dyn, dyn>{n, n, n})); + + verify_all(make_mapping<Layout>( + std::extents<Int, dyn, dyn, dyn, dyn>{0, n, n, n})); + } + +template<typename Layout, typename Int> + constexpr void + test_overflow() + { + test_static_overflow<Layout, Int>(); + test_dynamic_overflow<Layout, Int>(); + } + +template<typename Layout> + constexpr bool + test_all() + { + test_overflow<Layout, signed char>(); + test_overflow<Layout, short int>(); + test_overflow<Layout, int>(); + test_overflow<Layout, long int>(); + test_overflow<Layout, long long int>(); + + test_overflow<Layout, unsigned char>(); + test_overflow<Layout, unsigned short int>(); + test_overflow<Layout, unsigned int>(); + test_overflow<Layout, unsigned long int>(); + test_overflow<Layout, unsigned long long int>(); + test_overflow<Layout, size_t>(); + return true; + } + +template<template<size_t> typename Layout> + constexpr bool + test_padded_all() + { + static_assert(test_all<Layout<0>>()); + static_assert(test_all<Layout<1>>()); + static_assert(test_all<Layout<2>>()); + static_assert(test_all<Layout<dyn>>()); + return true; + } + +int +main() +{ + static_assert(test_all<std::layout_left>()); + static_assert(test_all<std::layout_right>()); + static_assert(test_all<std::layout_stride>()); +#if __cplusplus > 202302L + static_assert(test_padded_all<std::layout_left_padded>()); + static_assert(test_padded_all<std::layout_right_padded>()); +#endif + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc new file mode 100644 index 0000000..17cfac5 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc @@ -0,0 +1,717 @@ +// { dg-do run { target c++23 } } +#include <mdspan> + +#include "../int_like.h" +#include "../layout_traits.h" +#include <cstdint> +#include <testsuite_hooks.h> + +constexpr size_t dyn = std::dynamic_extent; + +template<typename Mapping> + concept has_static_is_exhaustive = requires + { + { Mapping::is_exhaustive() } -> std::same_as<bool>; + }; + +static_assert(has_static_is_exhaustive<std::layout_right::mapping<std::extents<int>>>); +static_assert(!has_static_is_exhaustive<std::layout_stride::mapping<std::extents<int>>>); + +template<typename Layout, typename Extents> + constexpr bool + test_mapping_properties() + { + using M = typename Layout::mapping<Extents>; + static_assert(std::__mdspan::__is_extents<typename M::extents_type>); + static_assert(std::__mdspan::__mapping_alike<M>); + static_assert(std::copyable<M>); + static_assert(std::is_nothrow_move_constructible_v<M>); + static_assert(std::is_nothrow_move_assignable_v<M>); + static_assert(std::is_nothrow_swappable_v<M>); + static_assert(std::is_same_v<typename M::extents_type, Extents>); + static_assert(std::is_same_v<typename M::index_type, + typename M::extents_type::index_type>); + static_assert(std::is_same_v<typename M::size_type, + typename M::extents_type::size_type>); + static_assert(std::is_same_v<typename M::rank_type, + typename M::extents_type::rank_type>); + static_assert(std::is_same_v<typename M::layout_type, Layout>); + + static_assert(std::is_trivially_copyable_v<M>); + static_assert(std::regular<M>); + + static_assert(M::is_always_unique() && M::is_unique()); + static_assert(M::is_always_strided() && M::is_strided()); + if constexpr (has_static_is_exhaustive<M>) + static_assert(M::is_always_exhaustive() && M::is_exhaustive()); + return true; + } + +template<typename Layout> + constexpr bool + test_mapping_properties_all() + { + test_mapping_properties<Layout, std::extents<int>>(); + test_mapping_properties<Layout, std::extents<int, 1>>(); + test_mapping_properties<Layout, std::extents<int, dyn>>(); + test_mapping_properties<Layout, std::extents<int, dyn, dyn>>(); + return true; + } + +// Check operator()(Indices...) +template<typename Mapping, size_t N> + constexpr typename Mapping::index_type + linear_index(const Mapping& mapping, + const std::array<typename Mapping::index_type, N>& indices) + { + typename Mapping::index_type ret = 0; + for(size_t r = 0; r < indices.size(); ++r) + ret += indices[r] * mapping.stride(r); + return ret; + } + +template<typename Int, typename Mapping, typename... Indices> + constexpr void + test_linear_index(const Mapping& m, Indices... i) + { + using index_type = typename Mapping::index_type; + index_type expected = linear_index(m, std::array{index_type(i)...}); + VERIFY(m(Int(i)...) == expected); + } + +template<typename Layout> + constexpr void + test_linear_index_0d() + { + constexpr typename Layout::mapping<std::extents<int>> m; + VERIFY(m() == 0); + } + +template<typename Layout, typename Int> + constexpr void + test_linear_index_1d() + { + typename Layout::mapping<std::extents<int, 5>> m; + test_linear_index<Int>(m, 0); + test_linear_index<Int>(m, 1); + test_linear_index<Int>(m, 4); + } + +template<typename Layout, typename Int> + constexpr void + test_linear_index_2d() + { + typename Layout::mapping<std::extents<int, 3, 256>> m; + test_linear_index<Int>(m, 0, 0); + test_linear_index<Int>(m, 1, 0); + test_linear_index<Int>(m, 0, 1); + test_linear_index<Int>(m, 1, 1); + test_linear_index<Int>(m, 2, 4); + } + +template<typename Layout> + struct MappingFactory + { + template<typename Extents> + static constexpr typename Layout::mapping<Extents> + create(Extents exts) + { return exts; } + }; + +template<> + struct MappingFactory<std::layout_stride> + { + template<typename Extents> + static constexpr std::layout_stride::mapping<Extents> + create(Extents exts) + { + if constexpr (Extents::rank() == 0) + { + auto strides = std::array<size_t, 0>{}; + return std::layout_stride::mapping(exts, strides); + } + else if constexpr (Extents::rank() == 1) + { + auto strides = std::array<size_t, 1>{2}; + return std::layout_stride::mapping(exts, strides); + } + else if constexpr (Extents::rank() == 2) + { + size_t m = exts.extent(1); + auto strides = std::array<size_t, 2>{3*m, 2}; + return std::layout_stride::mapping(exts, strides); + } + else if constexpr (Extents::rank() == 3) + { + size_t n = exts.extent(0); + size_t m = exts.extent(1); + auto strides = std::array<size_t, 3>{3*m, 2, 11*m*n}; + return std::layout_stride::mapping(exts, strides); + } + } + }; + +template<typename Layout, typename Int> + constexpr void + test_linear_index_3d() + { + auto m = MappingFactory<Layout>::create(std::extents(3, 5, 7)); + test_linear_index<Int>(m, 0, 0, 0); + test_linear_index<Int>(m, 1, 0, 0); + test_linear_index<Int>(m, 0, 1, 0); + test_linear_index<Int>(m, 0, 0, 1); + test_linear_index<Int>(m, 1, 1, 0); + test_linear_index<Int>(m, 2, 4, 6); + } + +template<typename Mapping, typename... Ints> + concept has_linear_index = requires (Mapping m) + { + { m(Ints(0)...) } -> std::same_as<typename Mapping::index_type>; + }; + +template<typename Layout> + constexpr void + test_has_linear_index_0d() + { + using Mapping = typename Layout::mapping<std::extents<int>>; + static_assert(has_linear_index<Mapping>); + static_assert(!has_linear_index<Mapping, int>); + static_assert(!has_linear_index<Mapping, IntLike>); + static_assert(!has_linear_index<Mapping, NotIntLike>); + } + +template<typename Layout> + constexpr void + test_has_linear_index_1d() + { + using Mapping = typename Layout::mapping<std::extents<int, 3>>; + static_assert(!has_linear_index<Mapping>); + static_assert(has_linear_index<Mapping, int>); + static_assert(has_linear_index<Mapping, double>); + static_assert(has_linear_index<Mapping, IntLike>); + static_assert(has_linear_index<Mapping, MutatingInt>); + static_assert(!has_linear_index<Mapping, ThrowingInt>); + static_assert(!has_linear_index<Mapping, NotIntLike>); + static_assert(!has_linear_index<Mapping, int, int>); + } + +template<typename Layout> + constexpr void + test_has_linear_index_2d() + { + using Mapping = typename Layout::mapping<std::extents<int, 3, 5>>; + static_assert(!has_linear_index<Mapping, int>); + static_assert(has_linear_index<Mapping, int, int>); + static_assert(has_linear_index<Mapping, double, double>); + static_assert(has_linear_index<Mapping, IntLike, int>); + static_assert(has_linear_index<Mapping, MutatingInt, int>); + static_assert(!has_linear_index<Mapping, ThrowingInt, int>); + static_assert(!has_linear_index<Mapping, NotIntLike, int>); + static_assert(!has_linear_index<Mapping, int, int, int>); + } + +template<typename Layout, typename Int> + constexpr bool + test_linear_index_all() + { + test_linear_index_0d<Layout>(); + test_linear_index_1d<Layout, Int>(); + test_linear_index_2d<Layout, Int>(); + test_linear_index_3d<Layout, Int>(); + test_has_linear_index_0d<Layout>(); + test_has_linear_index_1d<Layout>(); + test_has_linear_index_2d<Layout>(); + return true; + } + +template<typename Mapping> + constexpr typename Mapping::index_type + linear_index_end(Mapping m) + { + using index_type = typename Mapping::index_type; + constexpr size_t rank = Mapping::extents_type::rank(); + + auto impl = [m]<index_type... Counts>( + std::integer_sequence<index_type, Counts...>) -> index_type + { + auto exts = m.extents(); + if(((exts.extent(Counts) == 0) || ...)) + return 0; + return m((exts.extent(Counts) - 1)...) + 1; + }; + + return impl(std::make_integer_sequence<index_type, rank>()); + } + +// Check required_span_size +template<typename Mapping> + constexpr void + test_required_span_size(Mapping m) + { VERIFY(m.required_span_size() == linear_index_end(m)); } + +template<typename Layout> + constexpr void + test_required_span_size_0d() + { + typename Layout::mapping<std::extents<int>> m; + test_required_span_size(m); + } + +template<typename Layout> + constexpr void + test_required_span_size_1d() + { + auto m = MappingFactory<Layout>::create(std::extents(3)); + test_required_span_size(m); + } + +template<typename Layout> + constexpr void + test_required_span_size_2d() + { + auto m = MappingFactory<Layout>::create(std::extents(3, 5)); + test_required_span_size(m); + } + +template<typename Layout> + constexpr void + test_required_span_size_3d() + { + auto m = MappingFactory<Layout>::create(std::extents(3, 5, 7)); + test_required_span_size(m); + } + +template<typename Layout> + constexpr void + test_required_span_size_zero_1d() + { + auto m = MappingFactory<Layout>::create(std::extents(3, 0)); + test_required_span_size(m); + } + +template<typename Layout> + constexpr void + test_required_span_size_zero_3d() + { + auto m = MappingFactory<Layout>::create(std::extents(3, 0, 7)); + test_required_span_size(m); + } + +template<typename Layout> + constexpr bool + test_required_span_size_all() + { + test_required_span_size_0d<Layout>(); + test_required_span_size_1d<Layout>(); + test_required_span_size_2d<Layout>(); + test_required_span_size_3d<Layout>(); + test_required_span_size_zero_1d<Layout>(); + test_required_span_size_zero_3d<Layout>(); + return true; + } + +// Check stride +template<typename Layout> + constexpr void + test_stride_1d() + { + typename Layout::mapping<std::extents<int, 3>> m; + VERIFY(m.stride(0) == 1); + } + +template<> + constexpr void + test_stride_1d<std::layout_stride>() + { + std::array<int, 1> strides{13}; + std::layout_stride::mapping m(std::extents<int, 3>{}, strides); + VERIFY(m.stride(0) == strides[0]); + VERIFY(m.strides() == strides); + } + +template<typename Layout> +struct TestStride2D; + +template<> + struct TestStride2D<std::layout_left> + { + static constexpr void + run() + { + std::layout_left::mapping<std::extents<int, 3, 5>> m; + VERIFY(m.stride(0) == 1); + VERIFY(m.stride(1) == 3); + } + }; + +template<> + struct TestStride2D<std::layout_right> + { + static constexpr void + run() + { + std::layout_right::mapping<std::extents<int, 3, 5>> m; + VERIFY(m.stride(0) == 5); + VERIFY(m.stride(1) == 1); + } + }; + +template<> + struct TestStride2D<std::layout_stride> + { + static constexpr void + run() + { + std::array<int, 2> strides{13, 2}; + std::layout_stride::mapping m(std::extents<int, 3, 5>{}, strides); + VERIFY(m.stride(0) == strides[0]); + VERIFY(m.stride(1) == strides[1]); + VERIFY(m.strides() == strides); + } + }; + +#if __cplusplus > 202302L +template<typename Layout> + requires is_left_padded<Layout> || is_right_padded<Layout> + struct TestStride2D<Layout> + { + static constexpr void + run() + { + using Traits = LayoutTraits<DeducePaddingSide::from_typename<Layout>()>; + using Extents = typename Traits::extents_type<std::extents<int, 3, 5>>; + using Mapping = typename Layout::mapping<Extents>; + constexpr size_t padding_value = Mapping::padding_value; + + Mapping m; + size_t effective_pad = (padding_value == 0 || padding_value == dyn) + ? size_t(1) : padding_value; + + constexpr auto i0 = is_left_padded<Layout> ? 0 : 1; + VERIFY(m.stride(i0) == 1); + + // The next multiple of padding_value, that's greater or equal + // to exts.extent(0) is the unique value in the range: + // [exts.extent(0), exts.extent(0) + padding_value) + // that is divisible by padding_value. + auto stride = Traits::padded_stride(m); + VERIFY((stride % effective_pad) == 0); + VERIFY(3 <= stride && std::cmp_less(stride, 3 + effective_pad)); + } + }; +#endif + +template<typename Layout> + constexpr void + test_stride_2d() + { + TestStride2D<Layout>::run(); + } + +template<typename Layout> +struct TestStride3D; + +template<> + struct TestStride3D<std::layout_left> + { + static constexpr void + run() + { + std::layout_left::mapping m(std::dextents<int, 3>(3, 5, 7)); + VERIFY(m.stride(0) == 1); + VERIFY(m.stride(1) == 3); + VERIFY(m.stride(2) == 3*5); + } + }; + + +template<> + struct TestStride3D<std::layout_right> + { + static constexpr void + run() + { + std::layout_right::mapping m(std::dextents<int, 3>(3, 5, 7)); + VERIFY(m.stride(0) == 5*7); + VERIFY(m.stride(1) == 7); + VERIFY(m.stride(2) == 1); + } + }; + +template<> + struct TestStride3D<std::layout_stride> + { + static constexpr void + run() + { + std::dextents<int, 3> exts(3, 5, 7); + std::array<int, 3> strides{11, 2, 41}; + std::layout_stride::mapping<std::dextents<int, 3>> m(exts, strides); + VERIFY(m.stride(0) == strides[0]); + VERIFY(m.stride(1) == strides[1]); + VERIFY(m.stride(2) == strides[2]); + VERIFY(m.strides() == strides); + } + }; + +#if __cplusplus > 202302L +template<typename Layout> + requires is_left_padded<Layout> || is_right_padded<Layout> + struct TestStride3D<Layout> + { + static constexpr void + run() + { + using Traits = LayoutTraits<DeducePaddingSide::from_typename<Layout>()>; + using Extents = typename Traits::extents_type<std::extents<int, 3, 5, 7>>; + using Mapping = typename Layout::mapping<Extents>; + constexpr size_t padding_value = Mapping::padding_value; + + Mapping m; + size_t effective_pad = (padding_value == 0 || padding_value == dyn) + ? size_t(1) : padding_value; + + constexpr auto i0 = is_left_padded<Layout> ? 0 : 2; + VERIFY(m.stride(i0) == 1); + + // The next multiple of padding_value, that's greater or equal + // to exts.extent(0) is the unique value in the range: + // [exts.extent(0), exts.extent(0) + padding_value) + // that is divisible by padding_value. + auto stride = Traits::padded_stride(m); + VERIFY((stride % effective_pad) == 0); + VERIFY(3 <= stride && std::cmp_less(stride, 3 + effective_pad)); + + constexpr auto i2 = is_left_padded<Layout> ? 2 : 0; + VERIFY(stride * 5 == m.stride(i2)); + } + }; +#endif + +template<typename Layout> + constexpr void + test_stride_3d() + { + TestStride3D<Layout>::run(); + } + +template<typename Layout> + constexpr bool + test_stride_all() + { + test_stride_1d<Layout>(); + test_stride_2d<Layout>(); + test_stride_3d<Layout>(); + return true; + } + +template<typename Mapping> + concept has_stride = requires (Mapping m) + { + { m.stride(0) } -> std::same_as<typename Mapping::index_type>; + }; + +template<typename Layout> + constexpr void + test_has_stride_0d() + { + using Mapping = typename Layout::mapping<std::extents<int>>; + constexpr bool expected = !(std::is_same_v<Layout, std::layout_left> + || std::is_same_v<Layout, std::layout_right>); + static_assert(has_stride<Mapping> == expected); + } + +template<typename Layout> + constexpr void + test_has_stride_1d() + { static_assert(has_stride<typename Layout::mapping<std::extents<int, 1>>>); } + +template<typename Layout> + constexpr void + test_has_stride_2d() + { + using Extents = std::extents<int, 1, 2>; + static_assert(has_stride<typename Layout::mapping<Extents>>); + } + +// Check operator== +template<typename Layout> + constexpr void + test_eq() + { + typename Layout::mapping<std::extents<int, 1, 2>> m1; + typename Layout::mapping<std::extents<int, 2, 2>> m2; + typename Layout::mapping<std::dextents<int, 2>> m3(m1); + + VERIFY(m1 == m1); + VERIFY(m1 != m2); + VERIFY(m1 == m3); + VERIFY(m2 != m3); + } + +template<typename Layout> + constexpr void + test_eq_zero() + { + typename Layout::mapping<std::extents<int, 0, 2>> m1; + typename Layout::mapping<std::extents<int, 0, 2>> m2; + typename Layout::mapping<std::extents<int, 2, 0>> m3; + + VERIFY(m1 == m2); + VERIFY(m1 != m3); + } + +template<typename M1, typename M2> + concept has_op_eq = requires (M1 m1, M2 m2) + { + { m1 == m2 } -> std::same_as<bool>; + { m2 == m1 } -> std::same_as<bool>; + { m1 != m2 } -> std::same_as<bool>; + { m2 != m1 } -> std::same_as<bool>; + }; + +template<typename SLayout, typename OLayout, bool Expected> + constexpr void + test_has_op_eq() + { + static_assert(has_op_eq< + typename SLayout::mapping<std::extents<int>>, + typename OLayout::mapping<std::extents<int>>> == Expected); + + static_assert(!has_op_eq< + typename SLayout::mapping<std::extents<int>>, + typename OLayout::mapping<std::extents<int, 1>>>); + + static_assert(has_op_eq< + typename SLayout::mapping<std::extents<int, 1>>, + typename OLayout::mapping<std::extents<int, 1>>> == Expected); + + static_assert(has_op_eq< + typename SLayout::mapping<std::extents<int, 1>>, + typename OLayout::mapping<std::extents<int, 2>>> == Expected); + + static_assert(!has_op_eq< + typename SLayout::mapping<std::extents<int, 1>>, + typename OLayout::mapping<std::extents<int, 1, 2>>>); + + static_assert(has_op_eq< + typename SLayout::mapping<std::extents<int, 1, 2>>, + typename OLayout::mapping<std::extents<int, 1, 2>>> == Expected); + + static_assert(has_op_eq< + typename SLayout::mapping<std::extents<int, 1, 2>>, + typename OLayout::mapping<std::extents<int, 2, 2>>> == Expected); + + static_assert(!has_op_eq< + typename SLayout::mapping<std::extents<int, 1, 2>>, + typename OLayout::mapping<std::extents<int, 1, 2, 3>>>); + } + +constexpr void +test_has_op_eq_peculiar() +{ + static_assert(has_op_eq< + std::layout_right::mapping<std::extents<int>>, + std::layout_left::mapping<std::extents<unsigned int>>>); + + static_assert(has_op_eq< + std::layout_right::mapping<std::extents<int, 1>>, + std::layout_left::mapping<std::extents<int, dyn>>>); + + static_assert(!has_op_eq< + std::layout_right::mapping<std::extents<int, 1, 2>>, + std::layout_left::mapping<std::extents<int, dyn, 2>>>); +} + +template<typename Layout> + constexpr bool + test_mapping_all() + { + test_linear_index_all<Layout, uint8_t>(); + test_linear_index_all<Layout, int>(); + if !consteval + { + test_linear_index_all<Layout, IntLike>(); + test_linear_index_all<Layout, MutatingInt>(); + test_linear_index_all<Layout, RValueInt>(); + } + + test_required_span_size_all<Layout>(); + test_stride_all<Layout>(); + + test_eq<Layout>(); + test_eq_zero<Layout>(); + return true; + } + +template<typename Layout> + constexpr void + test_all() + { + static_assert(std::is_trivially_default_constructible_v<Layout>); + static_assert(std::is_trivially_copyable_v<Layout>); + static_assert(test_mapping_properties_all<Layout>()); + + test_mapping_all<Layout>(); + static_assert(test_mapping_all<Layout>()); + + test_has_stride_0d<Layout>(); + test_has_stride_1d<Layout>(); + test_has_stride_2d<Layout>(); + test_has_op_eq<Layout, Layout, true>(); + } + +#if __cplusplus > 202302L +template<template<size_t> typename Layout> + constexpr bool + test_padded_all() + { + test_all<Layout<0>>(); + test_all<Layout<1>>(); + test_all<Layout<2>>(); + test_all<Layout<5>>(); + test_all<Layout<dyn>>(); + return true; + } + +template<template<size_t> typename Layout> + constexpr bool + test_padded_has_op_eq() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + test_has_op_eq<typename Traits::layout_same, Layout<0>, false>(); + test_has_op_eq<typename Traits::layout_same, Layout<6>, false>(); + test_has_op_eq<typename Traits::layout_same, Layout<dyn>, false>(); + // The next one looks strange, because it's neither. Somehow, the + // conversion rules seem to be playing a critical role again. + // test_has_op_eq<typename Traits::layout_other, Layout<0>, false>(); + + test_has_op_eq<Layout<2>, Layout<6>, true>(); + test_has_op_eq<Layout<2>, Layout<dyn>, true>(); + return true; + } +#endif + +int +main() +{ + test_all<std::layout_left>(); + test_all<std::layout_right>(); + test_all<std::layout_stride>(); +#if __cplusplus > 202302L + test_padded_all<std::layout_left_padded>(); + test_padded_all<std::layout_right_padded>(); +#endif + + test_has_op_eq<std::layout_right, std::layout_left, false>(); + test_has_op_eq<std::layout_right, std::layout_stride, true>(); + test_has_op_eq<std::layout_left, std::layout_stride, true>(); +#if __cplusplus > 202302L + test_padded_has_op_eq<std::layout_left_padded>(); + test_padded_has_op_eq<std::layout_right_padded>(); +#endif + + test_has_op_eq_peculiar(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc new file mode 100644 index 0000000..1b6e063 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc @@ -0,0 +1,674 @@ +// { dg-do run { target c++26 } } +#include <mdspan> + +#include <cstdint> +#include "../int_like.h" +#include "../layout_traits.h" +#include <testsuite_hooks.h> + +constexpr size_t dyn = std::dynamic_extent; + +template<template<size_t> typename Layout> + constexpr bool + test_representable_padded_size() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + { + using E = typename Traits::extents_type<std::extents<uint8_t, 64, 2>>; + [[maybe_unused]] typename Layout<1>::mapping<E> m1; + } + + { + using E = typename Traits::extents_type<std::extents<uint8_t, 0, 2>>; + [[maybe_unused]] typename Layout<0>::mapping<E> m1; + [[maybe_unused]] typename Layout<1>::mapping<E> m2; + [[maybe_unused]] typename Layout<128>::mapping<E> m3; + [[maybe_unused]] typename Layout<255>::mapping<E> m4; + } + + { + using E = typename Traits::extents_type<std::extents<uint8_t, 0, 2>>; + [[maybe_unused]] typename Layout<dyn>::mapping<E> m1(E{}, 0); + [[maybe_unused]] typename Layout<dyn>::mapping<E> m2(E{}, 1); + [[maybe_unused]] typename Layout<dyn>::mapping<E> m3(E{}, 128); + [[maybe_unused]] typename Layout<dyn>::mapping<E> m4(E{}, 255); + } + + { + using E = typename Traits::extents_type<std::extents<uint8_t, dyn, 2>>; + [[maybe_unused]] typename Layout<0>::mapping<E> m1; + [[maybe_unused]] typename Layout<1>::mapping<E> m2; + [[maybe_unused]] typename Layout<128>::mapping<E> m3; + [[maybe_unused]] typename Layout<255>::mapping<E> m4; + } + return true; + } + +template<typename Layout, typename CanonicalExtents> + constexpr void + test_default_ctor_single(auto canonical_strides) + { + using Traits = LayoutTraits<DeducePaddingSide::from_typename<Layout>()>; + using E = typename Traits::extents_type<CanonicalExtents>; + auto strides = Traits::make_array(canonical_strides); + + typename Layout::template mapping<E> msta; + VERIFY(msta.stride(0) == strides[0]); + VERIFY(msta.stride(1) == strides[1]); + } + + +template<template<size_t> typename Layout> + constexpr bool + test_default_ctor() + { + using E1 = std::extents<size_t, 3, 5>; + test_default_ctor_single<Layout<2>, E1>(std::array<size_t, 2>{1, 4}); + test_default_ctor_single<Layout<dyn>, E1>(std::array<size_t, 2>{1, 3}); + + using E2 = std::extents<size_t, dyn, 5>; + test_default_ctor_single<Layout<2>, E2>(std::array<size_t, 2>{1, 0}); + test_default_ctor_single<Layout<dyn>, E2>(std::array<size_t, 2>{1, 0}); + return true; + } + +template<template<size_t> typename Layout> + constexpr bool + test_from_exts() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + auto exts = Traits::make_extents(std::dextents<size_t, 2>{3, 5}); + + typename Layout<0>::mapping m0_sta(exts); + VERIFY(Traits::padded_stride(m0_sta) == Traits::padded_extent(exts)); + + typename Layout<1>::mapping m1_sta(exts); + VERIFY(Traits::padded_stride(m1_sta) == Traits::padded_extent(exts)); + + typename Layout<2>::mapping m2_sta(exts); + VERIFY(Traits::padded_stride(m2_sta) == 4); + + typename Layout<dyn>::mapping mdyn(exts); + VERIFY(Traits::padded_stride(mdyn) == Traits::padded_extent(exts)); + return true; + } + +template<typename Layout, typename CustomPadType> + constexpr bool + test_from_pad_single() + { + using Traits = LayoutTraits<DeducePaddingSide::from_typename<Layout>()>; + auto pad = 3; + auto exts = Traits::make_extents(std::dextents<size_t, 3>{pad + 1, 5, 7}); + typename Layout::mapping m(exts, CustomPadType{pad}); + VERIFY(std::cmp_equal(Traits::padded_stride(m), 2*pad)); + VERIFY(m.extents() == exts); + return true; + } + +template<typename Layout> + constexpr void + test_from_pad() + { + test_from_pad_single<Layout, int>(); + static_assert(test_from_pad_single<Layout, int>()); + + test_from_pad_single<Layout, IntLike>(); + test_from_pad_single<Layout, MutatingInt>(); + test_from_pad_single<Layout, RValueInt>(); + + using Extents = std::dims<3>; + using Mapping = Layout::template mapping<Extents>; + static_assert(!std::is_constructible_v<Mapping, Extents, ThrowingInt>); + static_assert(!std::is_constructible_v<Mapping, Extents, NotIntLike>); + } + +template<template<size_t> typename Layout> + constexpr void + test_from_pad_all() + { + test_from_pad<Layout<3>>(); + test_from_pad<Layout<dyn>>(); + } + +constexpr bool +is_same_mapping(const auto& lhs, const auto& rhs) +{ + if (lhs.extents().rank() != rhs.extents().rank()) + return false; + + if (lhs.extents() != rhs.extents()) + return false; + + for (size_t i = 0; i < lhs.extents().rank(); ++i) + if (lhs.stride(i) != rhs.stride(i)) + return false; + return true; +} + +enum class ConversionRule +{ + Never, + Regular +}; + +template<typename To, typename From> +consteval bool +should_convert(auto rule) +{ + if constexpr (rule == ConversionRule::Never) + return false; + else + return std::is_convertible_v<typename From::extents_type, + typename To::extents_type>; +} + +template<typename To, typename From> + constexpr void + check_convertible(const From& m, auto conversion_rule) + { + VERIFY(is_same_mapping(m, To(m))); + constexpr bool expected = should_convert<To, From>(conversion_rule); + static_assert(std::is_convertible_v<From, To> == expected); + } + +template<typename LayoutTo, typename Esta, typename Edyn, typename Ewrong> + constexpr void + check_convertible_variants(auto msta, auto conversion_rule) + { + using LayoutFrom = decltype(msta)::layout_type; + constexpr auto cregular = std::cw<ConversionRule::Regular>; + + // There's a twist when both mappings are left-padded. There's two distinct + // ctors: a defaulted copy ctor and a constrained template that enables + // construction from left-padded mappings even if their layout_type (padding) is + // different. The two ctors have different rules regarding conversion. + + if constexpr (!std::same_as<LayoutTo, LayoutFrom>) + check_convertible<typename LayoutTo::mapping<Esta>>(msta, conversion_rule); + else + check_convertible<typename LayoutTo::mapping<Esta>>(msta, cregular); + + check_convertible<typename LayoutTo::mapping<Edyn>>(msta, conversion_rule); + + auto mdyn = typename LayoutFrom::mapping<Edyn>(msta); + check_convertible<typename LayoutTo::mapping<Esta>>(mdyn, conversion_rule); + if constexpr (!std::same_as<LayoutTo, LayoutFrom>) + check_convertible<typename LayoutTo::mapping<Edyn>>(mdyn, conversion_rule); + else + check_convertible<typename LayoutTo::mapping<Edyn>>(mdyn, cregular); + + static_assert(!std::is_constructible_v< + typename LayoutTo::mapping<Esta>, typename LayoutFrom::mapping<Ewrong>>); + }; + +template<typename Layout> + constexpr void + test_from_same_1d() + { + using E1 = std::extents<int, 6>; + using E2 = std::extents<int, dyn>; + using E3 = std::extents<int, 5>; + constexpr auto cr = std::cw<ConversionRule::Regular>; + + using Traits = LayoutTraits<DeducePaddingSide::from_typename<Layout>()>; + auto msta = typename Traits::layout_same::mapping(E1{}); + check_convertible_variants<Layout, E1, E2, E3>(msta, cr); + } + +template<typename Layout> + constexpr void + test_from_same_2d() + { + using Traits = LayoutTraits<DeducePaddingSide::from_typename<Layout>()>; + using E1 = typename Traits::extents_type<std::extents<int, 6, 5>>; + using E2 = typename Traits::extents_type<std::extents<int, dyn, 5>>; + using E3 = typename Traits::extents_type<std::extents<int, 6, 6>>; + constexpr auto cr = std::cw<ConversionRule::Regular>; + + auto msta = typename Traits::layout_same::mapping(E1{}); + check_convertible_variants<Layout, E1, E2, E3>(msta, cr); + } + +template<template<size_t> typename Layout> +constexpr bool +test_from_same() +{ + auto check = []<typename PaddedLayout>(PaddedLayout) + { + test_from_same_1d<PaddedLayout>(); + test_from_same_2d<PaddedLayout>(); + }; + + check(Layout<0>{}); + check(Layout<1>{}); + check(Layout<2>{}); + check(Layout<6>{}); + check(Layout<dyn>{}); + + // rank == 1 is more permissive: + test_from_same_1d<Layout<5>>(); + return true; +} + +template<template<size_t> typename Layout, typename E1_, typename E2_, + typename E3_> + constexpr bool + test_from_stride_nd(auto strides_) + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + using E1 = typename Traits::extents_type<E1_>; + using E2 = typename Traits::extents_type<E2_>; + using E3 = typename Traits::extents_type<E3_>; + auto strides = Traits::make_array(strides_); + + auto check = [&strides]<typename PaddedLayout>(PaddedLayout) + { + auto exts = E1{}; + constexpr auto cr = std::cw<ConversionRule::Never>; + + auto m = std::layout_stride::mapping(exts, strides); + check_convertible_variants<PaddedLayout, E1, E2, E3>(m, cr); + }; + + check(Layout<0>{}); + check(Layout<1>{}); + check(Layout<2>{}); + check(Layout<6>{}); + check(Layout<dyn>{}); + return true; + } + +template<template<size_t> typename Layout> + constexpr bool + test_from_stride_2d() + { + using E1 = std::extents<size_t, 6, 5>; + using E2 = std::dims<2>; + using E3 = std::extents<size_t, 6, 6>; + + auto strides = std::array<int, 2>{1, 6}; + test_from_stride_nd<Layout, E1, E2, E3>(strides); + return true; + } + +template<template<size_t> typename Layout> + constexpr bool + test_from_stride_3d() + { + using E1 = std::extents<size_t, 6, 5, 7>; + using E2 = std::dims<3>; + using E3 = std::extents<size_t, 6, 6, 7>; + + auto strides = std::array<int, 3>{1, 6, 6*5}; + test_from_stride_nd<Layout, E1, E2, E3>(strides); + return true; + } + +template<template<size_t> typename Layout> + constexpr bool + test_from_stride() + { + test_from_stride_2d<Layout>(); + test_from_stride_3d<Layout>(); + return true; + } + +template<template<size_t> typename Layout> + constexpr void + test_from_samepad_0d() + { + using E1 = std::extents<uint16_t>; + using E2 = std::extents<uint8_t>; + using E3 = std::extents<uint8_t, 1>; + + typename Layout<6>::mapping<E1> msta{E1{}}; + + auto check = []<typename To>(To, auto m) + { + constexpr auto cr = std::cw<ConversionRule::Regular>; + check_convertible_variants<To, E1, E2, E3>(m, cr); + }; + + check(Layout<6>{}, msta); + check(Layout<dyn>{}, msta); + } + +template<template<size_t> typename Layout> + constexpr void + test_from_samepad_1d() + { + using E1 = std::extents<int, 6>; + using E2 = std::extents<int, dyn>; + using E3 = std::extents<int, 6, 6>; + + typename Layout<6>::mapping<E1> msta{E1{}}; + typename Layout<dyn>::mapping<E1> mdyn{E1{}}; + + auto check = []<typename To>(To, auto m) + { + constexpr auto cr = std::cw<ConversionRule::Regular>; + check_convertible_variants<To, E1, E2, E3>(m, cr); + }; + + // Remember, for rank <= 1 the padding_value is irrelevant. + check(Layout<6>{}, msta); + check(Layout<6>{}, mdyn); + check(Layout<dyn>{}, msta); + check(Layout<dyn>{}, mdyn); + } + +template<template<size_t> typename Layout> + constexpr void + test_from_samepad_2d() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + using E1 = typename Traits::extents_type<std::extents<int, 6, 5>>; + using E2 = typename Traits::extents_type<std::extents<int, dyn, 5>>; + using E3 = typename Traits::extents_type<std::extents<int, 6, 6>>; + + typename Layout<6>::mapping<E1> msta{E1{}}; + typename Layout<dyn>::mapping<E1> mdyn{E1{}}; + + constexpr auto cregular = std::cw<ConversionRule::Regular>; + constexpr auto cnever = std::cw<ConversionRule::Never>; + + auto check = []<typename To>(To, auto m, auto cr) + { check_convertible_variants<To, E1, E2, E3>(m, cr); }; + + check(Layout<6>{}, msta, cnever); + check(Layout<6>{}, mdyn, cnever); + check(Layout<dyn>{}, msta, cregular); + check(Layout<dyn>{}, mdyn, cnever); + } + +template<template<size_t> typename Layout> + constexpr bool + test_from_samepad() + { + test_from_samepad_0d<Layout>(); + test_from_samepad_1d<Layout>(); + test_from_samepad_2d<Layout>(); + return true; + } + +template<template<size_t> typename Layout> + constexpr bool + test_from_other() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + using E1 = std::extents<size_t, 3>; + using E2 = std::dims<1>; + using E3 = std::extents<size_t, 5>; + + auto check = []<typename PaddedLayout>(PaddedLayout) + { + constexpr auto cr = std::cw<ConversionRule::Regular>; + + using layout_other = typename Traits::layout_other; + auto msta = typename layout_other::mapping(E1{}); + check_convertible_variants<PaddedLayout, E1, E2, E3>(msta, cr); + }; + + + // Remember, the padding_value has no effect for rank <= 1. + check(Layout<0>{}); + check(Layout<1>{}); + check(Layout<2>{}); + check(Layout<5>{}); + check(Layout<6>{}); + check(Layout<dyn>{}); + return true; + } + +template<template<size_t> typename Layout> + constexpr bool + test_to_same() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + using E1 = typename Traits::extents_type<std::extents<int, 6, 5>>; + using E2 = typename Traits::extents_type<std::extents<int, dyn, 5>>; + using E3 = typename Traits::extents_type<std::extents<int, 6, 6>>; + + auto check = [](auto msta) + { + constexpr auto cr = std::cw<ConversionRule::Regular>; + using LayoutSame = typename Traits::layout_same; + check_convertible_variants<LayoutSame, E1, E2, E3>(msta, cr); + }; + + check(typename Layout<0>::mapping(E1{})); + check(typename Layout<2>::mapping(E1{})); + check(typename Layout<6>::mapping(E1{})); + check(typename Layout<dyn>::mapping(E1{}, 0)); + check(typename Layout<dyn>::mapping(E1{}, 2)); + check(typename Layout<dyn>::mapping(E1{}, 6)); + return true; + } + +template<template<size_t> typename Layout> + constexpr bool + test_never_to_other() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + using E1 = std::extents<size_t, 3>; + using E2 = std::dims<1>; + + auto check = []<typename PaddedLayout>(PaddedLayout, auto exts) + { + using LayoutOther = typename Traits::layout_other; + auto mr = typename LayoutOther::mapping(exts); + auto mlp = typename PaddedLayout::mapping<decltype(exts)>{mr}; + static_assert(!std::is_constructible_v<decltype(mr), decltype(mlp)>); + }; + + check(Layout<2>{}, E1{}); + check(Layout<2>{}, E2{E1{}}); + return true; + } + +template<typename Layout> + constexpr void + test_strides() + { + auto check = [](auto exts) + { + auto m = typename Layout::mapping(exts); + using IndexType = typename decltype(m)::index_type; + constexpr size_t rank = decltype(m)::extents_type::rank(); + + auto strides = m.strides(); + static_assert(std::same_as<decltype(strides), + std::array<IndexType, rank>>); + VERIFY(strides.size() == rank); + for (size_t i = 0; i < strides.size(); ++i) + VERIFY(strides[i] == m.stride(i)); + }; + + check(std::extents()); + check(std::extents(0)); + check(std::extents(3)); + check(std::extents(3, 5, 7)); + check(std::extents(3, 0, 7)); + } + +template<template<size_t> typename Layout> + constexpr bool + test_strides_all() + { + test_strides<Layout<0>>(); + test_strides<Layout<1>>(); + test_strides<Layout<3>>(); + test_strides<Layout<dyn>>(); + return true; + } + +template<template<size_t> typename Layout> + constexpr void + test_exhaustive_0d() + { + auto exts = std::extents<int>{}; + + auto check = [](auto m) + { + static_assert(m.is_always_exhaustive()); + VERIFY(m.is_exhaustive()); + }; + + check(typename Layout<0>::mapping(exts)); + check(typename Layout<1>::mapping(exts)); + check(typename Layout<2>::mapping(exts)); + check(typename Layout<dyn>::mapping(exts)); + } + +template<template<size_t> typename Layout> +constexpr void + test_exhaustive_1d() + { + auto check_dyn_and_sta = []<typename PaddedLayout>(PaddedLayout) + { + auto check = [](auto exts) + { + auto m = typename PaddedLayout::mapping(exts); + static_assert(m.is_always_exhaustive()); + VERIFY(m.is_exhaustive()); + }; + + check(std::extents(4)); + check(std::extents<int, 4>{}); + }; + + check_dyn_and_sta(Layout<1>{}); + check_dyn_and_sta(Layout<2>{}); + check_dyn_and_sta(Layout<6>{}); + check_dyn_and_sta(Layout<dyn>{}); + } + +template<template<size_t> typename Layout> + constexpr void + test_exhaustive_3d() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + auto exts_dyn = Traits::make_extents(std::extents(4, 5, 7)); + auto exts_sta = Traits::make_extents(std::extents<int, 4, 5, 7>{}); + auto ctrue = std::cw<true>; + auto cfalse= std::cw<false>; + + auto check = [](auto m, auto static_expected, auto runtime_expected) + { + static_assert(m.is_always_exhaustive() == static_expected); + VERIFY(m.is_exhaustive() == runtime_expected); + }; + + check(typename Layout<0>::mapping(exts_sta), ctrue, true); + check(typename Layout<0>::mapping(exts_dyn), cfalse, true); + check(typename Layout<1>::mapping(exts_sta), ctrue, true); + check(typename Layout<1>::mapping(exts_dyn), cfalse, true); + check(typename Layout<2>::mapping(exts_sta), ctrue, true); + check(typename Layout<2>::mapping(exts_dyn), cfalse, true); + check(typename Layout<6>::mapping(exts_dyn), cfalse, false); + check(typename Layout<6>::mapping(exts_sta), cfalse, false); + check(typename Layout<dyn>::mapping(exts_sta), cfalse, true); + check(typename Layout<dyn>::mapping(exts_dyn, 2), cfalse, true); + check(typename Layout<dyn>::mapping(exts_dyn, 3), cfalse, false); + } + +template<template<size_t> typename Layout> + constexpr bool + test_exhaustive() + { + test_exhaustive_0d<Layout>(); + test_exhaustive_1d<Layout>(); + test_exhaustive_3d<Layout>(); + return true; + } + +template<template<size_t> typename Layout> + constexpr bool + test_op_eq() + { + // The generic cases are handled in layouts/mapping.cc. Here we check + // special cases related to non exhaustive layouts. + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + + auto exts_sta = Traits::make_extents(std::extents<size_t, 6, 5, 7>{}); + auto exts_dyn = std::dims<3>(exts_sta); + auto exts_other = Traits::make_extents(std::extents<size_t, 7, 5, 7>{}); + + auto m1 = typename Layout<0>::mapping(exts_sta); + auto m2 = typename Layout<7>::mapping(exts_sta); + + VERIFY(m1 == typename Layout<0>::mapping(exts_sta)); + VERIFY(m1 == typename Layout<1>::mapping(exts_sta)); + VERIFY(m1 == typename Layout<2>::mapping(exts_sta)); + VERIFY(m1 == typename Layout<6>::mapping(exts_sta)); + VERIFY(m1 != typename Layout<7>::mapping(exts_sta)); + VERIFY(m1 == typename Layout<dyn>::mapping(exts_sta)); + VERIFY(m1 != typename Layout<dyn>::mapping(exts_sta, 7)); + + VERIFY(m1 == typename Layout<0>::mapping(exts_dyn)); + VERIFY(m1 == typename Layout<1>::mapping(exts_dyn)); + VERIFY(m1 == typename Layout<2>::mapping(exts_dyn)); + VERIFY(m1 == typename Layout<6>::mapping(exts_dyn)); + VERIFY(m1 != typename Layout<7>::mapping(exts_dyn)); + VERIFY(m1 == typename Layout<dyn>::mapping(exts_dyn)); + VERIFY(m1 != typename Layout<dyn>::mapping(exts_dyn, 7)); + + VERIFY(m2 == typename Layout<7>::mapping(exts_sta)); + VERIFY(m2 == typename Layout<dyn>::mapping(exts_sta, 7)); + VERIFY(m2 == typename Layout<7>::mapping(exts_dyn)); + VERIFY(m2 == typename Layout<dyn>::mapping(exts_dyn, 7)); + + VERIFY(m2 != typename Layout<7>::mapping(exts_other)); + VERIFY(m2 != typename Layout<dyn>::mapping(exts_other, 7)); + return true; + } + +template<template<size_t> typename Layout> + constexpr bool + test_required_span_size_overflow() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + using Extents = std::dextents<uint8_t, 2>; + auto exts = Traits::make_extents(Extents{64, 2}); + auto strides = Traits::make_array(std::array<uint8_t, 2>{1, 128}); + auto ms = std::layout_stride::mapping(exts, strides); + auto m = typename Layout<dyn>::mapping<Extents>(ms); + VERIFY(is_same_mapping(m, ms)); + VERIFY(m.required_span_size() == ms.required_span_size()); + return true; + } + +template<template<size_t> typename Layout> + constexpr bool + test_all() + { + test_representable_padded_size<std::layout_left_padded>(); + test_default_ctor<Layout>(); + test_from_exts<Layout>(); + test_from_stride<Layout>(); + test_from_samepad<Layout>(); + test_from_same<Layout>(); + test_from_other<Layout>(); + test_to_same<Layout>(); + test_never_to_other<Layout>(); + test_strides_all<Layout>(); + test_exhaustive<Layout>(); + test_op_eq<Layout>(); + test_required_span_size_overflow<Layout>(); + return true; + } + +int +main() +{ + test_all<std::layout_left_padded>(); + static_assert(test_all<std::layout_left_padded>()); + + test_all<std::layout_right_padded>(); + static_assert(test_all<std::layout_right_padded>()); + + test_from_pad_all<std::layout_left_padded>(); + test_from_pad_all<std::layout_right_padded>(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc new file mode 100644 index 0000000..4073f68 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc @@ -0,0 +1,352 @@ +// { dg-do compile { target c++26 } } +#include <mdspan> + +#include "../layout_traits.h" +#include <cstdint> + +constexpr size_t dyn = std::dynamic_extent; + +template<template<size_t> typename Layout> + constexpr bool + test_from_extens_representable_sta() + { + using E1 = std::extents<uint8_t, 8, 128>; + auto m = typename Layout<dyn>::mapping(E1{}); // { dg-error "required from" } + return true; + } +static_assert(test_from_extens_representable_sta<std::layout_left_padded>()); // { dg-error "from here" } +static_assert(test_from_extens_representable_sta<std::layout_right_padded>()); // { dg-error "from here" } + +template<template<size_t> typename Layout> + constexpr bool + test_from_extents_representable_padded_size() + { + using E1 = std::extents<uint8_t, 8, 128>; + using E2 = std::dextents<uint8_t, 2>; + + auto m = typename Layout<dyn>::mapping(E2{E1{}}); // { dg-error "expansion of" } + (void) m; + return true; + } +static_assert(test_from_extents_representable_padded_size<std::layout_left_padded>()); // { dg-error "expansion of" } +static_assert(test_from_extents_representable_padded_size<std::layout_right_padded>()); // { dg-error "expansion of" } + +template<template<size_t> typename Layout> + constexpr bool + test_from_extents_representable_stride() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + using E1 = typename Traits::extents_type<std::extents<uint8_t, dyn, 1>>; + auto m = typename Layout<128>::mapping(E1{129}); // { dg-error "expansion of" } + (void) m; + return true; + } +static_assert(test_from_extents_representable_stride<std::layout_left_padded>()); // { dg-error "expansion of" } +static_assert(test_from_extents_representable_stride<std::layout_right_padded>()); // { dg-error "expansion of" } + +template<template<size_t> typename Layout> + constexpr bool + test_from_pad_representable_stride() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + auto exts = Traits::make_extents(std::dextents<uint8_t, 2>(129, 2)); + auto m = typename Layout<dyn>::mapping(exts, 128); // { dg-error "expansion of" } + return true; + } +static_assert(test_from_pad_representable_stride<std::layout_left_padded>()); // { dg-error "expansion of" } +static_assert(test_from_pad_representable_stride<std::layout_right_padded>()); // { dg-error "expansion of" } + +template<template<size_t> typename Layout> + constexpr bool + test_from_pad_representable_padded_size() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + auto exts = Traits::make_extents(std::dextents<uint8_t, 2>(64, 2)); + auto m = typename Layout<dyn>::mapping(exts, 128); // { dg-error "expansion of" } + return true; + } +static_assert(test_from_pad_representable_padded_size<std::layout_left_padded>()); // { dg-error "expansion of" } +static_assert(test_from_pad_representable_padded_size<std::layout_right_padded>()); // { dg-error "expansion of" } + +template<template<size_t> typename Layout> + constexpr bool + test_from_left() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + using LayoutSame = typename Traits::layout_same; + auto exts = Traits::make_extents(std::extents<uint8_t, 6, dyn>{4}); + auto ml = typename LayoutSame::mapping(exts); + + typename Layout<4>::mapping<decltype(exts)> m(ml); // { dg-error "expansion of" } + return true; + } +static_assert(test_from_left<std::layout_left_padded>()); // { dg-error "required from here" } +static_assert(test_from_left<std::layout_right_padded>()); // { dg-error "required from here" } + +template<template<size_t> typename Layout> + constexpr bool + test_from_left_bad_runtime_stride() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + auto exts = Traits::make_extents(std::extents<uint8_t, dyn, dyn>{6, 4}); + auto ml = typename Traits::layout_same::mapping(exts); + + typename Layout<4>::mapping<decltype(exts)> m(ml); // { dg-error "expansion of" } + (void) m; + return true; + } +static_assert(test_from_left_bad_runtime_stride<std::layout_left_padded>()); // { dg-error "expansion of" } +static_assert(test_from_left_bad_runtime_stride<std::layout_right_padded>()); // { dg-error "expansion of" } + +template<template<size_t> typename Layout> + constexpr bool + test_from_left_representable_extents() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + auto exts = Traits::make_extents(std::extents<uint16_t, dyn, dyn>{8, 128}); + auto ml = typename Traits::layout_same::mapping(exts); + + typename Layout<8>::mapping<std::extents<uint8_t, dyn, dyn>> m(ml); // { dg-error "expansion of" } + return true; + } +static_assert(test_from_left_representable_extents<std::layout_left_padded>()); // { dg-error "expansion of" } +static_assert(test_from_left_representable_extents<std::layout_right_padded>()); // { dg-error "expansion of" } + +template<template<size_t> typename Layout, size_t PaddingValue> + constexpr bool + test_pad_overflow() + { + auto exts = std::extents<uint8_t, dyn>{4}; + auto n = size_t(1) << 9; + auto m = typename Layout<PaddingValue>::mapping(exts, n); + (void) m; + return true; + } +static_assert(test_pad_overflow<std::layout_left_padded, 1>()); // { dg-error "expansion of" } +static_assert(test_pad_overflow<std::layout_left_padded, dyn>()); // { dg-error "expansion of" } +static_assert(test_pad_overflow<std::layout_right_padded, 1>()); // { dg-error "expansion of" } +static_assert(test_pad_overflow<std::layout_right_padded, dyn>()); // { dg-error "expansion of" } + +template<template<size_t> typename Layout, size_t PaddingValue> + constexpr bool + test_from_pad_negative() + { + auto exts = std::extents(4); + auto m = typename Layout<PaddingValue>::mapping(exts, -1); + (void) m; + return true; + } +static_assert(test_from_pad_negative<std::layout_left_padded, 1>()); // { dg-error "expansion of" } +static_assert(test_from_pad_negative<std::layout_left_padded, dyn>()); // { dg-error "expansion of" } +static_assert(test_from_pad_negative<std::layout_right_padded, 1>()); // { dg-error "expansion of" } +static_assert(test_from_pad_negative<std::layout_right_padded, dyn>()); // { dg-error "expansion of" } + +template<template<size_t> typename Layout, size_t Pad> + constexpr bool + test_static_pad_same() + { + using Extents = std::extents<int, dyn>; + using Mapping = typename Layout<Pad>::mapping<Extents>; + auto exts = Extents{4}; + auto m = Mapping(exts, Pad + 1); // { dg-error "expansion of" } + (void) m; + return true; + } +static_assert(test_static_pad_same<std::layout_left_padded, 1>()); // { dg-error "expansion of" } +static_assert(test_static_pad_same<std::layout_left_padded, 3>()); // { dg-error "expansion of" } +static_assert(test_static_pad_same<std::layout_right_padded, 1>()); // { dg-error "expansion of" } +static_assert(test_static_pad_same<std::layout_right_padded, 3>()); // { dg-error "expansion of" } + +template<template<size_t> typename Layout> + constexpr bool + test_from_stride_wrong_stride0() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + auto e = Traits::make_extents(std::extents{3, 5}); + auto s = Traits::make_array(std::array<int, 2>{2, 7}); + auto ms = std::layout_stride::mapping(e, s); + auto m = typename Layout<dyn>::mapping<decltype(e)>(ms); // { dg-error "expansion of" } + (void) m; + return true; + } +static_assert(test_from_stride_wrong_stride0<std::layout_left_padded>()); // { dg-error "expansion of" } +static_assert(test_from_stride_wrong_stride0<std::layout_right_padded>()); // { dg-error "expansion of" } + +template<template<size_t> typename Layout> + constexpr bool + test_from_stride_wrong_stride1() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + auto e = Traits::make_extents(std::extents(3, 5)); + auto s = Traits::make_array(std::array<int, 2>{1, 3}); + auto ms = std::layout_stride::mapping(e, s); + auto m = typename Layout<2>::mapping<decltype(e)>(ms); // { dg-error "expansion of" } + (void) m; + return true; + } +static_assert(test_from_stride_wrong_stride1<std::layout_left_padded>()); // { dg-error "expansion of" } +static_assert(test_from_stride_wrong_stride1<std::layout_right_padded>()); // { dg-error "expansion of" } + +template<template<size_t> typename Layout> + constexpr bool + test_from_stride_wrong_stride2() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + auto e = Traits::make_extents(std::extents(3, 5, 7)); + auto s = Traits::make_array(std::array<int, 3>{1, 4, 21}); + auto ms = std::layout_stride::mapping(e, s); + auto m = typename Layout<dyn>::mapping<decltype(e)>(ms); // here (not implemented) + (void) m; + return true; + } +static_assert(test_from_stride_wrong_stride2<std::layout_left_padded>()); +static_assert(test_from_stride_wrong_stride2<std::layout_right_padded>()); + +template<template<size_t> typename Layout> + constexpr bool + test_from_stride_oversized() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + auto exts = Traits::make_extents(std::extents<uint16_t, dyn, dyn>{3, 6}); + auto s = Traits::make_array(std::array<uint16_t, 2>{1, 128}); + auto ms = std::layout_stride::mapping(exts, s); + + using Mapping = typename Layout<dyn>::mapping<std::dextents<uint8_t, 2>>; + Mapping m(ms); // { dg-error "expansion of" } + (void) m; + return true; + } +static_assert(test_from_stride_oversized<std::layout_left_padded>()); // { dg-error "expansion of" } +static_assert(test_from_stride_oversized<std::layout_right_padded>()); // { dg-error "expansion of" } + +template<template<size_t> typename Layout> + constexpr bool + test_from_samepad_dyn() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + auto e = Traits::make_extents(std::extents(3, 5)); + auto mlp = typename Layout<dyn>::mapping(e); + auto m = typename Layout<2>::mapping<decltype(e)>(mlp); // { dg-error "expansion of" } + (void) m; + return true; + } +static_assert(test_from_samepad_dyn<std::layout_left_padded>()); // { dg-error "expansion of" } +static_assert(test_from_samepad_dyn<std::layout_right_padded>()); // { dg-error "expansion of" } + +template<template<size_t> typename Layout> + constexpr bool + test_from_samepad_sta() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + auto e = Traits::make_extents(std::extents{3, 5}); + auto mlp = typename Layout<3>::mapping(e); + auto m = typename Layout<2>::mapping<decltype(e)>(mlp); // { dg-error "expansion of" } + (void) m; + return true; + } +static_assert(test_from_samepad_sta<std::layout_left_padded>()); // { dg-error "expansion of" } +static_assert(test_from_samepad_sta<std::layout_right_padded>()); // { dg-error "expansion of" } + +template<template<size_t> typename Layout> + constexpr bool + test_from_samepad_oversized() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + using E1 = typename Traits::extents_type<std::extents<uint16_t, 8, 128>>; + using E2 = typename Traits::extents_type<std::extents<uint8_t, dyn, dyn>>; + auto mlp = typename Layout<dyn>::mapping<E1>(E1{}); + auto m = typename Layout<dyn>::mapping<E2>(mlp); // { dg-error "expansion of" } + (void) m; + return true; + } +static_assert(test_from_samepad_oversized<std::layout_left_padded>()); // { dg-error "expansion of" } +static_assert(test_from_samepad_oversized<std::layout_right_padded>()); // { dg-error "expansion of" } + +template<template<size_t> typename Layout, size_t RunId> + constexpr bool + test_to_same_not_exhaustive() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + using E1 = typename Traits::extents_type<std::extents<int, 6, 5>>; + using E2 = typename Traits::extents_type<std::extents<int, dyn, 5>>; + + [[maybe_unused]] auto msta = typename Layout<7>::mapping(E1{}); + if constexpr (RunId == 0) + { + auto m = typename Traits::layout_same::mapping<E1>(msta); // { dg-error "required from" } + (void) m; + } + if constexpr (RunId == 1) + { + auto m = typename Traits::layout_same::mapping<E2>(msta); // { dg-error "expansion of" } + (void) m; + } + + [[maybe_unused]] auto mdyn = typename Layout<dyn>::mapping(E2{E1{}}, 7); + if constexpr (RunId == 2) + { + auto m = typename Traits::layout_same::mapping<E1>(mdyn); // { dg-error "expansion of" } + (void) m; + } + if constexpr (RunId == 3) + { + auto m = typename Traits::layout_same::mapping<E2>(mdyn); // { dg-error "expansion of" } + (void) m; + } + return true; + } +static_assert(test_to_same_not_exhaustive<std::layout_left_padded, 0>()); // { dg-error "expansion of" } +static_assert(test_to_same_not_exhaustive<std::layout_left_padded, 1>()); // { dg-error "expansion of" } +static_assert(test_to_same_not_exhaustive<std::layout_left_padded, 2>()); // { dg-error "expansion of" } +static_assert(test_to_same_not_exhaustive<std::layout_left_padded, 3>()); // { dg-error "expansion of" } +static_assert(test_to_same_not_exhaustive<std::layout_right_padded, 0>()); // { dg-error "expansion of" } +static_assert(test_to_same_not_exhaustive<std::layout_right_padded, 1>()); // { dg-error "expansion of" } +static_assert(test_to_same_not_exhaustive<std::layout_right_padded, 2>()); // { dg-error "expansion of" } +static_assert(test_to_same_not_exhaustive<std::layout_right_padded, 3>()); // { dg-error "expansion of" } + +template<template<size_t> typename Layout> + constexpr bool + test_statically_bad_padding_value1() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + constexpr auto N = std::numeric_limits<size_t>::max() - 1; + using Extents = typename Traits::extents_type<std::extents<size_t, N, 0>>; + typename Layout<10>::mapping<Extents> m; // { dg-error "required from" } + return true; + } +static_assert(test_statically_bad_padding_value1<std::layout_left_padded>()); // { dg-error "required from" } +static_assert(test_statically_bad_padding_value1<std::layout_right_padded>()); // { dg-error "required from" } + +template<template<size_t> typename Layout> + constexpr bool + test_statically_bad_padding_value2() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + using Extents = typename Traits::extents_type<std::extents<uint8_t, 255, 0>>; + typename Layout<2>::mapping<Extents> m; // { dg-error "required from" } + return true; + } +static_assert(test_statically_bad_padding_value2<std::layout_left_padded>()); // { dg-error "required from" } +static_assert(test_statically_bad_padding_value2<std::layout_right_padded>()); // { dg-error "required from" } + +template<template<size_t> typename Layout> + constexpr bool + test_statically_oversized() + { + using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>; + using Extents = typename Traits::extents_type<std::extents<uint8_t, 127, 2>>; + typename Layout<2>::mapping<Extents> m; // { dg-error "required from" } + return true; + } +static_assert(test_statically_oversized<std::layout_left_padded>()); // { dg-error "from here" } +static_assert(test_statically_oversized<std::layout_right_padded>()); // { dg-error "from here" } + +// { dg-prune-output "padding_value must be representable as index_type" } +// { dg-prune-output "non-constant condition for static assertion" } +// { dg-prune-output "called in a constant expression" } +// { dg-prune-output "no matching function" } +// { dg-prune-output "static assertion failed" } +// { dg-prune-output "__glibcxx_assert_fail()" } +// { dg-prune-output "must be compatible with other.stride" } +// { dg-prune-output "padding_value is dynamic_extent" } +// { dg-prune-output "_S_rank <= 1" } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride.cc new file mode 100644 index 0000000..8d2fad2 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride.cc @@ -0,0 +1,513 @@ +// { dg-do run { target c++23 } } +#include <mdspan> + +#include "../int_like.h" +#include <testsuite_hooks.h> + +constexpr size_t dyn = std::dynamic_extent; + +template<typename MappingStride> + constexpr void + test_ctor_default_stride() + { + using Extents = typename MappingStride::extents_type; + MappingStride actual; + typename std::layout_right::mapping<Extents> expected; + + constexpr auto rank = MappingStride::extents_type::rank(); + if constexpr (rank > 0) + for(size_t i = 0; i < rank; ++i) + VERIFY(actual.stride(i) == expected.stride(i)); + } + +constexpr bool +test_ctor_default_stride_all() +{ + test_ctor_default_stride< + std::layout_stride::mapping<std::extents<int, 3>>>(); + + test_ctor_default_stride< + std::layout_stride::mapping<std::extents<int, 3, 5, 7>>>(); + + test_ctor_default_stride< + std::layout_stride::mapping<std::dextents<int, 3>>>(); + + test_ctor_default_stride< + std::layout_stride::mapping<std::extents<int, 0, 5, 7>>>(); + + test_ctor_default_stride< + std::layout_stride::mapping<std::extents<int, 3, dyn, dyn>>>(); + + test_ctor_default_stride< + std::layout_stride::mapping<std::extents<int, dyn, dyn, 3>>>(); + return true; +} + +template<typename E, typename E_arg, typename T, size_t N, bool Expected> +constexpr void +test_stride_constructible() +{ + static_assert(std::is_nothrow_constructible_v< + std::layout_stride::mapping<E>, E_arg, std::span<T, N>> == Expected); + static_assert(std::is_nothrow_constructible_v< + std::layout_stride::mapping<E>, E_arg, std::array<T, N>> == Expected); + static_assert(!std::is_constructible_v<std::layout_stride::mapping<E>, + E_arg>); +} + +constexpr void +test_stride_constructible_all() +{ + using E0 = std::extents<int>; + using E1 = std::extents<int, 2>; + using E2 = std::extents<int, dyn>; + + test_stride_constructible<E0, E0, int, 0, true>(); + test_stride_constructible<E0, E0, IntLike, 0, true>(); + test_stride_constructible<E0, E0, ThrowingInt, 0, false>(); + test_stride_constructible<E0, E0, MutatingInt, 0, false>(); + test_stride_constructible<E0, E0, NotIntLike, 0, false>(); + test_stride_constructible<E1, E1, int, 1, true>(); + test_stride_constructible<E2, E1, int, 1, true>(); + test_stride_constructible<E1, E1, int, 2, false>(); + test_stride_constructible<E1, E0, int, 1, false>(); +} + +template<typename Extents, typename Shape> + constexpr void + test_ctor_shape_strides(Extents exts, Shape strides) + { + using M = std::layout_stride::mapping<Extents>; + M m(exts, strides); + + if constexpr (Extents::rank() > 0) + for(size_t i = 0; i < exts.rank(); ++i) + { + VERIFY(m.stride(i) == strides[i]); + VERIFY(m.extents().extent(i) == exts.extent(i)); + } + } + +constexpr bool +test_ctor_shape_stride_all() +{ + test_ctor_shape_strides(std::extents<int>{}, std::array<int, 0>{}); + test_ctor_shape_strides(std::extents<int, 2>{}, std::array<int, 1>{3}); + test_ctor_shape_strides(std::extents<int, 2, 4, 6>{}, + std::array<int, 3>{20, 5, 45}); + return true; +} + +template<typename Extents, std::array<bool, 2> Strided, + std::array<bool, 2> Unique, std::array<bool, 2> Exhautive, + typename Extents::index_type Offset = 0> + struct MappingLike + { + using extents_type = Extents; + using index_type = typename Extents::index_type; + + constexpr + MappingLike(extents_type extents, + std::array<index_type, Extents::rank()> strides) + : _extents(extents), _strides(strides) + { } + + static constexpr bool + is_always_strided() requires (Strided[0]) + { return Strided[1]; } + + static constexpr bool + is_always_unique() requires (Unique[0]) + { return Unique[1]; } + + static constexpr bool + is_always_exhaustive() requires (Exhautive[0]) + { return Exhautive[1]; } + + constexpr Extents + extents() const { return _extents; } + + constexpr index_type + stride(size_t i) const { return _strides[i]; } + + template<typename... Indices> + constexpr index_type + operator()(Indices... indices) const + { + if (empty()) + VERIFY(false); + + std::array<index_type, Extents::rank()> ind_arr{indices...}; + index_type ret = Offset; + for(size_t i = 0; i < Extents::rank(); ++i) + ret += ind_arr[i]*_strides[i]; + return ret; + } + + private: + constexpr bool + empty() const + { + for (size_t i = 0; i < extents_type::rank(); ++i) + if (_extents.extent(i) == 0) + return true; + return false; + } + + Extents _extents; + std::array<index_type, Extents::rank()> _strides; + }; + + +template<size_t Rank> +struct ExtentLike +{ + using index_type = int; + + static constexpr size_t + rank() { return Rank; } +}; + + +template<typename E1> +constexpr void +test_mapping_like_constructible() +{ + using M = std::layout_stride::mapping<E1>; + using E2 = std::dextents<typename E1::index_type, E1::rank()>; + using E3 = std::dextents<typename E1::index_type, E1::rank() + 1>; + using E4 = ExtentLike<E1::rank()>; + + constexpr auto TT = std::array{true, true}; + constexpr auto FT = std::array{false, true}; + constexpr auto TF = std::array{true, false}; + + static_assert(std::is_constructible_v<M, MappingLike<E1, TT, TT, TT>>); + static_assert(std::is_constructible_v<M, MappingLike<E2, TT, TT, TT>>); + static_assert(!std::is_constructible_v<M, MappingLike<E3, TT, TT, TT>>); + static_assert(!std::is_constructible_v<M, MappingLike<E1, FT, TT, TT>>); + static_assert(!std::is_constructible_v<M, MappingLike<E1, TF, TT, TT>>); + static_assert(!std::is_constructible_v<M, MappingLike<E1, TT, FT, TT>>); + static_assert(!std::is_constructible_v<M, MappingLike<E1, TT, TF, TT>>); + static_assert(!std::is_constructible_v<M, MappingLike<E1, TT, TT, FT>>); + static_assert(std::is_constructible_v<M, MappingLike<E1, TT, TT, TF>>); + static_assert(!std::is_constructible_v<M, MappingLike<E4, TT, TT, TF>>); + static_assert(!std::is_constructible_v<M, MappingLike<E4, TT, TT, TT>>); +} + +constexpr void +test_mapping_like_constructible_all() +{ + test_mapping_like_constructible<std::extents<int>>(); + test_mapping_like_constructible<std::extents<int, 2>>(); + test_mapping_like_constructible<std::extents<int, 2, 3>>(); +} + +template<typename E1, typename E2> +constexpr void +test_mapping_like_convertible() +{ + using M1 = std::layout_stride::mapping<E1>; + using M2 = std::layout_stride::mapping<E2>; + constexpr auto TT = std::array{true, true}; + + static_assert(!std::is_convertible_v<MappingLike<E1, TT, TT, TT>, M1>); + static_assert(!std::is_convertible_v<MappingLike<E2, TT, TT, TT>, M1>); + static_assert(!std::is_convertible_v<MappingLike<E1, TT, TT, TT>, M2>); + + static_assert(std::is_convertible_v<std::layout_stride::mapping<E2>, M1>); + static_assert(std::is_convertible_v<std::layout_left::mapping<E2>, M1>); + static_assert(std::is_convertible_v<std::layout_right::mapping<E2>, M1>); + + static_assert(!std::is_convertible_v<std::layout_stride::mapping<E1>, M2>); + static_assert(!std::is_convertible_v<std::layout_left::mapping<E1>, M2>); + static_assert(!std::is_convertible_v<std::layout_right::mapping<E1>, M2>); +} + +constexpr void +test_mapping_like_convertible_all() +{ + test_mapping_like_convertible<std::extents<unsigned int>, + std::extents<int>>(); + test_mapping_like_convertible<std::extents<unsigned int, 2>, + std::extents<int, 2>>(); + test_mapping_like_convertible<std::extents<int, dyn, 3>, + std::extents<int, 2, 3>>(); +} + +template<typename Extents> +constexpr void +test_ctor_stride_like(Extents exts, std::array<int, Extents::rank()> strides) +{ + auto other_right = std::layout_right::mapping(exts); + auto other_left = std::layout_left::mapping(exts); + auto other_stride = std::layout_stride::mapping(exts, strides); + + VERIFY(std::layout_stride::mapping<Extents>(other_right) == other_right); + VERIFY(std::layout_stride::mapping<Extents>(other_left) == other_left); + VERIFY(std::layout_stride::mapping<Extents>(other_stride) == other_stride); +} + +constexpr void +test_ctor_stride_like_all() +{ + using E1 = std::extents<int>; + auto s1 = std::array<int, 0>{}; + test_ctor_stride_like(E1{}, s1); + + using E2 = std::extents<int, 3>; + auto s2 = std::array<int, 1>{2}; + test_ctor_stride_like(E2{}, s2); + + using E3 = std::extents<int, 3, 5, 7>; + auto s3 = std::array<int, 3>{5, 1, 15}; + test_ctor_stride_like(E3{}, s3); +} + +constexpr bool +test_ctor_strides_all() +{ + test_ctor_default_stride_all(); + test_ctor_shape_stride_all(); + test_ctor_stride_like_all(); + return true; +} + +// Check is_exhaustive. +template<typename Extents, typename Strides> + constexpr void + test_is_exhaustive(Extents extents, Strides strides, bool expected) + { + std::layout_stride::mapping<Extents> m(extents, strides); + VERIFY(m.is_exhaustive() == expected); + + bool always_exhaustive = extents.rank() == 0 || m.required_span_size() == 0; + VERIFY(m.is_always_exhaustive() == always_exhaustive); + } + +constexpr void +test_is_exhaustive_zero_1d() +{ + std::extents<int, 0> extents; + test_is_exhaustive(extents, std::array{1}, true); + test_is_exhaustive(extents, std::array{2}, true); +} + +constexpr void +test_is_exhaustive_zero_3d() +{ + std::extents<int, 3, 0, 7> extents; + + test_is_exhaustive(extents, std::array{1, 1, 1}, true); + test_is_exhaustive(extents, std::array{1, 2*21, 2*3}, true); + test_is_exhaustive(extents, std::array{7, 2*21, 1}, true); + test_is_exhaustive(extents, std::array{1, 21, 3}, true); + test_is_exhaustive(extents, std::array{7, 21, 1}, true); +} + +constexpr void +test_is_exhaustive_0d() +{ + std::extents<int> extents; + test_is_exhaustive(extents, std::array<int, 0>{}, true); +} + +constexpr void +test_is_exhaustive_1d() +{ + std::extents<int, 3> extents; + test_is_exhaustive(extents, std::array{1}, true); + test_is_exhaustive(extents, std::array{3}, false); +} + + +constexpr void +test_is_exhaustive_3d() +{ + std::extents<int, 3, dyn, 7> extents(5); + + test_is_exhaustive(extents, std::array{1, 3, 3*5}, true); + test_is_exhaustive(extents, std::array{5*7, 1, 5}, true); + test_is_exhaustive(extents, std::array{7, 3*7, 1}, true); + + test_is_exhaustive(extents, std::array{1, 3, 2*3*5}, false); + test_is_exhaustive(extents, std::array{2*5*7, 1, 2*5}, false); + test_is_exhaustive(extents, std::array{2*7, 2*3*7, 2}, false); +} + +constexpr void +test_is_exhaustive_ones() +{ + std::extents<int, 1, 1, 3, 1> extents; + test_is_exhaustive(extents, std::array{1, 1, 1, 1}, true); + test_is_exhaustive(extents, std::array{1, 1, 1, 3}, true); + test_is_exhaustive(extents, std::array{3, 3, 1, 3}, true); + test_is_exhaustive(extents, std::array{3, 1, 1, 3}, true); +} + +constexpr bool +test_is_exhaustive_all() +{ + test_is_exhaustive_zero_1d(); + test_is_exhaustive_zero_3d(); + test_is_exhaustive_ones(); + test_is_exhaustive_0d(); + test_is_exhaustive_1d(); + test_is_exhaustive_3d(); + return true; +} + +template<typename Extents, int Offset> + using OffsetMapping = MappingLike<Extents, {true, true}, {true, true}, + {true, false}, Offset>; + +template<typename Extents> + constexpr void + test_eq(Extents exts, + std::array<typename Extents::index_type, Extents::rank()> left_strides, + std::array<typename Extents::index_type, Extents::rank()> right_strides, + std::array<typename Extents::index_type, Extents::rank()> padded_strides) + { + using DExtents = std::dextents<int, Extents::rank()>; + + std::layout_left::mapping<Extents> ml; + std::layout_right::mapping<DExtents> mr(exts); + + std::layout_stride::mapping<Extents> msd; + std::layout_stride::mapping<Extents> msl(exts, left_strides); + std::layout_stride::mapping<Extents> msr(exts, right_strides); + std::layout_stride::mapping<Extents> msp(exts, padded_strides); + + OffsetMapping<Extents, 0> mor{exts, right_strides}; + OffsetMapping<Extents, 0> mol{exts, left_strides}; + OffsetMapping<Extents, 0> mop{exts, padded_strides}; + OffsetMapping<Extents, 1> moo{exts, right_strides}; + + VERIFY(msd == mr); + VERIFY(msd == mor); + VERIFY(msd != msp); + VERIFY(msd != mop); + + VERIFY(msl == ml); + VERIFY(msl == mol); + VERIFY(msd != msp); + VERIFY(msl != mop); + + VERIFY(msp == mop); + VERIFY(msp != ml); + VERIFY(msp != mr); + + VERIFY(msd != moo); + } + +constexpr void +test_eq_0d() +{ + using Extents = std::extents<int>; + Extents exts; + std::layout_left::mapping<Extents> ml; + std::layout_right::mapping<Extents> mr; + std::layout_stride::mapping<Extents> ms; + OffsetMapping<Extents, 0> mor{exts, {}}; + OffsetMapping<Extents, 1> moo{exts, {}}; + + VERIFY(ms == ml); + VERIFY(ms == mr); + VERIFY(ms == mor); + VERIFY(ms != moo); +} + +constexpr void +test_eq_1d() +{ + using Extents = std::extents<int, 2>; + auto exhaustive_strides = std::array{1}; + auto padded_strides = std::array{2}; + + test_eq(Extents{}, exhaustive_strides, exhaustive_strides, padded_strides); +} + +constexpr void +test_eq_2d() +{ + using Extents = std::extents<int, 1, 2>; + auto left_strides = std::array{1, 1}; + auto right_strides = std::array{2, 1}; + auto padded_strides = std::array{2, 8}; + + test_eq(Extents{}, left_strides, right_strides, padded_strides); +} + +constexpr void +test_eq_zero() +{ + using Extents = std::extents<int, 0, 2>; + using Mapping = std::layout_stride::mapping<Extents>; + + Extents exts; + std::array<int, 2> sl{1, 5}; + std::array<int, 2> sr{5, 1}; + + Mapping m1(exts, sl); + Mapping m2(exts, sl); + Mapping m3(exts, sr); + OffsetMapping<Extents, 0> m4(exts, sl); + + VERIFY(m1 == m2); + VERIFY(m1 != m3); + VERIFY(m1 == m4); + +} + +constexpr bool +test_eq_all() +{ + test_eq_0d(); + test_eq_1d(); + test_eq_2d(); + test_eq_zero(); + return true; +} + +template<typename M1, typename M2> + concept has_op_eq = requires (M1 m1, M2 m2) + { + { m1 == m2 } -> std::same_as<bool>; + { m2 == m1 } -> std::same_as<bool>; + { m1 != m2 } -> std::same_as<bool>; + { m2 != m1 } -> std::same_as<bool>; + }; + +constexpr void +test_has_op_eq() +{ + using E1 = std::extents<int>; + using E2 = std::extents<int, 2>; + using E3 = std::extents<int, 1, 2>; + constexpr auto FT = std::array{false, true}; + + static_assert(!has_op_eq< + std::layout_stride::mapping<E1>, MappingLike<E1, FT, FT, FT>>); + + static_assert(!has_op_eq< + std::layout_stride::mapping<E2>, MappingLike<E2, FT, FT, FT>>); + + static_assert(!has_op_eq< + std::layout_stride::mapping<E3>, MappingLike<E3, FT, FT, FT>>); +} + +int +main() +{ + test_ctor_strides_all(); + static_assert(test_ctor_strides_all()); + test_mapping_like_convertible_all(); + test_mapping_like_constructible_all(); + test_stride_constructible_all(); + test_is_exhaustive_all(); + static_assert(test_is_exhaustive_all()); + test_eq_all(); + static_assert(test_eq_all()); + test_has_op_eq(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc b/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc new file mode 100644 index 0000000..a92a055 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/mdspan.cc @@ -0,0 +1,779 @@ +// { dg-do run { target c++23 } } +#include <mdspan> + +#include <testsuite_hooks.h> +#include "int_like.h" +#include "layout_like.h" +#include <stdexcept> + +constexpr auto dyn = std::dynamic_extent; + +template<typename MDSpan, typename T, typename E, typename L = std::layout_right, + typename A = std::default_accessor<T>> + constexpr void + assert_typedefs() + { + static_assert(std::same_as<typename MDSpan::extents_type, E>); + static_assert(std::same_as<typename MDSpan::layout_type, L>); + static_assert(std::same_as<typename MDSpan::accessor_type, A>); + static_assert(std::same_as<typename MDSpan::mapping_type, + typename L::mapping<E>>); + static_assert(std::same_as<typename MDSpan::element_type, T>); + static_assert(std::same_as<typename MDSpan::value_type, + std::remove_const_t<T>>); + static_assert(std::same_as<typename MDSpan::index_type, + typename E::index_type>); + static_assert(std::same_as<typename MDSpan::size_type, + typename E::size_type>); + static_assert(std::same_as<typename MDSpan::rank_type, + typename E::rank_type>); + static_assert(std::same_as<typename MDSpan::data_handle_type, + typename A::data_handle_type>); + static_assert(std::same_as<typename MDSpan::reference, + typename A::reference>); + } + +template<typename T, typename E, typename L, template<typename U> typename A> + constexpr void + test_typedefs() + { assert_typedefs<std::mdspan<T, E, L, A<T>>, T, E, L, A<T>>(); } + +constexpr void +test_typedefs_all() +{ + using E = std::extents<int, 1, 2>; + using L = std::layout_left; + + test_typedefs<double, E, L, std::default_accessor>(); + test_typedefs<const double, E, L, std::default_accessor>(); +} + +template<typename MDSpan> + constexpr void + test_rank() + { + using Extents = typename MDSpan::extents_type; + static_assert(MDSpan::rank() == Extents::rank()); + static_assert(MDSpan::rank_dynamic() == Extents::rank_dynamic()); + } + +constexpr bool +test_rank_all() +{ + test_rank<std::mdspan<double, std::extents<int>>>(); + test_rank<std::mdspan<double, std::extents<int, 1>>>(); + test_rank<std::mdspan<double, std::extents<int, dyn>>>(); + return true; +} + +template<typename Extents> + constexpr void + test_extent(Extents exts) + { + double data = 1.0; + auto md = std::mdspan(&data, exts); + using MDSpan = decltype(md); + + for(size_t i = 0; i < MDSpan::rank(); ++i) + { + VERIFY(MDSpan::static_extent(i) == Extents::static_extent(i)); + VERIFY(md.extent(i) == exts.extent(i)); + } + } + +constexpr bool +test_extent_all() +{ + // For rank == 0, check existence of the methods without calling them. + test_extent(std::extents<int>{}); + test_extent(std::extents<int, 0>{}); + test_extent(std::extents<int, dyn>{}); + return true; +} + +template<typename MDSpan> + constexpr void + test_class_properties() + { + static_assert(std::copyable<MDSpan>); + static_assert(std::is_nothrow_move_constructible_v<MDSpan>); + static_assert(std::is_nothrow_move_assignable_v<MDSpan>); + static_assert(std::is_nothrow_swappable_v<MDSpan>); + constexpr bool trivially_copyable = + std::is_trivially_copyable_v<typename MDSpan::accessor_type> + && std::is_trivially_copyable_v<typename MDSpan::mapping_type> + && std::is_trivially_copyable_v<typename MDSpan::data_handle_type>; + static_assert(std::is_trivially_copyable_v<MDSpan> == trivially_copyable); + } + +constexpr bool +test_class_properties_all() +{ + test_class_properties<std::mdspan<double, std::extents<int>>>(); + test_class_properties<std::mdspan<double, std::extents<int, 1>>>(); + test_class_properties<std::mdspan<double, std::extents<int, dyn>>>(); + return true; +} + +template<typename T> + class ThrowingDefaultAccessor + { + public: + using element_type = T; + using reference = element_type&; + using data_handle_type = element_type*; + using offset_policy = ThrowingDefaultAccessor; + + ThrowingDefaultAccessor() noexcept(false) + { } + + reference + access(data_handle_type p, size_t i) const + { return p[i]; } + + typename offset_policy::data_handle_type + offset(data_handle_type p, size_t i) const + { return p + i; } + }; + +constexpr bool +test_default_ctor() +{ + static_assert(!std::is_default_constructible_v<std::mdspan<double, + std::extents<int>>>); + static_assert(!std::is_default_constructible_v<std::mdspan<double, + std::extents<int, 1>>>); + static_assert(std::is_default_constructible_v<std::mdspan<double, + std::extents<int, dyn>>>); + + std::mdspan<double, std::extents<int, dyn>> md; + VERIFY(md.data_handle() == nullptr); + VERIFY(md.empty()); + return true; +} + +template<template<typename T> typename Accessor, bool Expected> + constexpr void + test_nothrow_default_ctor() + { + using Extents = std::extents<int, dyn>; + using Layout = std::layout_left; + using MDSpan = std::mdspan<double, Extents, Layout, Accessor<double>>; + + static_assert(std::is_default_constructible_v<MDSpan>); + static_assert(std::is_nothrow_default_constructible_v<MDSpan> == Expected); + } + +constexpr bool +test_from_other() +{ + using Extents = std::extents<int, 3, 5, 7>; + auto exts = Extents{}; + + auto mapping = std::layout_right::mapping(exts); + constexpr size_t n = mapping.required_span_size(); + std::array<double, n> storage{}; + + auto md1 = std::mdspan(storage.data(), exts); + auto md2 = std::mdspan<double, std::dextents<int, 3>>(md1); + + VERIFY(md1.data_handle() == md2.data_handle()); + VERIFY(md1.size() == md2.size()); + + static_assert(!std::is_convertible_v< + std::mdspan<double, std::extents<unsigned int, 2>>, + std::mdspan<double, std::extents<int, 2>>>); + + static_assert(std::is_convertible_v< + std::mdspan<double, std::extents<int, 2>>, + std::mdspan<const double, std::extents<int, 2>>>); + + static_assert(!std::is_constructible_v< + std::mdspan<double, std::extents<int, 2>>, + std::mdspan<const double, std::extents<int, 2>>>); + + return true; +} + +template<typename T, typename E, typename L = std::layout_right, + typename A = std::default_accessor<T>> + constexpr void + assert_deduced_typedefs(auto md) + { assert_typedefs<decltype(md), T, E, L, A>(); } + +constexpr bool +test_from_carray() +{ + constexpr size_t n = 5; + double data[n] = {1.1, 2.2, 3.3, 4.4, 5.5}; + + auto md = std::mdspan(data); + assert_deduced_typedefs<double, std::extents<size_t, n>>(md); + VERIFY(md.rank() == 1); + VERIFY(md.rank_dynamic() == 0); + VERIFY(md[2] == data[2]); + return true; +} + +constexpr bool +test_from_pointer() +{ + double value = 12.3; + auto md = std::mdspan(&value); + assert_deduced_typedefs<double, std::extents<size_t>>(md); + VERIFY(md.rank() == 0); + VERIFY(md.rank_dynamic() == 0); + VERIFY(md[] == value); + return true; +} + +constexpr bool +test_from_pointer_and_shape() +{ + constexpr size_t n = 6; + std::array<double, n> data{1.1, 2.2, 3.3, 4.4, 5.5, 6.6}; + std::array<int, 2> shape{2, 3}; + std::span<const int, 2> shape_view(shape); + + auto verify = [&data](auto md) + { + assert_deduced_typedefs<double, std::dextents<size_t, 2>>(md); + VERIFY(md.rank() == 2); + VERIFY(md.rank_dynamic() == 2); + VERIFY(md[0, 0] == data[0]); + VERIFY(md[0, 1] == data[1]); + VERIFY(md[1, 0] == data[3]); + }; + + verify(std::mdspan(data.data(), shape[0], shape[1])); + verify(std::mdspan(data.data(), shape)); + verify(std::mdspan(data.data(), shape_view)); + + std::mdspan<double, std::dextents<size_t, 2>> md1 = {data.data(), shape}; + verify(md1); + + std::mdspan<double, std::dextents<size_t, 2>> md2 = {data.data(), shape_view}; + verify(md2); + + static_assert(std::is_constructible_v< + std::mdspan<float, std::extents<int, 3, 5>>, float*>); + static_assert(!std::is_constructible_v< + std::mdspan<float, std::extents<int, 3, 5>>, float*, int>); + static_assert(std::is_constructible_v< + std::mdspan<float, std::extents<int, 3, 5>>, float*, int, int>); + static_assert(std::is_constructible_v< + std::mdspan<float, std::extents<int, 3, 5>>, float*, std::span<int, 0>>); + static_assert(std::is_constructible_v< + std::mdspan<float, std::extents<int, 3, 5>>, float*, std::span<int, 2>>); + static_assert(!std::is_convertible_v< + float*, std::mdspan<float, std::extents<int, 3, 5>>>); + + static_assert(std::is_constructible_v< + std::mdspan<float, std::dextents<int, 2>>, float*, std::span<int, 2>>); + static_assert(!std::is_constructible_v< + std::mdspan<float, std::dextents<int, 2>>, float*, std::span<int, 1>>); + static_assert(!std::is_constructible_v< + std::mdspan<float, std::dextents<int, 2>>, float*, std::span<int, 3>>); + static_assert(!std::is_constructible_v< + std::mdspan<float, std::dextents<int, 2>>, float*, std::span<int, dyn>>); + return true; +} + +constexpr bool +test_from_pointer_and_constant() +{ + std::array<double, 6> buffer{}; + double * ptr = buffer.data(); + + auto verify = [ptr](auto actual, auto exts) + { + auto expected = std::mdspan<double, decltype(exts)>(ptr, exts); + static_assert(std::same_as<decltype(actual), decltype(expected)>); + VERIFY(actual.extents() == expected.extents()); + }; + + auto i3 = std::integral_constant<int, 3>{}; + auto i6 = std::integral_constant<int, 6>{}; + + verify(std::mdspan(ptr, 6), std::extents(6)); + verify(std::mdspan(ptr, i6), std::extents(i6)); + verify(std::mdspan(ptr, 2, i3), std::extents(2, i3)); + verify(std::mdspan(ptr, std::true_type{}, i3), std::extents(1, i3)); + +#if __glibcxx_constant_wrapper + auto c3 = std::constant_wrapper<3>{}; + verify(std::mdspan(ptr, 2, c3), std::extents(2, i3)); + verify(std::mdspan(ptr, 2, std::cw<3>), std::extents(2, i3)); + verify(std::mdspan(ptr, std::cw<true>, std::cw<3>), std::extents(1, i3)); +#endif + return true; +} + +constexpr bool +test_from_extents() +{ + constexpr size_t n = 3*5*7; + std::array<double, n> storage{}; + using Extents = std::extents<int, 3, 5, 7>; + auto exts = Extents{}; + auto md = std::mdspan(storage.data(), exts); + + assert_deduced_typedefs<double, Extents>(md); + VERIFY(md.data_handle() == storage.data()); + VERIFY(md.extents() == exts); + return true; +} + +constexpr bool +test_from_mapping() +{ + constexpr size_t n = 3*5*7; + std::array<double, n> storage{}; + using Extents = std::extents<int, 3, 5, 7>; + + auto exts = Extents{}; + auto m = std::layout_left::mapping(exts); + auto md = std::mdspan(storage.data(), m); + + assert_deduced_typedefs<double, Extents, std::layout_left>(md); + VERIFY(md.data_handle() == storage.data()); + VERIFY(md.mapping() == m); + return true; +} + +constexpr bool +test_from_accessor() +{ + constexpr size_t n = 3*5*7; + std::array<double, n> storage{}; + using Extents = std::extents<int, 3, 5, 7>; + + auto exts = Extents{}; + auto m = std::layout_left::mapping(exts); + auto a = std::default_accessor<double>{}; + auto md = std::mdspan(storage.data(), m, a); + + assert_deduced_typedefs<double, Extents, std::layout_left>(md); + VERIFY(md.data_handle() == storage.data()); + VERIFY(md.mapping() == m); + return true; +} + +template<typename MDSpan, typename Pointer, typename... Ints> + concept has_pack_ctor = requires + { + { MDSpan(Pointer{}, Ints(0)...) } -> std::same_as<MDSpan>; + }; + +template<typename CustomInt, bool ValidForPacks, bool ValidForArrays> + constexpr bool + test_from_int_like() + { + constexpr size_t n = 3*5*7; + std::array<double, n> storage{}; + + auto verify = [&](auto md) + { + VERIFY(md.data_handle() == storage.data()); + VERIFY(md.extent(0) == 3); + VERIFY(md.extent(1) == 5); + VERIFY(md.extent(2) == 7); + }; + + static_assert(has_pack_ctor<std::mdspan<float, std::dextents<int, 3>>, + float*, CustomInt, int, CustomInt> == ValidForPacks); + + static_assert(std::is_constructible_v< + std::mdspan<float, std::dextents<int, 3>>, float*, + std::span<CustomInt, 3>> == ValidForArrays); + + static_assert(std::is_constructible_v< + std::mdspan<float, std::dextents<int, 3>>, float*, + std::array<CustomInt, 3>> == ValidForArrays); + + if constexpr (ValidForPacks) + verify(std::mdspan(storage.data(), CustomInt(3), 5, CustomInt(7))); + + if constexpr (ValidForArrays) + { + auto shape = std::array{CustomInt(3), CustomInt(5), CustomInt(7)}; + auto shape_view = std::span<CustomInt, 3>{shape}; + verify(std::mdspan(storage.data(), shape)); + verify(std::mdspan(storage.data(), shape_view)); + } + return true; + } + +template<typename T, bool NothrowConstructible = true, + bool NothrowAssignable = true> + class OpaqueAccessor + { + struct Handle + { + constexpr + Handle(T * other) + : ptr(other) + { } + + constexpr + Handle(const Handle&) noexcept(NothrowConstructible) = default; + + constexpr + Handle(Handle&&) noexcept(NothrowConstructible) = default; + + constexpr Handle& + operator=(const Handle&) noexcept(NothrowAssignable) = default; + + constexpr Handle& + operator=(Handle&&) noexcept(NothrowAssignable) = default; + + T * ptr; + }; + + public: + using element_type = T; + using reference = T&; + using data_handle_type = Handle; + using offset_policy = OpaqueAccessor; + + reference + access(data_handle_type p, size_t i) const + { + ++access_count; + return p.ptr[i]; + } + + typename offset_policy::data_handle_type + offset(data_handle_type p, size_t i) const + { + ++offset_count; + return typename offset_policy::data_handle_type{(void*)(p.ptr + i)}; + } + + mutable size_t access_count = 0; + mutable size_t offset_count = 0; + }; + +void +test_from_opaque_accessor() +{ + constexpr size_t n = 3*5*7; + std::array<double, n> storage{}; + using Extents = std::extents<int, 3, 5, 7>; + + auto exts = Extents{}; + auto m = std::layout_left::mapping(exts); + auto a = OpaqueAccessor<double>{}; + auto handle = OpaqueAccessor<double>::data_handle_type{storage.data()}; + auto md = std::mdspan(handle, m, a); + + using MDSpan = decltype(md); + static_assert(std::same_as<MDSpan::accessor_type, decltype(a)>); + + VERIFY(md[0, 0, 0] == 0.0); + VERIFY(md.accessor().access_count == 1); + + VERIFY(md[2, 4, 6] == 0.0); + VERIFY(md.accessor().access_count == 2); +} + +template<typename T, typename Base> + class BaseClassAccessor + { + public: + using element_type = T; + using reference = Base&; + using data_handle_type = T*; + using offset_policy = BaseClassAccessor; + + static_assert(std::common_reference_with<reference&&, element_type&>); + + reference + access(data_handle_type p, size_t i) const + { return p[i]; } + + typename offset_policy::data_handle_type + offset(data_handle_type p, size_t i) const + { return typename offset_policy::data_handle_type{p + i}; } + }; + +struct Base +{ + double value = 1.0; +}; + +struct Derived : Base +{ + double value = 2.0; +}; + +void +test_from_base_class_accessor() +{ + constexpr size_t n = 3*5*7; + std::array<Derived, n> storage{}; + using Extents = std::extents<int, 3, 5, 7>; + + auto exts = Extents{}; + auto m = std::layout_left::mapping(exts); + auto a = BaseClassAccessor<Derived, Base>{}; + auto md = std::mdspan(storage.data(), m, a); + + using MDSpan = decltype(md); + static_assert(std::same_as<MDSpan::accessor_type, decltype(a)>); + static_assert(std::same_as<decltype(md[0, 0, 0]), Base&>); + VERIFY(md[0, 0, 0].value == 1.0); + VERIFY(md[2, 4, 6].value == 1.0); +} + +constexpr bool +test_from_mapping_like() +{ + double data = 1.1; + auto m = LayoutLike::mapping<std::extents<int, 1, 2, 3>>{}; + auto md = std::mdspan(&data, m); + VERIFY(md[0, 0, 0] == data); + VERIFY(md[0, 1, 2] == data); + return true; +} + +template<typename MDSpan> + constexpr void + test_empty(MDSpan md) + { + VERIFY(md.empty() == (md.size() == 0)); + } + +constexpr bool +test_empty_all() +{ + test_empty(std::mdspan<double, std::extents<int, dyn>>{}); + return true; +} + +template<typename MDSpan, typename... Args> +concept indexable = requires (MDSpan md, Args... args) +{ + { md[args...] } -> std::same_as<typename MDSpan::reference>; +}; + +template<typename Int, bool ValidForPacks, bool ValidForArrays> + constexpr bool + test_access() + { + using Extents = std::extents<int, 3, 5, 7>; + auto exts = Extents{}; + + auto mapping = std::layout_left::mapping(exts); + constexpr size_t n = mapping.required_span_size(); + std::array<double, n> storage{}; + + auto md = std::mdspan(storage.data(), mapping); + using MDSpan = decltype(md); + + for(int i = 0; i < exts.extent(0); ++i) + for(int j = 0; j < exts.extent(1); ++j) + for(int k = 0; k < exts.extent(2); ++k) + { + storage[mapping(i, j, k)] = 1.0; + if constexpr (ValidForPacks) + VERIFY(md[Int(i), Int(j), Int(k)] == 1.0); + + if constexpr (ValidForArrays) + { + std::array<Int, 3> ijk{Int(i), Int(j), Int(k)}; + VERIFY(md[ijk] == 1.0); + VERIFY(md[std::span(ijk)] == 1.0); + } + storage[mapping(i, j, k)] = 0.0; + } + + if constexpr (!ValidForPacks) + static_assert(!indexable<MDSpan, Int, int, Int>); + + if constexpr (!ValidForArrays) + { + static_assert(!indexable<MDSpan, std::array<Int, 3>>); + static_assert(!indexable<MDSpan, std::span<Int, 3>>); + } + return true; + } + +constexpr bool +test_swap() +{ + using Extents = std::dextents<int, 2>; + auto e1 = Extents{3, 5}; + auto e2 = Extents{7, 11}; + + std::array<double, 3*5> s1{}; + std::array<double, 7*11> s2{}; + + auto md1 = std::mdspan(s1.data(), e1); + auto md2 = std::mdspan(s2.data(), e2); + + std::swap(md1, md2); + + VERIFY(md1.data_handle() == s2.data()); + VERIFY(md2.data_handle() == s1.data()); + + VERIFY(md1.size() == s2.size()); + VERIFY(md2.size() == s1.size()); + return true; +} + +namespace adl +{ + template<typename T> + struct SwappableAccessor + { + using element_type = T; + using reference = T&; + using data_handle_type = T*; + using offset_policy = SwappableAccessor; + + reference + access(data_handle_type p, size_t i) const + { return p[i]; } + + typename offset_policy::data_handle_type + offset(data_handle_type p, size_t i) const + { return p + i; } + + friend void + swap(SwappableAccessor&, SwappableAccessor&) + { ++swap_count; } + + static inline size_t swap_count = 0; + }; +} + +void +test_swap_adl() +{ + using Extents = std::extents<int, dyn>; + using Layout = std::layout_left; + using Accessor = adl::SwappableAccessor<double>; + Accessor::swap_count = 0; + + std::mdspan<double, Extents, Layout, Accessor> m1, m2; + swap(m1, m2); + VERIFY(Accessor::swap_count == 1); +} + +template<bool Constructible, bool Assignable> +constexpr void +test_nothrow_movable() +{ + using Layout = std::layout_left; + using Extents = std::dextents<int, 3>; + using Accessor = OpaqueAccessor<int, Constructible, Assignable>; + using Handle = Accessor::data_handle_type; + static_assert(std::is_nothrow_move_assignable_v<Accessor>); + static_assert(std::is_nothrow_move_constructible_v<Accessor>); + static_assert(std::is_nothrow_move_assignable_v<Handle> == Assignable); + static_assert(std::is_nothrow_move_constructible_v<Handle> == Constructible); + + using MDSpan = std::mdspan<int, Extents, Layout, Accessor>; + static_assert(std::is_nothrow_move_assignable_v<MDSpan> == Assignable); + static_assert(std::is_nothrow_move_constructible_v<MDSpan> == Constructible); +} + +constexpr void +test_nothrow_movable_all() +{ + using MDSpan = std::mdspan<double, std::dextents<int, 3>>; + static_assert(std::is_nothrow_move_assignable_v<MDSpan>); + static_assert(std::is_nothrow_move_constructible_v<MDSpan>); + + test_nothrow_movable<true, true>(); + test_nothrow_movable<true, false>(); + test_nothrow_movable<false, true>(); + test_nothrow_movable<false, false>(); +} + +template<typename Layout, bool Expected> + constexpr void + test_nothrow_is_methods() + { + using Extents = std::extents<int, dyn>; + using MDSpan = std::mdspan<double, Extents, Layout>; + static_assert(noexcept(MDSpan::is_always_unique()) == Expected); + static_assert(noexcept(MDSpan::is_always_exhaustive()) == Expected); + static_assert(noexcept(MDSpan::is_always_strided()) == Expected); + + static_assert(noexcept(std::declval<MDSpan>().is_unique()) == Expected); + static_assert(noexcept(std::declval<MDSpan>().is_exhaustive()) == Expected); + static_assert(noexcept(std::declval<MDSpan>().is_strided()) == Expected); + } + +int +main() +{ + test_typedefs_all(); + + test_rank_all(); + test_extent_all(); + static_assert(test_extent_all()); + + test_class_properties_all(); + static_assert(test_class_properties_all()); + + test_empty_all(); + static_assert(test_empty_all()); + + test_default_ctor(); + static_assert(test_default_ctor()); + + test_nothrow_default_ctor<std::default_accessor, true>(); + test_nothrow_default_ctor<ThrowingDefaultAccessor, false>(); + + test_from_other(); + static_assert(test_from_other()); + + test_from_carray(); + static_assert(test_from_carray()); + + test_from_pointer_and_shape(); + static_assert(test_from_pointer_and_shape()); + + test_from_pointer_and_constant(); + static_assert(test_from_pointer_and_constant()); + + test_from_extents(); + static_assert(test_from_extents()); + + test_from_mapping(); + static_assert(test_from_mapping()); + + test_from_accessor(); + static_assert(test_from_accessor()); + + test_from_int_like<int, true, true>(); + static_assert(test_from_int_like<int, true, true>()); + test_from_int_like<IntLike, true, true>(); + test_from_int_like<ThrowingInt, false, false>(); + test_from_int_like<MutatingInt, true, false>(); + test_from_int_like<RValueInt, true, false>(); + + test_from_opaque_accessor(); + test_from_base_class_accessor(); + test_from_mapping_like(); + static_assert(test_from_mapping_like()); + + test_access<int, true, true>(); + static_assert(test_access<int, true, true>()); + test_access<IntLike, true, true>(); + test_access<ThrowingInt, false, false>(); + test_access<MutatingInt, true, false>(); + test_access<RValueInt, true, false>(); + + test_swap(); + static_assert(test_swap()); + test_swap_adl(); + + test_nothrow_movable_all(); + test_nothrow_is_methods<std::layout_right, true>(); + test_nothrow_is_methods<ThrowingLayout, false>(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/out_of_bounds_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/out_of_bounds_neg.cc new file mode 100644 index 0000000..dceae56 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/out_of_bounds_neg.cc @@ -0,0 +1,24 @@ +// { dg-do compile { target c++23 } } +#include<mdspan> + +#include "layout_like.h" + +template<typename Layout> +constexpr bool +test_invalid_multi_index() +{ + + double data = 1.1; + auto m = typename Layout::mapping<std::extents<int, 1, 2, 3>>{}; + auto md = std::mdspan(&data, m); + + [[maybe_unused]] double x = md[0, 2, 2]; // { dg-error "expansion of" } + return true; +}; +static_assert(test_invalid_multi_index<LayoutLike>()); // { dg-error "expansion of" } +static_assert(test_invalid_multi_index<std::layout_left>()); // { dg-error "expansion of" } +static_assert(test_invalid_multi_index<std::layout_right>()); // { dg-error "expansion of" } +static_assert(test_invalid_multi_index<std::layout_stride>()); // { dg-error "expansion of" } + +// { dg-prune-output "non-constant condition" } +// { dg-prune-output "__glibcxx_assert" } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/generic.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/generic.cc new file mode 100644 index 0000000..682ff62 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/generic.cc @@ -0,0 +1,71 @@ +// { dg-do compile { target c++26 } } +#include <mdspan> + +namespace adl +{ + struct NoFriend + { + template<typename Extents> + class mapping + { + public: + using extents_type = Extents; + using index_type = typename extents_type::index_type; + }; + }; + + struct NoFull + { + template<typename Extents> + class mapping + { + public: + using extents_type = Extents; + using index_type = typename extents_type::index_type; + + private: + friend constexpr auto + submdspan_mapping(mapping, int) + { return std::submdspan_mapping_result{mapping{}, 0}; } + }; + }; + + struct WrongReturnValue + { + template<typename Extents> + class mapping + { + public: + using extents_type = Extents; + using index_type = typename extents_type::index_type; + + private: + friend constexpr int + submdspan_mapping(mapping, std::full_extent_t) + { return 42; } + }; + }; +} + +template<typename MdSpan, typename... Slices> + concept submdspan_exists = requires (MdSpan md, Slices... slices) + { + std::submdspan(md, slices...); + }; + +template<typename Layout, bool Expected> +constexpr bool +test_invalid_mapping() +{ + using Extents = std::extents<int, 3>; + using MdSpan = std::mdspan<double, Extents, Layout>; + static_assert(submdspan_exists<MdSpan, int> == Expected); + static_assert(submdspan_exists<MdSpan, std::full_extent_t> == Expected); + static_assert(!submdspan_exists<MdSpan>); + static_assert(!submdspan_exists<MdSpan, int, int>); + return true; +} +static_assert(test_invalid_mapping<std::layout_left, true>()); +static_assert(test_invalid_mapping<adl::NoFriend, false>()); +static_assert(test_invalid_mapping<adl::NoFull, false>()); +static_assert(test_invalid_mapping<adl::WrongReturnValue, false>()); diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left.cc new file mode 100644 index 0000000..d6a85d0 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left.cc @@ -0,0 +1,9 @@ +// { dg-do run { target c++26 } } +#include "testcases.h" + +int +main() +{ + test_all<std::layout_left>(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left_padded.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left_padded.cc new file mode 100644 index 0000000..711ce35 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left_padded.cc @@ -0,0 +1,12 @@ +// { dg-do run { target c++26 } } +// { dg-timeout-factor 2 } +#include "testcases.h" + +int +main() +{ + test_all<std::layout_left_padded<1>>(); + test_all<std::layout_left_padded<8>>(); + test_all<std::layout_left_padded<dyn>>(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/right.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/right.cc new file mode 100644 index 0000000..2557072 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/right.cc @@ -0,0 +1,9 @@ +// { dg-do run { target c++26 } } +#include "testcases.h" + +int +main() +{ + test_all<std::layout_right>(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/right_padded.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/right_padded.cc new file mode 100644 index 0000000..24ed6cb --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/right_padded.cc @@ -0,0 +1,12 @@ +// { dg-do run { target c++26 } } +// { dg-timeout-factor 2 } +#include "testcases.h" + +int +main() +{ + test_all<std::layout_right_padded<1>>(); + test_all<std::layout_right_padded<8>>(); + test_all<std::layout_right_padded<dyn>>(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/stride.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/stride.cc new file mode 100644 index 0000000..19451d7 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/stride.cc @@ -0,0 +1,9 @@ +// { dg-do run { target c++26 } } +#include "testcases.h" + +int +main() +{ + test_all<std::layout_stride>(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/testcases.h b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/testcases.h new file mode 100644 index 0000000..d7b751d --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/testcases.h @@ -0,0 +1,360 @@ +#include <mdspan> + +#include <vector> +#include <numeric> +#include "../../layout_traits.h" +#include <testsuite_hooks.h> + +constexpr size_t dyn = std::dynamic_extent; +constexpr auto all = std::full_extent; + +template<typename T> + constexpr bool is_strided_slice = false; + +template<typename O, typename E, typename S> + constexpr bool is_strided_slice<std::strided_slice<O, E, S>> = true; + +template<typename MDSpan> + constexpr void + fill(const MDSpan& md) + { + using IndexType = typename MDSpan::index_type; + auto exts = md.extents(); + if constexpr (exts.rank() == 3) + for(IndexType i = 0; i < exts.extent(0); ++i) + for(IndexType j = 0; j < exts.extent(1); ++j) + for(IndexType k = 0; k < exts.extent(2); ++k) + md[i, j, k] = 100 * i + 10 * j + k; + } + +template<typename Int, size_t Rank> + class multi_index_generator + { + struct sentinel + { }; + + class iterator + { + public: + constexpr + iterator(const std::array<Int, Rank>& shape) + : M_shape(shape) + { } + + constexpr iterator& + operator++() + { + if constexpr (Rank > 0) + { + ++M_indices[Rank-1]; + for(size_t i = Rank; i > 1; --i) + if (M_indices[i-1] == M_shape[i-1]) + { + M_indices[i-1] = 0; + ++M_indices[i-2]; + } + } + return *this; + } + + constexpr auto + operator*() + { return M_indices; } + + private: + friend constexpr bool + operator==(const iterator& it, sentinel) + { + if constexpr (Rank > 0) + return it.M_indices[0] == it.M_shape[0]; + else + return true; + } + + std::array<Int, Rank> M_indices{}; + std::array<Int, Rank> M_shape; + }; + + public: + constexpr + multi_index_generator(std::array<Int, Rank> shape) + : M_shape(shape) + { } + + constexpr iterator + begin() const + { return iterator(M_shape); } + + constexpr sentinel + end() const + { return sentinel{}; } + + private: + std::array<Int, Rank> M_shape; + }; + +constexpr bool +test_multi_index() +{ + auto shape = std::array{3, 5, 7, 1}; + auto gen = multi_index_generator(shape); + auto it = gen.begin(); + auto end = gen.end(); + for (int i = 0; i < shape[0]; ++i) + for (int j = 0; j < shape[1]; ++j) + for (int k = 0; k < shape[2]; ++k) + for (int l = 0; l < shape[3]; ++l) + { + VERIFY(it != end); + VERIFY(*it == std::array{i, j, k, l}); + ++it; + } + return true; +} +static_assert(test_multi_index()); + +struct +collapse +{ }; + +template<typename... Slices> + consteval auto + inv_collapsed_index_map() + { + constexpr size_t rank = sizeof...(Slices); + auto is_collapsing = std::array{std::same_as<Slices, collapse>...}; + constexpr auto collapsed_rank = ((!std::same_as<Slices, collapse>) + ... + 0); + + std::array<size_t, collapsed_rank> ret; + if constexpr (collapsed_rank > 0) + for(size_t k = 0, i = 0; i < rank; ++i) + if (!is_collapsing[i]) + ret[k++] = i; + return ret; + } + +static_assert(inv_collapsed_index_map<collapse, collapse, collapse>() + == std::array<size_t, 0>{}); + +static_assert(inv_collapsed_index_map<collapse, decltype(all), collapse>() + == std::array<size_t, 1>{1}); + +template<typename IndexType, typename Slice> + constexpr std::vector<IndexType> + make_selection(IndexType extent, const Slice& slice) + { + if constexpr (std::convertible_to<Slice, IndexType>) + return {static_cast<IndexType>(slice)}; + else if constexpr (std::same_as<Slice, std::full_extent_t>) + { + auto ret = std::vector<IndexType>(static_cast<size_t>(extent)); + std::ranges::iota(ret, 0); + return ret; + } + else if constexpr (is_strided_slice<Slice>) + { + auto ret = std::vector<IndexType>{}; + size_t n = static_cast<size_t>(slice.extent); + for(size_t i = 0; i < n; i += slice.stride) + ret.push_back(slice.offset + i); + return ret; + } + else + { + auto [begin, end] = slice; + auto ret = std::vector<IndexType>(static_cast<size_t>(end - begin)); + std::ranges::iota(ret, begin); + return ret; + } + } + +template<typename Layout, size_t... I, typename... Slices> + constexpr bool + check_selection(std::index_sequence<I...>, auto md, Slices... slices) + { + auto exts = md.extents(); + auto outer_shape = std::array{exts.extent(0), exts.extent(1), exts.extent(2)}; + + constexpr auto full_index = inv_collapsed_index_map<Slices...>(); + auto make_slice = [](size_t i, auto slice) + { + if constexpr (std::same_as<decltype(slice), collapse>) + return i; + else + return slice; + }; + + auto loop_body = [&]<size_t... J>(std::index_sequence<J...>, auto ijk, + auto... slices) + { + auto submd = submdspan(md, slices...[I]...); + auto selection = std::tuple{make_selection(exts.extent(I), slices...[I])...}; + auto inner_shape = std::array<size_t, full_index.size()>{ + std::get<full_index[J]>(selection).size()... + }; + + for (auto ij : multi_index_generator(inner_shape)) + { + ((ijk[full_index[J]] = get<full_index[J]>(selection)[ij[J]]),...); + VERIFY(submd[ij] == md[ijk]); + } + }; + + for (auto ijk : multi_index_generator(outer_shape)) + loop_body(std::make_index_sequence<full_index.size()>(), ijk, + make_slice(ijk[I], slices...[I])...); + return true; + } + +template<typename Layout, typename...MD, typename... Slices> + constexpr bool + check_selection(std::mdspan<MD...> md, Slices... slices) + { + auto indices = std::make_index_sequence<sizeof...(slices)>(); + return check_selection<Layout>(indices, md, slices...); + } + +template<typename Layout, typename IndexType, size_t... Extents, + typename... Slices> + constexpr bool + check_selection(std::extents<IndexType, Extents...>exts, Slices... slices) + { + auto run = [&](auto m) + { + auto storage = std::vector<double>(m.required_span_size()); + auto md = std::mdspan(storage.data(), m); + fill(md); + return check_selection<Layout>(md, slices...); + }; + + if constexpr (std::same_as<Layout, std::layout_stride>) + { + auto m = typename Layout::mapping(exts, std::array{15, 2, 50}); + return run(m); + } + else + { + auto m = typename Layout::mapping(exts); + return run(m); + } + } + +template<typename Layout> + constexpr bool + test_scalar_selection(auto exts) + { + check_selection<Layout>(exts, collapse{}, collapse{}, collapse{}); + return true; + } + +template<typename Layout> + constexpr bool + test_full_lines(auto exts) + { + check_selection<Layout>(exts, all, collapse{}, collapse{}); + check_selection<Layout>(exts, collapse{}, all, collapse{}); + check_selection<Layout>(exts, collapse{}, collapse{}, all); + return true; + } + +template<typename Layout> + constexpr bool + test_full_blocks(auto exts) + { + check_selection<Layout>(exts, all, all, collapse{}); + check_selection<Layout>(exts, all, collapse{}, all); + check_selection<Layout>(exts, collapse{}, all, all); + return true; + } + +template<typename Layout> + constexpr bool + test_cubes(auto exts) + { + auto s0 = std::pair{0, 2}; + auto s1 = std::pair{1, 4}; + auto s2 = std::pair{3, 7}; + + check_selection<Layout>(exts, all, all, all); + check_selection<Layout>(exts, all, all, s2); + check_selection<Layout>(exts, s0, all, all); + check_selection<Layout>(exts, s0, all, s2); + check_selection<Layout>(exts, s0, s1, s2); + return true; + } + +template<typename Layout> + constexpr bool + test_strided_line_selection(auto exts) + { + auto check = [&](auto s) + { + check_selection<Layout>(exts, collapse{}, s, collapse{}); + }; + + check(std::strided_slice(0, 2, 2)); + check(std::strided_slice(0, 3, 2)); + check(std::strided_slice(1, 3, 2)); + check(std::strided_slice(1, std::cw<3>, std::cw<2>)); + return true; + } + +template<typename Layout> + constexpr bool + test_strided_box_selection(auto exts) + { + auto s0 = std::strided_slice(0, 3, 2); + auto s1 = std::strided_slice(1, 4, 2); + auto s2 = std::strided_slice(0, 7, 3); + + check_selection<Layout>(exts, s0, s1, s2); + return true; + } + +template<typename Layout> + constexpr bool + test_all_cheap() + { + constexpr auto dyn_exts = std::extents(3, 5, 7); + constexpr auto sta_exts = std::extents<int, 3, 5, 7>{}; + + test_scalar_selection<Layout>(dyn_exts); + test_scalar_selection<Layout>(sta_exts); + static_assert(test_scalar_selection<Layout>(dyn_exts)); + static_assert(test_scalar_selection<Layout>(sta_exts)); + + test_full_lines<Layout>(dyn_exts); + test_full_lines<Layout>(sta_exts); + static_assert(test_full_lines<Layout>(dyn_exts)); + static_assert(test_full_lines<Layout>(sta_exts)); + + test_strided_box_selection<Layout>(dyn_exts); + test_strided_box_selection<Layout>(sta_exts); + static_assert(test_strided_box_selection<Layout>(dyn_exts)); + static_assert(test_strided_box_selection<Layout>(sta_exts)); + return true; + } + +template<typename Layout> + constexpr bool + test_all_expensive() + { + auto run = [](auto exts) + { + test_full_blocks<Layout>(exts); + test_cubes<Layout>(exts); + }; + + run(std::extents(3, 5, 7)); + run(std::extents<int, 3, 5, 7>{}); + return true; + } + +template<typename Layout> + constexpr bool + test_all() + { + test_all_cheap<Layout>(); + test_all_expensive<Layout>(); + return true; + } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/strided_slice.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/strided_slice.cc new file mode 100644 index 0000000..6fa5aaa --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/strided_slice.cc @@ -0,0 +1,49 @@ +// { dg-do run { target c++26 } } +#include <mdspan> + +#include <cstdint> +#include <testsuite_hooks.h> + +constexpr void +check_strided_slice(auto s, auto offset, auto extent, auto stride) +{ + using slice_type = std::strided_slice<decltype(offset), decltype(extent), + decltype(stride)>; + static_assert(std::same_as<decltype(s), slice_type>); + VERIFY(s.offset == offset); + VERIFY(s.extent == extent); + VERIFY(s.stride == stride); +} + +constexpr void +test_initializers(auto offset, auto extent, auto stride) +{ + auto check = [&](auto s) + { + check_strided_slice(s, offset, extent, stride); + }; + + check(std::strided_slice{.offset=offset, .extent=extent, .stride=stride}); + check(std::strided_slice{offset, extent, stride}); + check(std::strided_slice(offset, extent, stride)); +} + +constexpr bool +test_all() +{ + test_initializers(0, 1, 2); + test_initializers(std::integral_constant<short, 0>{}, size_t{1}, std::cw<2>); + test_initializers(-1, 2, 2); +#ifdef __SIZEOF_INT128__ + test_initializers((__int128)1, (unsigned __int128)-2, std::cw<(__int128)3>); +#endif + return true; +} + +int +main() +{ + test_all(); + static_assert(test_all()); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/strided_slice_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/strided_slice_neg.cc new file mode 100644 index 0000000..0f1d791 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/strided_slice_neg.cc @@ -0,0 +1,19 @@ +// { dg-do compile { target c++26 } } +#include <mdspan> + +#include <cstdint> + +template<typename OffsetType, typename ExtentType, typename StrideType> + constexpr bool + test_invalid() + { + auto s1 = std::strided_slice(OffsetType{}, ExtentType{}, StrideType{}); // { dg-error "required from" } + return true; + } + +static_assert(test_invalid<double, int, int>()); // { dg-error "required from" } +static_assert(test_invalid<int, double, int>()); // { dg-error "required from" } +static_assert(test_invalid<int, int, double>()); // { dg-error "required from" } +static_assert(test_invalid<double, double, double>()); // { dg-error "required from" } + +// { dg-prune-output "static assertion failed" } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_canonicalize_slices.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_canonicalize_slices.cc new file mode 100644 index 0000000..077bafc --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_canonicalize_slices.cc @@ -0,0 +1,220 @@ +// { dg-do run { target c++26 } } +#include <mdspan> + +#include <testsuite_hooks.h> +#include <cstddef> +#include <cstdint> + +constexpr size_t dyn = std::dynamic_extent; + +template<typename Extents, typename CInt> + constexpr bool + check_collapsing(Extents exts, CInt ci_raw) + { + using IndexType = typename Extents::index_type; + auto ci_expected = std::cw<IndexType{ci_raw.value}>; + auto [ci] = std::submdspan_canonicalize_slices(exts, ci_raw); + static_assert(std::same_as<decltype(ci), decltype(ci_expected)>); + VERIFY(std::cmp_equal(ci.value, ci_raw.value)); + + auto [i] = std::submdspan_canonicalize_slices(exts, ci_raw.value); + static_assert(std::same_as<decltype(i), IndexType>); + VERIFY(std::cmp_equal(i, ci_raw.value)); + return true; + } + +template<typename Extents> + constexpr bool + test_scalar(Extents exts) + { + using IndexType = typename Extents::index_type; + + check_collapsing(exts, std::cw<uint8_t{0}>); + check_collapsing(exts, std::cw<IndexType{0}>); + + check_collapsing(exts, std::cw<uint8_t{4}>); + check_collapsing(exts, std::cw<IndexType{4}>); + return true; + } + +constexpr bool +test_scalar() +{ + test_scalar(std::extents<int, dyn>{5}); + test_scalar(std::extents<int, 5>{}); + test_scalar(std::extents<unsigned int, dyn>{5}); + test_scalar(std::extents<unsigned int, 5>{}); + return true; +} + +constexpr void +assert_same(auto lhs, auto rhs) +{ + static_assert(std::same_as<decltype(lhs), decltype(rhs)>); + VERIFY(lhs == rhs); +} + +template<template<typename, typename> typename Pair> + constexpr bool + test_pair(auto exts, auto cbegin, auto cend, auto coffset, auto cextent) + { + using IndexType = typename decltype(exts)::index_type; + auto c1 = std::cw<IndexType{1}>; + + auto raw_cc = Pair{cbegin, cend}; + auto [cc] = std::submdspan_canonicalize_slices(exts, raw_cc); + assert_same(cc.offset, coffset); + assert_same(cc.extent, cextent); + assert_same(cc.stride, c1); + + auto raw_cd = Pair{cbegin, cend.value}; + auto [cd] = std::submdspan_canonicalize_slices(exts, raw_cd); + assert_same(cd.offset, coffset); + assert_same(cd.extent, cextent.value); + assert_same(cd.stride, c1); + + auto raw_dc = Pair{cbegin.value, cend}; + auto [dc] = std::submdspan_canonicalize_slices(exts, raw_dc); + assert_same(dc.offset, coffset.value); + assert_same(dc.extent, cextent.value); + assert_same(dc.stride, c1); + + auto raw_dd = Pair{cbegin.value, cend.value}; + auto [dd] = std::submdspan_canonicalize_slices(exts, raw_dd); + assert_same(dd.offset, coffset.value); + assert_same(dd.extent, cextent.value); + assert_same(dd.stride, c1); + return true; + } + +template<template<typename, typename> typename Pair> + constexpr bool + test_pair() + { + test_pair<Pair>(std::extents<int, dyn>{5}, std::cw<uint8_t{2}>, + std::cw<uint8_t{5}>, std::cw<2>, std::cw<3>); + test_pair<Pair>(std::extents<int, 5>{}, std::cw<uint8_t{2}>, + std::cw<uint8_t{5}>, std::cw<2>, std::cw<3>); + test_pair<Pair>(std::extents<int, 0>{}, std::cw<uint8_t{0}>, + std::cw<uint8_t{0}>, std::cw<0>, std::cw<0>); + test_pair<Pair>(std::extents<int, dyn>{0}, std::cw<uint8_t{0}>, + std::cw<uint8_t{0}>, std::cw<0>, std::cw<0>); + return true; + } + +template<typename Lower, typename Upper> +struct Range +{ + Lower lower; + Upper upper; +}; + +constexpr bool +test_pair_all() +{ + test_pair<std::pair>(); + test_pair<std::tuple>(); + test_pair<Range>(); + return true; +} + +constexpr bool +test_strided_slice(auto exts, auto co, auto ce, auto cs) +{ + using IndexType = decltype(exts)::index_type; + + auto coffset = std::cw<IndexType{co.value}>; + auto cextent = std::cw<IndexType{ce.value}>; + auto cstride = std::cw<IndexType{cs.value}>; + + auto raw_ccc = std::strided_slice{co, ce, cs}; + auto [ccc] = std::submdspan_canonicalize_slices(exts, raw_ccc); + assert_same(ccc.offset, coffset); + assert_same(ccc.extent, cextent); + assert_same(ccc.stride, cstride); + + auto raw_dcc = std::strided_slice{co.value, ce, cs}; + auto [dcc] = std::submdspan_canonicalize_slices(exts, raw_dcc); + assert_same(dcc.offset, coffset.value); + assert_same(dcc.extent, cextent); + assert_same(dcc.stride, cstride); + + auto raw_cdc = std::strided_slice{co, ce.value, cs}; + auto [cdc] = std::submdspan_canonicalize_slices(exts, raw_cdc); + assert_same(cdc.offset, coffset); + assert_same(cdc.extent, cextent.value); + assert_same(cdc.stride, cstride); + + auto raw_ccd = std::strided_slice{co, ce, cs.value}; + auto [ccd] = std::submdspan_canonicalize_slices(exts, raw_ccd); + assert_same(ccd.offset, coffset); + assert_same(ccd.extent, cextent); + assert_same(ccd.stride, cstride.value); + return true; +} + +constexpr bool +test_strided_slice() +{ + auto run = [](auto exts) + { + auto cs = std::cw<uint8_t{9}>; + test_strided_slice(exts, std::cw<uint8_t{2}>, std::cw<uint8_t{3}>, cs); + test_strided_slice(exts, std::cw<uint8_t{0}>, std::cw<uint8_t{5}>, cs); + }; + + run(std::extents<int, 5>{}); + run(std::extents<int, dyn>{5}); + return true; +} + +constexpr bool +test_strided_slice_zero_extent(auto exts, auto cs) +{ + using IndexType = typename decltype(exts)::index_type; + auto c0 = std::cw<uint8_t{0}>; + auto raw_ccc = std::strided_slice{c0, c0, cs}; + auto [ccc] = std::submdspan_canonicalize_slices(exts, raw_ccc); + assert_same(ccc.stride, std::cw<IndexType{1}>); + + auto raw_ccd = std::strided_slice{c0, c0, cs.value}; + auto [ccd] = std::submdspan_canonicalize_slices(exts, raw_ccd); + assert_same(ccd.stride, std::cw<IndexType{1}>); + return true; +} + +constexpr bool +test_strided_slice_zero_extent(auto exts) +{ + test_strided_slice_zero_extent(exts, std::cw<uint8_t{0}>); + test_strided_slice_zero_extent(exts, std::cw<uint8_t{9}>); + return true; +} + +constexpr bool +test_strided_slice_zero_extent() +{ + test_strided_slice_zero_extent(std::extents<int, 0>{}); + test_strided_slice_zero_extent(std::extents<int, dyn>{0}); + test_strided_slice_zero_extent(std::extents<int, 5>{}); + test_strided_slice_zero_extent(std::extents<int, dyn>{5}); + return true; +} + +constexpr bool +test_all() +{ + test_scalar(); + test_pair_all(); + test_strided_slice(); + test_strided_slice_zero_extent(); + return true; +} + +int +main() +{ + test_all(); + static_assert(test_all()); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_canonicalize_slices_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_canonicalize_slices_neg.cc new file mode 100644 index 0000000..94bca18 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_canonicalize_slices_neg.cc @@ -0,0 +1,208 @@ +// { dg-do compile { target c++26 } } +#include <mdspan> + +#include <cstdint> + +constexpr size_t dyn = std::dynamic_extent; + +constexpr auto dyn_empty = std::extents<int32_t, dyn>{0}; +constexpr auto sta_empty = std::extents<uint32_t, 0>{}; + +constexpr auto dyn_uexts = std::extents<uint8_t, dyn>{5}; +constexpr auto sta_uexts = std::extents<uint16_t, 5>{5}; +constexpr auto dyn_sexts = std::extents<int8_t, dyn>{5}; +constexpr auto sta_sexts = std::extents<int16_t, 5>{5}; + +constexpr bool +test_rank_mismatch() +{ + auto exts = std::extents(1); + std::submdspan_canonicalize_slices(exts, 0, 0); // { dg-error "no matching" } + return true; +} + +template<typename Int, typename Extents> +constexpr bool +test_under1(Int i1, Extents exts) +{ + auto [s1] = std::submdspan_canonicalize_slices(exts, i1); + return true; +} + +static_assert(test_under1(-1, dyn_sexts)); // { dg-error "expansion of" } +static_assert(test_under1(-1, dyn_uexts)); // { dg-error "expansion of" } +static_assert(test_under1(-1, sta_sexts)); // { dg-error "expansion of" } +static_assert(test_under1(-1, sta_uexts)); // { dg-error "expansion of" } + +static_assert(test_under1(std::cw<-1>, dyn_sexts)); // { dg-error "required from" } +static_assert(test_under1(std::cw<-1>, dyn_uexts)); // { dg-error "required from" } +static_assert(test_under1(std::cw<-1>, sta_sexts)); // { dg-error "required from" } +static_assert(test_under1(std::cw<-1>, sta_uexts)); // { dg-error "required from" } + +template<typename Int, typename Extents> +constexpr bool +test_over1(Int i1, Extents exts) +{ + auto [s1] = std::submdspan_canonicalize_slices(exts, i1); + return true; +} + +static_assert(test_over1(0, dyn_empty)); // { dg-error "expansion of" } +static_assert(test_over1(0, sta_empty)); // { dg-error "expansion of" } +static_assert(test_over1(5, dyn_sexts)); // { dg-error "expansion of" } +static_assert(test_over1(5, dyn_uexts)); // { dg-error "expansion of" } +static_assert(test_over1(5, sta_sexts)); // { dg-error "expansion of" } +static_assert(test_over1(5, sta_uexts)); // { dg-error "expansion of" } + +static_assert(test_over1(std::cw<0>, dyn_empty)); // { dg-error "expansion of" } +static_assert(test_over1(std::cw<0>, sta_empty)); // { dg-error "expansion of" } +static_assert(test_over1(std::cw<5>, dyn_sexts)); // { dg-error "expansion of" } +static_assert(test_over1(std::cw<5>, dyn_uexts)); // { dg-error "expansion of" } +static_assert(test_over1(std::cw<5>, sta_sexts)); // { dg-error "expansion of" } +static_assert(test_over1(std::cw<5>, sta_uexts)); // { dg-error "expansion of" } + +template<typename Offset, typename Extent, typename Stride, typename Extents> + constexpr bool + test_under2(Offset o, Extent e, Stride s, Extents exts) + { + std::submdspan_canonicalize_slices(exts, std::strided_slice{o, e, s}); + return true; + } + +constexpr auto i8_1 = int8_t{1}; + +static_assert(test_under2(-i8_1, 0, 1, dyn_uexts)); // { dg-error "expansion of" } +static_assert(test_under2(0, -i8_1, 1, dyn_uexts)); // { dg-error "expansion of" } +static_assert(test_under2(0, 1, -i8_1, dyn_uexts)); // { dg-error "expansion of" } +static_assert(test_under2(-i8_1, 0, 1, dyn_sexts)); // { dg-error "expansion of" } +static_assert(test_under2(0, -i8_1, 1, dyn_sexts)); // { dg-error "expansion of" } +static_assert(test_under2(0, 1, -i8_1, dyn_sexts)); // { dg-error "expansion of" } +static_assert(test_under2(-i8_1, 0, 1, sta_uexts)); // { dg-error "expansion of" } +static_assert(test_under2(0, -i8_1, 1, sta_uexts)); // { dg-error "expansion of" } +static_assert(test_under2(0, 1, -i8_1, sta_uexts)); // { dg-error "expansion of" } +static_assert(test_under2(-i8_1, 0, 1, sta_sexts)); // { dg-error "expansion of" } +static_assert(test_under2(0, -i8_1, 1, sta_sexts)); // { dg-error "expansion of" } +static_assert(test_under2(0, 1, -i8_1, sta_sexts)); // { dg-error "expansion of" } + +constexpr auto c_i8_m1 = std::cw<int8_t{-1}>; +constexpr auto c_i16_m1 = std::cw<int16_t{-1}>; +constexpr auto c_i64_m1 = std::cw<int64_t{-1}>; + +static_assert(test_under2(c_i8_m1, 0, 1, dyn_uexts)); // { dg-error "required from" } +static_assert(test_under2(0, c_i16_m1, 1, dyn_uexts)); // { dg-error "required from" } +static_assert(test_under2(0, 1, c_i64_m1, dyn_uexts)); // { dg-error "required from" } +static_assert(test_under2(c_i8_m1, 0, 1, dyn_sexts)); // { dg-error "required from" } +static_assert(test_under2(0, c_i16_m1, 1, dyn_sexts)); // { dg-error "required from" } +static_assert(test_under2(0, 1, c_i64_m1, dyn_sexts)); // { dg-error "required from" } +static_assert(test_under2(c_i8_m1, 0, 1, sta_uexts)); // { dg-error "required from" } +static_assert(test_under2(0, c_i16_m1, 1, sta_uexts)); // { dg-error "required from" } +static_assert(test_under2(0, 1, c_i64_m1, sta_uexts)); // { dg-error "required from" } +static_assert(test_under2(c_i8_m1, 0, 1, sta_sexts)); // { dg-error "required from" } +static_assert(test_under2(0, c_i16_m1, 1, sta_sexts)); // { dg-error "required from" } +static_assert(test_under2(0, 1, c_i64_m1, sta_sexts)); // { dg-error "required from" } + +template<typename Offset, typename Extent, typename Stride, typename Extents> + constexpr bool + test_over2(Offset o, Extent e, Stride s, Extents exts) + { + std::submdspan_canonicalize_slices(exts, std::strided_slice{o, e, s}); + return true; + } + +constexpr auto i8_6 = int8_t{6}; +constexpr auto c_i8_6 = std::cw<int8_t{6}>; +constexpr auto c2 = std::cw<2>; +constexpr auto c4 = std::cw<4>; + +static_assert(test_over2(i8_6, 0, 1, dyn_uexts)); // { dg-error "expansion of" } +static_assert(test_over2(0, i8_6, 1, dyn_uexts)); // { dg-error "expansion of" } +static_assert(test_over2(2, 4, 0, dyn_uexts)); // { dg-error "expansion of" } +static_assert(test_over2(c_i8_6, 0, 1, dyn_uexts)); // { dg-error "expansion of" } +static_assert(test_over2(0, c_i8_6, 1, dyn_uexts)); // { dg-error "expansion of" } +static_assert(test_over2(c2, 4, 1, dyn_uexts)); // { dg-error "expansion of" } +static_assert(test_over2(2, c4, 1, dyn_uexts)); // { dg-error "expansion of" } +static_assert(test_over2(c2, c4, 1, dyn_uexts)); // { dg-error "expansion of" } + +static_assert(test_over2(i8_6, 0, 1, dyn_sexts)); // { dg-error "expansion of" } +static_assert(test_over2(0, i8_6, 1, dyn_sexts)); // { dg-error "expansion of" } +static_assert(test_over2(2, 4, 0, dyn_sexts)); // { dg-error "expansion of" } +static_assert(test_over2(c_i8_6, 0, 1, dyn_sexts)); // { dg-error "expansion of" } +static_assert(test_over2(0, c_i8_6, 1, dyn_sexts)); // { dg-error "expansion of" } +static_assert(test_over2(c2, 4, 1, dyn_sexts)); // { dg-error "expansion of" } +static_assert(test_over2(2, c4, 1, dyn_sexts)); // { dg-error "expansion of" } +static_assert(test_over2(c2, c4, 1, dyn_sexts)); // { dg-error "expansion of" } + +static_assert(test_over2(i8_6, 0, 1, sta_uexts)); // { dg-error "expansion of" } +static_assert(test_over2(0, i8_6, 1, sta_uexts)); // { dg-error "expansion of" } +static_assert(test_over2(2, 4, 0, sta_uexts)); // { dg-error "expansion of" } +static_assert(test_over2(c_i8_6, 0, 1, sta_uexts)); // { dg-error "expansion of" } +static_assert(test_over2(0, c_i8_6, 1, sta_uexts)); // { dg-error "expansion of" } +static_assert(test_over2(c2, 4, 1, sta_uexts)); // { dg-error "expansion of" } +static_assert(test_over2(2, c4, 1, sta_uexts)); // { dg-error "expansion of" } +static_assert(test_over2(c2, c4, 1, sta_uexts)); // { dg-error "expansion of" } + +static_assert(test_over2(i8_6, 0, 1, sta_sexts)); // { dg-error "expansion of" } +static_assert(test_over2(0, i8_6, 1, sta_sexts)); // { dg-error "expansion of" } +static_assert(test_over2(2, 4, 0, sta_sexts)); // { dg-error "expansion of" } +static_assert(test_over2(c_i8_6, 0, 1, sta_sexts)); // { dg-error "expansion of" } +static_assert(test_over2(0, c_i8_6, 1, sta_sexts)); // { dg-error "expansion of" } +static_assert(test_over2(c2, 4, 1, sta_sexts)); // { dg-error "expansion of" } +static_assert(test_over2(2, c4, 1, sta_sexts)); // { dg-error "expansion of" } +static_assert(test_over2(c2, c4, 1, sta_sexts)); // { dg-error "expansion of" } + +// Checks the precondition: offset + extent <= exts.extent(0) for unsigned +// index_type when offset + extent overflows. +constexpr bool +test_overflow1(auto o, auto e) +{ + auto exts = std::extents<uint8_t, dyn>{255}; + auto slice = std::strided_slice{o, e, 1}; + std::submdspan_canonicalize_slices(exts, slice); + return true; +} + +static_assert(test_overflow1(128, 128)); // { dg-error "expansion of" } +static_assert(test_overflow1(std::cw<128>, 128)); // { dg-error "expansion of" } +static_assert(test_overflow1(128, std::cw<128>)); // { dg-error "expansion of" } +static_assert(test_overflow1(std::cw<128>, std::cw<128>)); // { dg-error "expansion of" } + +constexpr bool +test_overflow2(auto b, auto e) +{ + auto exts = std::extents<uint8_t, dyn>{255}; + auto slice = std::pair{b, e}; + std::submdspan_canonicalize_slices(exts, slice); + return true; +} + +static_assert(test_overflow2(5, 4)); // { dg-error "expansion of" } +static_assert(test_overflow2(std::cw<5>, 4)); // { dg-error "expansion of" } +static_assert(test_overflow2(5, std::cw<4>)); // { dg-error "expansion of" } +static_assert(test_overflow2(std::cw<5>, std::cw<4>)); // { dg-error "expansion of" } + +constexpr auto u8_4 = uint8_t{4}; +constexpr auto u8_5 = uint8_t{5}; +static_assert(test_overflow2(u8_5, u8_4)); // { dg-error "expansion of" } +static_assert(test_overflow2(std::cw<u8_5>, u8_4)); // { dg-error "expansion of" } +static_assert(test_overflow2(u8_5, std::cw<u8_4>)); // { dg-error "expansion of" } +static_assert(test_overflow2(std::cw<u8_5>, std::cw<u8_4>)); // { dg-error "expansion of" } + +constexpr bool +test_invalid(auto e, auto s) +{ + auto exts = std::extents(5); + auto slice = std::strided_slice(0, e, s); + std::submdspan_canonicalize_slices(exts, slice); + return true; +} + +static_assert(test_invalid(3, 0)); // { dg-error "expansion of" } +static_assert(test_invalid(3, std::cw<0>)); // { dg-error "expansion of" } +static_assert(test_invalid(3, std::cw<0>)); // { dg-error "expansion of" } +static_assert(test_invalid(std::cw<3>, std::cw<0>)); // { dg-error "expansion of" } + + +// { dg-prune-output "static assertion failed" } +// { dg-prune-output "__glibcxx_assert_fail" } +// { dg-prune-output "__glibcxx_assert" } +// { dg-prune-output "non-constant condition" } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents.cc new file mode 100644 index 0000000..841910a --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents.cc @@ -0,0 +1,169 @@ +// { dg-do run { target c++26 } } +#include <mdspan> + +#include "../int_like.h" +#include <testsuite_hooks.h> + +constexpr size_t dyn = std::dynamic_extent; +constexpr auto all = std::full_extent; + +constexpr bool +test_from_full_extent() +{ + auto exts = std::extents<int, 3, dyn, 7>{}; + auto sub_exts = submdspan_extents(exts, 1, all, all); + VERIFY(sub_exts.rank() == 2); + VERIFY(sub_exts.static_extent(0) == dyn); + VERIFY(sub_exts.extent(0) == exts.extent(1)); + VERIFY(std::cmp_equal(sub_exts.static_extent(1), exts.extent(2))); + return true; +} + +template<template<typename, typename> typename Pair, template<int> typename Cw> + constexpr bool + test_from_tuple() + { + auto exts = std::extents<int, 3, 5, 7>{}; + auto s0 = Cw<1>{}; + auto s1 = Pair{Cw<1>{}, Cw<2>{}}; + auto s2 = Pair{Cw<1>{}, 4}; + auto sub_exts = submdspan_extents(exts, s0, s1, s2); + VERIFY(sub_exts.rank() == 2); + VERIFY(sub_exts.static_extent(0) == size_t(get<1>(s1) - get<0>(s1))); + VERIFY(sub_exts.static_extent(1) == dyn); + VERIFY(std::cmp_equal(sub_exts.extent(1), get<1>(s2) - get<0>(s2))); + return true; + } + +template<template<int> typename Cw> + constexpr bool + test_from_tuple_all() + { + test_from_tuple<std::tuple, Cw>(); + test_from_tuple<std::pair, Cw>(); + return true; + } + + +template<typename Int> + void + test_from_int_like_as_scalar() + { + auto exts = std::extents<int, 3, 5>{}; + auto sub_exts = submdspan_extents(exts, Int(1), std::tuple{1, 3}); + VERIFY(sub_exts.rank() == 1); + VERIFY(sub_exts.static_extent(0) == dyn); + VERIFY(sub_exts.extent(0) == 2); + } + +template<template<int> typename Cw> + constexpr bool + test_from_const_int() + { + auto exts = std::extents<int, 3, 5>{}; + auto sub_exts = submdspan_extents(exts, Cw<1>{}, std::tuple{1, 3}); + VERIFY(sub_exts.rank() == 1); + VERIFY(sub_exts.static_extent(0) == dyn); + VERIFY(sub_exts.extent(0) == 2); + return true; + } + +template<typename Int> + constexpr bool + test_from_int_like_in_tuple() + { + auto exts = std::extents<int, 3, 5>{}; + auto sub_exts = submdspan_extents(exts, Int(1), std::tuple{Int(1), Int(3)}); + VERIFY(sub_exts.rank() == 1); + VERIFY(sub_exts.static_extent(0) == dyn); + VERIFY(sub_exts.extent(0) == 2); + return true; + } + +template<template<int> typename Cw> + constexpr bool + test_from_strided_slice() + { + auto exts = std::extents<int, 5, 7, 11>{}; + { + auto s0 = 1; + auto s1 = std::strided_slice{0, 0, 0}; + auto s2 = std::strided_slice{1, Cw<0>{}, 0}; + auto sub_exts = submdspan_extents(exts, s0, s1, s2); + VERIFY(sub_exts.rank() == 2); + VERIFY(sub_exts.static_extent(0) == dyn); + VERIFY(sub_exts.extent(0) == 0); + VERIFY(sub_exts.static_extent(1) == 0); + } + + { + auto s0 = 1; + auto s1 = std::strided_slice{0, 2, Cw<1>{}}; + auto s2 = std::strided_slice{1, Cw<2>{}, 1}; + auto sub_exts = submdspan_extents(exts, s0, s1, s2); + VERIFY(sub_exts.rank() == 2); + VERIFY(sub_exts.static_extent(0) == dyn); + VERIFY(sub_exts.extent(0) == 2); + VERIFY(sub_exts.static_extent(1) == dyn); + VERIFY(sub_exts.extent(1) == 2); + } + + { + // selected = 1 x [1, 3] x [1, 4, 7, 10] + auto s0 = 1; + auto s1 = std::strided_slice{1, Cw<4>{}, 2}; + auto s2 = std::strided_slice{1, Cw<10>{}, Cw<3>{}}; + auto sub_exts = submdspan_extents(exts, s0, s1, s2); + VERIFY(sub_exts.rank() == 2); + VERIFY(sub_exts.static_extent(0) == dyn); + VERIFY(sub_exts.extent(0) == 2); + VERIFY(sub_exts.static_extent(1) == 4); + } + + { + // selected = [0, 2] x [1, 3] x [0, 3, 6] + auto s0 = std::strided_slice(0, 3, 2); + auto s1 = std::strided_slice(1, 4, 2); + auto s2 = std::strided_slice(0, 7, 3); + auto sub_exts = submdspan_extents(exts, s0, s1, s2); + VERIFY(sub_exts.rank() == 3); + VERIFY(sub_exts.extent(0) == 2); + VERIFY(sub_exts.extent(1) == 2); + VERIFY(sub_exts.extent(2) == 3); + } + return true; + } + +template<int Value> + using CW = std::constant_wrapper<Value, int>; + +template<int Value> + using IC = std::integral_constant<int, Value>; + +constexpr bool +test_all() +{ + test_from_full_extent(); + test_from_tuple_all<CW>(); + test_from_tuple_all<IC>(); + test_from_const_int<CW>(); + test_from_const_int<IC>(); + test_from_strided_slice<CW>(); + test_from_strided_slice<IC>(); + test_from_int_like_in_tuple<StructuralInt>(); + return true; +} + +int +main() +{ + test_all(); + static_assert(test_all()); + + test_from_int_like_as_scalar<CustomIndexType<CustomIndexKind::Const, true>>(); + test_from_int_like_as_scalar<CustomIndexType<CustomIndexKind::Throwing, true>>(); + test_from_int_like_as_scalar<CustomIndexType<CustomIndexKind::Mutating, true>>(); + test_from_int_like_as_scalar<CustomIndexType<CustomIndexKind::RValue, true>>(); + test_from_int_like_as_scalar<StructuralInt>(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents_neg.cc new file mode 100644 index 0000000..cf27c0c --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents_neg.cc @@ -0,0 +1,48 @@ +// { dg-do compile { target c++26 } } +#include <mdspan> + +#include <cstdint> + +struct NotASlice +{ }; + +constexpr bool +test_unrelated_stride_type() +{ + auto exts = std::extents(3, 5, 7); + auto sub_exts = submdspan_extents(exts, 1, NotASlice{}, 2); // { dg-error "required from" } + return true; +} +static_assert(test_unrelated_stride_type()); + +constexpr bool +test_invalid_stride_zero() +{ + auto exts = std::extents(3, 5, 7); + auto s = std::strided_slice{0, 1, 0}; + auto sub_exts = submdspan_extents(exts, 1, s, 2); // { dg-error "expansion of" } + return true; +} +static_assert(test_invalid_stride_zero()); + +template<typename Slice> +constexpr bool +test_out_of_bounds(const Slice& slice) +{ + auto exts = std::extents<uint16_t, 3, 5, 7>{}; + auto sub_exts = submdspan_extents(exts, 1, slice, 2); // { dg-error "expansion of" } + return true; +} +static_assert(test_out_of_bounds(std::strided_slice{0, 6, 1})); // { dg-error "expansion of" } +static_assert(test_out_of_bounds(std::strided_slice{0, 7, 2})); // { dg-error "expansion of" } +static_assert(test_out_of_bounds(std::strided_slice{1, 6, 1})); // { dg-error "expansion of" } +static_assert(test_out_of_bounds(std::strided_slice{1, 6, 2})); // { dg-error "expansion of" } +static_assert(test_out_of_bounds(std::tuple{1, 6})); // { dg-error "expansion of" } +static_assert(test_out_of_bounds(std::tuple{std::cw<1>, std::cw<6>})); // { dg-error "expansion of" } +static_assert(test_out_of_bounds(std::strided_slice{-1, 2, 1})); // { dg-error "expansion of" } +static_assert(test_out_of_bounds(std::tuple{-1, 2})); // { dg-error "expansion of" } + +// { dg-prune-output "cannot decompose class type 'NotASlice'" } +// { dg-prune-output "static assertion failed" } +// { dg-prune-output "__glibcxx_assert_fail" } +// { dg-prune-output "non-constant condition" } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc new file mode 100644 index 0000000..efd71d1 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc @@ -0,0 +1,301 @@ +// { dg-do run { target c++26 } } +#include <mdspan> + +#include <iostream> // TODO remove +#include "../layout_traits.h" +#include <testsuite_hooks.h> + +constexpr size_t dyn = std::dynamic_extent; +constexpr auto all = std::full_extent; + +template<typename Mapping, typename... Slices> + constexpr auto + call_submdspan_mapping(const Mapping& m, std::tuple<Slices...> slices) + { + auto impl = [&]<size_t... I>(std::index_sequence<I...>) + { return submdspan_mapping(m, get<I>(slices)...); }; + return impl(std::make_index_sequence<sizeof...(Slices)>()); + } + +template<typename Layout> + constexpr bool + test_layout_common_return_types() + { + constexpr auto padding_side = DeducePaddingSide::from_typename<Layout>(); + using Traits = LayoutTraits<padding_side>; + using layout_unpadded = typename Traits::layout_same; + + { + auto m0 = typename Layout::mapping(std::extents()); + auto result = submdspan_mapping(m0); + using layout_type = typename decltype(result.mapping)::layout_type; + static_assert(std::same_as<layout_type, Layout>); + } + + auto exts = Traits::make_extents(std::dims<5, int>(3, 5, 7, 11, 13)); + auto m = typename Layout::mapping(exts); + auto s251 = std::strided_slice{2, 5, std::cw<1>}; + + { + auto slices = std::tuple{0, 0, 0, 0, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + static_assert(std::same_as<layout_type, layout_unpadded>); + } + + { + auto s0 = std::strided_slice{1, 1, std::cw<1>}; + auto slices = std::tuple{s0, all, all, s251, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + static_assert(is_same_padded<padding_side, layout_type>); + } + + { + auto s0 = std::strided_slice{1, 2, std::cw<1>}; + auto slices = std::tuple{s0, all, all, s251, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + static_assert(is_same_padded<padding_side, layout_type>); + } + + { + auto s0 = std::strided_slice{1, 2, std::cw<1>}; + auto slices = std::tuple{s0, 0, all, s251, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + static_assert(is_same_padded<padding_side, layout_type>); + } + + { + auto s0 = std::strided_slice{1, 2, 1}; + auto slices = std::tuple{s0, all, all, s251, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = decltype(result.mapping)::layout_type; + static_assert(std::same_as<layout_type, std::layout_stride>); + } + + { + auto slices = std::tuple{1, all, all, s251, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = decltype(result.mapping)::layout_type; + static_assert(std::same_as<layout_type, std::layout_stride>); + } + + { + auto s3 = std::strided_slice{2, std::cw<7>, std::cw<2>}; + auto slices = std::tuple{all, all, all, s3, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = decltype(result.mapping)::layout_type; + static_assert(std::same_as<layout_type, std::layout_stride>); + } + return true; + } + +template<typename Layout> + constexpr bool + test_layout_unpadded_return_types() + { + constexpr auto padding_side = DeducePaddingSide::from_typename<Layout>(); + using Traits = LayoutTraits<padding_side>; + + auto exts = Traits::make_extents(std::dims<5, int>(3, 5, 7, 11, 13)); + auto m = typename Layout::mapping(exts); + auto s251 = std::strided_slice{2, 5, std::cw<1>}; + + { + auto slices = std::tuple{all, all, all, s251, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + static_assert(std::same_as<layout_type, Layout>); + } + return true; + } + +template<typename Layout> + constexpr bool + test_layout_padded_return_types() + { + constexpr auto padding_side = DeducePaddingSide::from_typename<Layout>(); + using Traits = LayoutTraits<padding_side>; + + auto exts = Traits::make_extents(std::dims<5, int>(3, 5, 7, 11, 13)); + auto m = typename Layout::mapping(exts); + auto s251 = std::strided_slice{2, 5, std::cw<1>}; + + { + auto slices = std::tuple{all, all, all, s251, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + using layout_expected = typename Traits::layout_same_padded<dyn>; + static_assert(std::same_as<layout_type, layout_expected>); + } + + { + auto slices = std::tuple{all, 0, 0, 0, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + using layout_expected = typename Traits::layout_same; + static_assert(std::same_as<layout_type, layout_expected>); + } + + { + auto s121 = std::strided_slice{1, 2, std::cw<1>}; + auto slices = std::tuple{s121, 0, 0, 0, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + using layout_expected = typename Traits::layout_same; + static_assert(std::same_as<layout_type, layout_expected>); + } + + { + auto s121 = std::strided_slice{1, 2, std::cw<1>}; + auto slices = std::tuple{0, s121, 0, 0, 0}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + using layout_type = typename decltype(result.mapping)::layout_type; + static_assert(std::same_as<layout_type, std::layout_stride>); + } + return true; + } + +template<typename Layout> + constexpr bool + test_layout_unpadded_padding_value() + { + using Traits = LayoutTraits<DeducePaddingSide::from_typename<Layout>()>; + auto s0 = std::strided_slice{size_t(1), size_t(2), std::cw<size_t(1)>}; + auto s3 = std::strided_slice{size_t(2), size_t(5), std::cw<size_t(1)>}; + + auto check = [&](auto exts, size_t expected) + { + auto m = typename Layout::mapping(Traits::make_extents(exts)); + auto slices = std::tuple{s0, size_t(0), all, s3, size_t(0)}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + auto padding_value = decltype(result.mapping)::padding_value; + VERIFY(padding_value == expected); + }; + + check(std::extents(std::cw<3>, std::cw<5>, std::cw<7>, 11, 13), 3*5); + check(std::extents(std::cw<3>, std::cw<5>, 7, 11, 13), 3*5); + check(std::extents(std::cw<3>, 5, 7, 11, 13), dyn); + check(std::extents(3, 5, 7, 11, 13), dyn); + return true; + } + +template<typename Layout> +constexpr size_t static_padding_value = 1; + +template<size_t PaddingValue> +constexpr size_t static_padding_value<std::layout_left_padded<PaddingValue>> = PaddingValue; + +template<size_t PaddingValue> +constexpr size_t static_padding_value<std::layout_right_padded<PaddingValue>> = PaddingValue; + +template<typename Layout> + constexpr bool + test_layout_padded_padding_value() + { + using Traits = LayoutTraits<DeducePaddingSide::from_typename<Layout>()>; + auto s0 = std::strided_slice{size_t(1), size_t(2), std::cw<size_t(1)>}; + auto s3 = std::strided_slice{size_t(2), size_t(5), std::cw<size_t(1)>}; + + auto check = [&](auto exts, size_t expected) + { + auto m = typename Layout::mapping(Traits::make_extents(exts)); + auto slices = std::tuple{s0, size_t(0), all, s3, size_t(0)}; + auto result = call_submdspan_mapping(m, Traits::make_tuple(slices)); + auto padding_value = decltype(result.mapping)::padding_value; + VERIFY(padding_value == expected); + }; + + auto pad = [](int n, int m) -> size_t + { + constexpr auto padding_value = static_padding_value<Layout>; + if constexpr (padding_value != dyn) + { + auto npad = ((n + padding_value - 1) / padding_value) * padding_value; + return npad * m; + } + else + return dyn; + }; + + check(std::extents(std::cw<3>, std::cw<5>, std::cw<7>, 11, 13), pad(3, 5)); + check(std::extents(std::cw<3>, std::cw<5>, 7, 11, 13), pad(3, 5)); + check(std::extents(std::cw<3>, std::cw<6>, 7, 11, 13), pad(3, 6)); + check(std::extents(std::cw<3>, 5, 7, 11, 13), dyn); + check(std::extents(3, 5, 7, 11, 13), dyn); + return true; + } + +constexpr bool +test_layout_stride_return_types() +{ + auto exts = std::extents(3, 5); + auto m = std::layout_stride::mapping(exts, std::array{2, 12}); + + using index_type = decltype(exts)::index_type; + auto s1 = std::strided_slice{index_type(2), index_type(2), + std::cw<index_type(2)>}; + auto result = submdspan_mapping(m, index_type(1), s1); + using layout_type = decltype(result.mapping)::layout_type; + static_assert(std::same_as<layout_type, std::layout_stride>); + return true; +} + +template<typename Layout> + constexpr bool + test_return_types_all() + { + return true; + } + +template<typename Layout> + constexpr bool + test_return_types_unpadded_all() + { + test_layout_common_return_types<Layout>(); + static_assert(test_layout_common_return_types<Layout>()); + + test_layout_unpadded_return_types<Layout>(); + static_assert(test_layout_unpadded_return_types<Layout>()); + + test_layout_unpadded_padding_value<Layout>(); + static_assert(test_layout_unpadded_padding_value<Layout>()); + return true; + } + +template<typename Layout> + constexpr bool + test_return_types_padded_all() + { + test_layout_common_return_types<Layout>(); + static_assert(test_layout_common_return_types<Layout>()); + + test_layout_padded_return_types<Layout>(); + static_assert(test_layout_padded_return_types<Layout>()); + + test_layout_padded_padding_value<Layout>(); + static_assert(test_layout_padded_padding_value<Layout>()); + return true; + } + +int +main() +{ + test_return_types_unpadded_all<std::layout_left>(); + test_return_types_unpadded_all<std::layout_right>(); + + test_return_types_padded_all<std::layout_left_padded<1>>(); + test_return_types_padded_all<std::layout_left_padded<2>>(); + test_return_types_padded_all<std::layout_left_padded<dyn>>(); + + test_return_types_padded_all<std::layout_right_padded<1>>(); + test_return_types_padded_all<std::layout_right_padded<2>>(); + test_return_types_padded_all<std::layout_right_padded<dyn>>(); + + test_layout_stride_return_types(); + static_assert(test_layout_stride_return_types()); + return 0; +} + diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc new file mode 100644 index 0000000..39eb18c --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc @@ -0,0 +1,122 @@ +// { dg-do compile { target c++26 } } +#include <mdspan> + +#include <vector> + +template<typename Layout, typename... Slices> + constexpr bool + check_slice_range(Slices... slices) + { + auto m = typename Layout::mapping<std::extents<int, 3, 5, 7>>{}; + auto storage = std::vector<double>(m.required_span_size()); + auto md = std::mdspan(storage.data(), m); + + auto submd = submdspan(md, slices...); // { dg-error "expansion of" } + (void) submd; + return true; + } + +template<typename Layout> + constexpr bool + test_int_under() + { + check_slice_range<Layout>(1, -1, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_int_under<std::layout_left>()); // { dg-error "expansion of" } +static_assert(test_int_under<std::layout_right>()); // { dg-error "expansion of" } +static_assert(test_int_under<std::layout_stride>()); // { dg-error "expansion of" } + +template<typename Layout> + constexpr bool + test_int_over() + { + check_slice_range<Layout>(1, 5, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_int_over<std::layout_left>()); // { dg-error "expansion of" } +static_assert(test_int_over<std::layout_right>()); // { dg-error "expansion of" } +static_assert(test_int_over<std::layout_stride>()); // { dg-error "expansion of" } + +template<typename Layout> + constexpr bool + test_tuple_under() + { + check_slice_range<Layout>(1, std::tuple{-1, 2}, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_tuple_under<std::layout_left>()); // { dg-error "expansion of" } +static_assert(test_tuple_under<std::layout_right>()); // { dg-error "expansion of" } +static_assert(test_tuple_under<std::layout_stride>()); // { dg-error "expansion of" } + +template<typename Layout> + constexpr bool + test_tuple_reversed() + { + check_slice_range<Layout>(1, std::tuple{3, 2}, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_tuple_reversed<std::layout_left>()); // { dg-error "expansion of" } +static_assert(test_tuple_reversed<std::layout_right>()); // { dg-error "expansion of" } +static_assert(test_tuple_reversed<std::layout_stride>()); // { dg-error "expansion of" } + +template<typename Layout> + constexpr bool + test_tuple_over() + { + check_slice_range<Layout>(1, std::tuple{0, 6}, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_tuple_over<std::layout_left>()); // { dg-error "expansion of" } +static_assert(test_tuple_over<std::layout_right>()); // { dg-error "expansion of" } +static_assert(test_tuple_over<std::layout_stride>()); // { dg-error "expansion of" } + +template<typename Layout> + constexpr bool + test_strided_slice_zero() + { + check_slice_range<Layout>(1, std::strided_slice{1, 1, 0}, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_strided_slice_zero<std::layout_left>()); // { dg-error "expansion of" } +static_assert(test_strided_slice_zero<std::layout_right>()); // { dg-error "expansion of" } +static_assert(test_strided_slice_zero<std::layout_stride>()); // { dg-error "expansion of" } + +template<typename Layout> + constexpr bool + test_strided_slice_offset_under() + { + check_slice_range<Layout>(1, std::strided_slice{-1, 1, 1}, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_strided_slice_offset_under<std::layout_left>()); // { dg-error "expansion of" } +static_assert(test_strided_slice_offset_under<std::layout_right>()); // { dg-error "expansion of" } +static_assert(test_strided_slice_offset_under<std::layout_stride>()); // { dg-error "expansion of" } + +template<typename Layout> + constexpr bool + test_strided_slice_offset_over() + { + check_slice_range<Layout>(1, std::strided_slice{6, 0, 1}, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_strided_slice_offset_over<std::layout_left>()); // { dg-error "expansion of" } +static_assert(test_strided_slice_offset_over<std::layout_right>()); // { dg-error "expansion of" } +static_assert(test_strided_slice_offset_over<std::layout_stride>()); // { dg-error "expansion of" } + +template<typename Layout> + constexpr bool + test_strided_slice_extent_over() + { + check_slice_range<Layout>(1, std::strided_slice{1, 5, 1}, 2); // { dg-error "expansion of" } + return true; + } +static_assert(test_strided_slice_extent_over<std::layout_left>()); // { dg-error "expansion of" } +static_assert(test_strided_slice_extent_over<std::layout_right>()); // { dg-error "expansion of" } +static_assert(test_strided_slice_extent_over<std::layout_stride>()); // { dg-error "expansion of" } + +// { dg-prune-output "static assertion failed" } +// { dg-prune-output "__glibcxx_assert_fail" } +// { dg-prune-output "non-constant condition" } +// { dg-prune-output "no matching function" } +// { dg-prune-output "does not satisfy placeholder constraints" } diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/version.cc b/libstdc++-v3/testsuite/23_containers/mdspan/version.cc new file mode 100644 index 0000000..1882600 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/version.cc @@ -0,0 +1,20 @@ +// { dg-do preprocess { target c++23 } } +// { dg-add-options no_pch } + +#include <mdspan> + +#ifndef __cpp_lib_mdspan +#error "Feature test macro __cpp_lib_mdspan is missing for <mdspan>" +#elif __cplusplus <= 202302L && __cpp_lib_mdspan != 202207L +#error "Feature test macro __cpp_lib_mdspan has the wrong value for C++23" +#elif __cplusplus > 202302L && __cpp_lib_mdspan != 202406L +#error "Feature test macro __cpp_lib_mdspan has the wrong value for C++26" +#endif + +#if __cplusplus > 202302L +#ifndef __cpp_lib_aligned_accessor +#error "Feature test macro __cpp_lib_aligned_accessor is missing for <mdspan>" +#elif __cpp_lib_aligned_accessor != 202411L +#error "Feature test macro __cpp_lib_aligned_accessor has the wrong value" +#endif +#endif diff --git a/libstdc++-v3/testsuite/23_containers/multimap/48101_neg.cc b/libstdc++-v3/testsuite/23_containers/multimap/48101_neg.cc index f597ef3..1c1c79d 100644 --- a/libstdc++-v3/testsuite/23_containers/multimap/48101_neg.cc +++ b/libstdc++-v3/testsuite/23_containers/multimap/48101_neg.cc @@ -28,8 +28,8 @@ test01() c2.find(2); // { dg-error "here" } } -// { dg-error "_Compare = std::(__8::)?less<int.>" "" { target *-*-* } 0 } -// { dg-error "_Compare = std::(__8::)?allocator<int>" "" { target *-*-* } 0 } +// { dg-error "_Compare = std::less<int.>" "" { target *-*-* } 0 } +// { dg-error "_Compare = std::allocator<int>" "" { target *-*-* } 0 } // { dg-error "comparison object must be invocable" "" { target *-*-* } 0 } // { dg-prune-output "no match for call" } // { dg-prune-output "invalid conversion" } diff --git a/libstdc++-v3/testsuite/23_containers/multimap/cons/deduction.cc b/libstdc++-v3/testsuite/23_containers/multimap/cons/deduction.cc index f0699e2..de5e6ac 100644 --- a/libstdc++-v3/testsuite/23_containers/multimap/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/multimap/cons/deduction.cc @@ -210,3 +210,49 @@ test_p1518r2() std::multimap s2(std::move(m), p); check_type<MMap>(s2); } + +struct MyPred +{ + template<typename T, typename U> + bool operator()(T const&, U const&) const; +}; + +template<typename K, typename V> +constexpr bool test_lwg4223() +{ + using KD = std::remove_cv_t<std::remove_reference_t<K>>; + using VD = std::remove_cv_t<std::remove_reference_t<V>>; + using Alloc = __gnu_test::SimpleAllocator<std::pair<const KD, VD>>; + + std::initializer_list<std::pair<K, V>> il = {}; + Alloc a; + MyPred p; + + // The remove_cvref_t is not applied here. + // static_assert(std::is_same_v< + // decltype(std::multimap(il)), + // std::multimap<KD, VD>>); + + static_assert(std::is_same_v< + decltype(std::multimap(il.begin(), il.end())), + std::multimap<KD, VD>>); + + static_assert(std::is_same_v< + decltype(std::multimap(il.begin(), il.end(), p)), + std::multimap<KD, VD, MyPred>>); + + static_assert(std::is_same_v< + decltype(std::multimap(il.begin(), il.end(), a)), + std::multimap<KD, VD, std::less<KD>, Alloc>>); + + static_assert(std::is_same_v< + decltype(std::multimap(il.begin(), il.end(), p, a)), + std::multimap<KD, VD, MyPred, Alloc>>); + + return true; +} + +static_assert(test_lwg4223<const int, const float>()); +static_assert(test_lwg4223<int&, float&>()); +static_assert(test_lwg4223<int&&, float&&>()); +static_assert(test_lwg4223<const int&, const float&>()); diff --git a/libstdc++-v3/testsuite/23_containers/multimap/cons/from_range.cc b/libstdc++-v3/testsuite/23_containers/multimap/cons/from_range.cc index 4a8ea9f..bb79720 100644 --- a/libstdc++-v3/testsuite/23_containers/multimap/cons/from_range.cc +++ b/libstdc++-v3/testsuite/23_containers/multimap/cons/from_range.cc @@ -43,11 +43,11 @@ test_deduction_guide() __gnu_test::test_input_range<std::pair<const long, const float>> r2(0, 0); std::multimap m5(std::from_range, r2); - static_assert(std::is_same_v<decltype(m5), std::multimap<long, const float>>); + static_assert(std::is_same_v<decltype(m5), std::multimap<long, float>>); - // LWG4223: deduces multimap<const long&, float&> - //__gnu_test::test_input_range<std::pair<const long&, float&>> r3(0, 0); - // std::multimap m6(std::from_range, r3); + __gnu_test::test_input_range<std::pair<const long&, float&>> r3(0, 0); + std::multimap m6(std::from_range, r3); + static_assert(std::is_same_v<decltype(m6), std::multimap<long, float>>); __gnu_test::test_input_range<std::tuple<long, float>> r4(0, 0); std::multimap m7(std::from_range, r4); @@ -61,7 +61,7 @@ constexpr bool is_equal(std::less<T>, std::less<U>) { return true; } constexpr bool is_equal(StateCmp lhs, StateCmp rhs) -{ return lhs.state = rhs.state; } +{ return lhs.state == rhs.state; } constexpr auto get0 = [](auto const& t) { using std::get; @@ -103,12 +103,12 @@ do_test(Alloc alloc, Cmp cmp) std::multimap<K, V, Cmp, Alloc> m4(std::from_range, Range(a, a+4), cmp); VERIFY( eq(m4, {a, 4}) ); VERIFY( m4.get_allocator() == Alloc() ); - VERIFY( is_equal(m4.key_comp(), Cmp()) ); + VERIFY( is_equal(m4.key_comp(), cmp) ); std::multimap<K, V, Cmp, Alloc> m9(std::from_range, Range(a, a+9), alloc); VERIFY( eq(m9, {a, 9}) ); VERIFY( m9.get_allocator() == alloc ); - VERIFY( is_equal(m9.key_comp(), cmp) ); + VERIFY( is_equal(m9.key_comp(), Cmp()) ); std::multimap<K, V, Cmp, Alloc> mr(std::from_range, Range(a, a+14), cmp, alloc); VERIFY( eq(mr, {a, 14}) ); diff --git a/libstdc++-v3/testsuite/23_containers/multiset/48101_neg.cc b/libstdc++-v3/testsuite/23_containers/multiset/48101_neg.cc index f0786cf..3cc0658 100644 --- a/libstdc++-v3/testsuite/23_containers/multiset/48101_neg.cc +++ b/libstdc++-v3/testsuite/23_containers/multiset/48101_neg.cc @@ -29,7 +29,6 @@ test01() // { dg-error "non-const, non-volatile value_type" "" { target *-*-* } 0 } // { dg-error "comparison object must be invocable" "" { target *-*-* } 0 } -// { dg-prune-output "std::allocator<.* has no member named " } // { dg-prune-output "must have the same value_type as its allocator" } // { dg-prune-output "no match for call" } // { dg-prune-output "invalid conversion" } diff --git a/libstdc++-v3/testsuite/23_containers/multiset/cons/from_range.cc b/libstdc++-v3/testsuite/23_containers/multiset/cons/from_range.cc index cdba7eb..aa9be9c 100644 --- a/libstdc++-v3/testsuite/23_containers/multiset/cons/from_range.cc +++ b/libstdc++-v3/testsuite/23_containers/multiset/cons/from_range.cc @@ -48,7 +48,7 @@ constexpr bool is_equal(std::less<T>, std::less<U>) { return true; } constexpr bool is_equal(StateCmp lhs, StateCmp rhs) -{ return lhs.state = rhs.state; } +{ return lhs.state == rhs.state; } template<typename Range, typename Alloc, typename Cmp> constexpr void @@ -82,12 +82,12 @@ do_test(Alloc alloc, Cmp cmp) std::set<V, Cmp, Alloc> s4(std::from_range, Range(a, a+4), cmp); VERIFY( eq(s4, {a, 4}) ); VERIFY( s4.get_allocator() == Alloc() ); - VERIFY( is_equal(s4.key_comp(), Cmp()) ); + VERIFY( is_equal(s4.key_comp(), cmp) ); std::set<V, Cmp, Alloc> s9(std::from_range, Range(a, a+9), alloc); VERIFY( eq(s9, {a, 9}) ); VERIFY( s9.get_allocator() == alloc ); - VERIFY( is_equal(s9.key_comp(), cmp) ); + VERIFY( is_equal(s9.key_comp(), Cmp()) ); std::set<V, Cmp, Alloc> sr(std::from_range, Range(a, a+14), cmp, alloc); VERIFY( eq(sr, {a, 9}) ); diff --git a/libstdc++-v3/testsuite/23_containers/set/48101_neg.cc b/libstdc++-v3/testsuite/23_containers/set/48101_neg.cc index e8dec72..fe38d1a 100644 --- a/libstdc++-v3/testsuite/23_containers/set/48101_neg.cc +++ b/libstdc++-v3/testsuite/23_containers/set/48101_neg.cc @@ -29,7 +29,6 @@ test01() // { dg-error "non-const, non-volatile value_type" "" { target *-*-* } 0 } // { dg-error "comparison object must be invocable" "" { target *-*-* } 0 } -// { dg-prune-output "std::allocator<.* has no member named " } // { dg-prune-output "must have the same value_type as its allocator" } // { dg-prune-output "no match for call" } // { dg-prune-output "invalid conversion" } diff --git a/libstdc++-v3/testsuite/23_containers/set/cons/from_range.cc b/libstdc++-v3/testsuite/23_containers/set/cons/from_range.cc index efde05d..23922bf 100644 --- a/libstdc++-v3/testsuite/23_containers/set/cons/from_range.cc +++ b/libstdc++-v3/testsuite/23_containers/set/cons/from_range.cc @@ -47,7 +47,7 @@ constexpr bool is_equal(std::less<T>, std::less<U>) { return true; } constexpr bool is_equal(StateCmp lhs, StateCmp rhs) -{ return lhs.state = rhs.state; } +{ return lhs.state == rhs.state; } template<typename Range, typename Alloc, typename Cmp> constexpr void @@ -81,12 +81,12 @@ do_test(Alloc alloc, Cmp cmp) std::set<V, Cmp, Alloc> s4(std::from_range, Range(a, a+4), cmp); VERIFY( eq(s4, {a, 4}) ); VERIFY( s4.get_allocator() == Alloc() ); - VERIFY( is_equal(s4.key_comp(), Cmp()) ); + VERIFY( is_equal(s4.key_comp(), cmp) ); std::set<V, Cmp, Alloc> s9(std::from_range, Range(a, a+9), alloc); VERIFY( eq(s9, {a, 9}) ); VERIFY( s9.get_allocator() == alloc ); - VERIFY( is_equal(s9.key_comp(), cmp) ); + VERIFY( is_equal(s9.key_comp(), Cmp()) ); std::set<V, Cmp, Alloc> sr(std::from_range, Range(a, a+14), cmp, alloc); VERIFY( eq(sr, {a, 9}) ); diff --git a/libstdc++-v3/testsuite/23_containers/span/120997.cc b/libstdc++-v3/testsuite/23_containers/span/120997.cc new file mode 100644 index 0000000..fbf194c --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/span/120997.cc @@ -0,0 +1,46 @@ +// { dg-do run { target c++26 } } + +#include <span> +#include <testsuite_hooks.h> + +void +test_first() +{ + bool arr[5]; + std::span<const bool> s(arr); + std::span<const bool> s2 = s.first(5); + VERIFY( s2.data() == s.data() ); + std::span<const bool> s3 = s.first<5>(); + VERIFY( s3.data() == s.data() ); +} + +void +test_last() +{ + bool arr[5]; + std::span<const bool> s(arr); + std::span<const bool> s2 = s.last(5); + VERIFY( s2.data() == s.data() ); + std::span<const bool> s3 = s.last<5>(); + VERIFY( s3.data() == s.data() ); +} + +void +test_subspan() +{ + bool arr[5]; + std::span<const bool> s(arr); + std::span<const bool> s2 = s.subspan(0, 5); + VERIFY( s2.data() == s.data() ); + std::span<const bool> s3 = s.subspan<0>(); + VERIFY( s3.data() == s.data() ); + std::span<const bool> s4 = s.subspan<0, 5>(); + VERIFY( s4.data() == s.data() ); +} + +int main() +{ + test_first(); + test_last(); + test_subspan(); +} diff --git a/libstdc++-v3/testsuite/23_containers/span/contiguous_range_neg.cc b/libstdc++-v3/testsuite/23_containers/span/contiguous_range_neg.cc index c9e9112..890fdf8 100644 --- a/libstdc++-v3/testsuite/23_containers/span/contiguous_range_neg.cc +++ b/libstdc++-v3/testsuite/23_containers/span/contiguous_range_neg.cc @@ -25,6 +25,7 @@ main() { std::deque<int> d{}; std::span<int, std::dynamic_extent> myspan(d); // { dg-error "no match" } + (void) myspan; } // { dg-prune-output "data" } diff --git a/libstdc++-v3/testsuite/23_containers/span/deduction.cc b/libstdc++-v3/testsuite/23_containers/span/deduction.cc index dce6ced..55a5862 100644 --- a/libstdc++-v3/testsuite/23_containers/span/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/span/deduction.cc @@ -80,4 +80,25 @@ test01() std::span s12(const_cast<const std::span<int>&>(s5)); static_assert( is_dynamic_span<int>(s12) ); + + std::span s13(a.data(), std::integral_constant<size_t, 3>{}); + static_assert( is_static_span<long, 3>(s13) ); + + std::span s14(a.data(), true); + static_assert( is_dynamic_span<long>(s14) ); + + std::span s15(a.data(), std::true_type{}); + static_assert( is_dynamic_span<long>(s15) ); + +#if __glibcxx_constant_wrapper + auto c5 = std::constant_wrapper<5>{}; + std::span s16(a.data(), c5); + static_assert( is_static_span<long, 5>(s16) ); + + std::span s17(a.data(), std::cw<4>); + static_assert( is_static_span<long, 4>(s17) ); + + std::span s18(a.data(), std::cw<true>); + static_assert( is_dynamic_span<long>(s18) ); +#endif } diff --git a/libstdc++-v3/testsuite/23_containers/tuple/comparison_operators/119721.cc b/libstdc++-v3/testsuite/23_containers/tuple/comparison_operators/119721.cc new file mode 100644 index 0000000..cad7ca0 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/tuple/comparison_operators/119721.cc @@ -0,0 +1,65 @@ +// { dg-do compile { target c++23 } } +// { dg-options "-std=c++23" } + +// Test for PR libstdc++/119721: tuple<> comparison with array<T, 0> + +#include <tuple> +#include <array> +#include <testsuite_hooks.h> +#include <cassert> + +constexpr void +test01() +{ + std::tuple<> t; + std::array<int, 0> a; + + // Basic comparison should work + VERIFY( t == a ); + VERIFY( a == t ); + VERIFY( !(t != a) ); + VERIFY( !(a != t) ); + + // Ordering comparisons should be equal + VERIFY( !(t < a) ); + VERIFY( !(t > a) ); + VERIFY( t <= a ); + VERIFY( t >= a ); + VERIFY( !(a < t) ); + VERIFY( !(a > t) ); + VERIFY( a <= t ); + VERIFY( a >= t ); + + // Three-way comparison should return equal + VERIFY( (t <=> a) == std::strong_ordering::equal ); + VERIFY( (a <=> t) == std::strong_ordering::equal ); +} + +constexpr void +test02() +{ + // Test with non-comparable element type + struct NonComparable { + void operator==(const NonComparable&) const = delete; + void operator<=>(const NonComparable&) const = delete; + }; + + std::tuple<> t; + std::array<NonComparable, 0> a; + + // Should still work because empty containers don't compare elements + VERIFY( t == a ); + VERIFY( (t <=> a) == std::strong_ordering::equal ); +} + +int main() +{ + auto test_all = [] { + test01(); + test02(); + }; + + test_all(); + static_VERIFY( test_all() ); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/tuple/cons/119721.cc b/libstdc++-v3/testsuite/23_containers/tuple/cons/119721.cc new file mode 100644 index 0000000..1d15238 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/tuple/cons/119721.cc @@ -0,0 +1,121 @@ +// { dg-do run { target c++23 } } + +// Test for PR libstdc++/119721: tuple<> construction/assignment with array<T, 0> + +#include <tuple> +#include <array> +#include <memory> +#include <testsuite_hooks.h> + +constexpr void +test01() +{ + std::array<int, 0> a{}; + + // Constructor from array<int, 0> + std::tuple<> t1(a); + std::tuple<> t2(std::move(a)); + + // Assignment from array<int, 0> + std::tuple<> t3; + t3 = a; + t3 = std::move(a); + + VERIFY( t1 == a ); + VERIFY( t2 == a ); + VERIFY( t3 == a ); +} + +constexpr void +test02() +{ + // Test with non-comparable element type + struct NonComparable + { + void operator==(const NonComparable&) const = delete; + void operator<=>(const NonComparable&) const = delete; + }; + + std::array<NonComparable, 0> a{}; + + std::tuple<> t1(a); + std::tuple<> t2(std::move(a)); + + std::tuple<> t3; + t3 = a; + t3 = std::move(a); + + VERIFY( t1 == a ); +} + +constexpr void +test03() +{ + // Test assignment return type (non-const assignment) + std::tuple<> t, u; + std::tuple<>& r1 = (t = u); + VERIFY( &r1 == &t ); + + std::tuple<>& r2 = (t = {}); + VERIFY( &r2 == &t ); + + std::array<int, 0> a{}; + std::tuple<>& r3 = (t = a); + VERIFY( &r3 == &t ); +} + +constexpr void +test04() +{ + std::array<int, 0> a{}; + const std::tuple<> t1; + + // Const assignment from array + std::tuple<> t2; + const std::tuple<>& r1 = (t1 = t2); + VERIFY( &r1 == &t1 ); + const std::tuple<>& r2 = (t1 = std::move(t2)); + VERIFY( &r2 == &t1 ); + + const std::tuple<>& r3 = (t1 = {}); + VERIFY( &r3 == &t1 ); + + // Const assignment from array + const std::tuple<>& r4 = (t1 = a); + VERIFY( &r4 == &t1 ); + const std::tuple<>& r5 = (t1 = std::move(a)); + VERIFY( &r5 == &t1 ); +} + +void +test05() +{ + std::array<int, 0> a{}; + std::allocator<int> alloc; + + // Allocator constructor from array + std::tuple<> t1(std::allocator_arg, alloc, a); + std::tuple<> t2(std::allocator_arg, alloc, std::move(a)); + + VERIFY( t1 == a ); + VERIFY( t2 == a ); +} + +int main() +{ + auto test_all = [] { + test01(); + test02(); + test03(); + test04(); + return true; + }; + + test_all(); + static_assert( test_all() ); + + // allocator test is not constexpr + test05(); + return 0; +} + diff --git a/libstdc++-v3/testsuite/23_containers/unordered_map/96088.cc b/libstdc++-v3/testsuite/23_containers/unordered_map/96088.cc index c7dfd4f..0ec0bba 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_map/96088.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_map/96088.cc @@ -1,6 +1,6 @@ // { dg-do run { target c++17 } } // { dg-require-effective-target std_allocator_new } -// { dg-xfail-run-if "AIX operator new" { powerpc-ibm-aix* } } +// { dg-require-effective-target c++20 { target powerpc-ibm-aix* } } // Copyright (C) 2021-2025 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/23_containers/unordered_map/cons/66055.cc b/libstdc++-v3/testsuite/23_containers/unordered_map/cons/66055.cc index c7a12c1..0f95976 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_map/cons/66055.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_map/cons/66055.cc @@ -27,7 +27,10 @@ using alloc_type = test_type::allocator_type; test_type h1(10, alloc_type()); test_type h2(10, hasher_type(), alloc_type()); -test_type h3(h1.begin(), h1.end(), 10, alloc_type()); -test_type h4(h1.begin(), h1.end(), 10, hasher_type(), alloc_type()); -test_type h5({ { 1, 1 } }, 10, alloc_type()); -test_type h6({ { 1, 1 } }, 10, hasher_type(), alloc_type()); +test_type h3(h1.begin(), h1.end(), alloc_type()); +test_type h4(h1.begin(), h1.end(), 10, alloc_type()); +test_type h5(h1.begin(), h1.end(), 10, hasher_type(), alloc_type()); +test_type h6({ { 1, 1 } }, alloc_type()); +test_type h7({ { 1, 1 } }, 10, alloc_type()); +test_type h8({ { 1, 1 } }, 10, hasher_type(), alloc_type()); + diff --git a/libstdc++-v3/testsuite/23_containers/unordered_map/cons/deduction.cc b/libstdc++-v3/testsuite/23_containers/unordered_map/cons/deduction.cc index 8b69af8..26013da 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_map/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_map/cons/deduction.cc @@ -16,12 +16,28 @@ static_assert(std::is_same_v< std::unordered_map<int, double>>); static_assert(std::is_same_v< + decltype(std::unordered_map{{std::pair{1, 2.0}, + {2, 3.0}, {3, 4.0}}, + SimpleAllocator<std::pair<const int, double>>{}}), + std::unordered_map<int, double, std::hash<int>, + std::equal_to<int>, + SimpleAllocator<std::pair<const int, double>>>>); + +static_assert(std::is_same_v< decltype(std::unordered_map{ {std::pair{1, 2.0}, {2, 3.0}, {3, 4.0}}, 1}), std::unordered_map<int, double>>); static_assert(std::is_same_v< + decltype(std::unordered_map{ + {std::pair{1, 2.0}, {2, 3.0}, {3, 4.0}}, + 1, SimpleAllocator<std::pair<const int, double>>{}}), + std::unordered_map<int, double, std::hash<int>, + std::equal_to<int>, + SimpleAllocator<std::pair<const int, double>>>>); + +static_assert(std::is_same_v< decltype(std::unordered_map{{std::pair{1, 2.0}, {2, 3.0}, {3, 4.0}}, {}, std::hash<int>{}, std::equal_to<int>{}}), @@ -98,6 +114,18 @@ void f() static_assert(std::is_same_v< decltype(std::unordered_map{x.begin(), x.end(), + std::allocator<std::pair<const int, double>>{}}), + std::unordered_map<int, double>>); + + static_assert(std::is_same_v< + decltype(std::unordered_map{x.begin(), x.end(), + SimpleAllocator<std::pair<const int, double>>{}}), + std::unordered_map<int, double, std::hash<int>, + std::equal_to<int>, + SimpleAllocator<std::pair<const int, double>>>>); + + static_assert(std::is_same_v< + decltype(std::unordered_map{x.begin(), x.end(), 1, std::hash<int>{}, std::allocator<std::pair<const int, double>>{}}), std::unordered_map<int, double>>); @@ -162,3 +190,72 @@ test_p1518r2() std::unordered_map s2(std::move(m), p); check_type<UMap>(s2); } + +struct MyHash +{ + template<typename T> + std::size_t operator()(T const&) const; +}; + +struct MyPred +{ + template<typename T, typename U> + bool operator()(T const&, U const&) const; +}; + +template<typename K, typename V> +constexpr bool test_lwg4223() +{ + using KD = std::remove_cv_t<std::remove_reference_t<K>>; + using VD = std::remove_cv_t<std::remove_reference_t<V>>; + using Alloc = __gnu_test::SimpleAllocator<std::pair<const KD, VD>>; + + std::initializer_list<std::pair<K, V>> il = {}; + Alloc a; + MyHash h; + MyPred p; + + // The remove_cvref_t is not applied here. + // static_assert(std::is_same_v< + // decltype(std::unordered_map(il)), + // std::unordered_map<KD, VD>>); + + static_assert(std::is_same_v< + decltype(std::unordered_map(il.begin(), il.end())), + std::unordered_map<KD, VD>>); + + static_assert(std::is_same_v< + decltype(std::unordered_map(il.begin(), il.end(), 0)), + std::unordered_map<KD, VD>>); + + static_assert(std::is_same_v< + decltype(std::unordered_map(il.begin(), il.end(), 0, h)), + std::unordered_map<KD, VD, MyHash>>); + + static_assert(std::is_same_v< + decltype(std::unordered_map(il.begin(), il.end(), 0, h, p)), + std::unordered_map<KD, VD, MyHash, MyPred>>); + + static_assert(std::is_same_v< + decltype(std::unordered_map(il.begin(), il.end(), a)), + std::unordered_map<KD, VD, std::hash<KD>, std::equal_to<KD>, Alloc>>); + + static_assert(std::is_same_v< + decltype(std::unordered_map(il.begin(), il.end(), 0, a)), + std::unordered_map<KD, VD, std::hash<KD>, std::equal_to<KD>, Alloc>>); + + static_assert(std::is_same_v< + decltype(std::unordered_map(il.begin(), il.end(), 0, h, a)), + std::unordered_map<KD, VD, MyHash, std::equal_to<KD>, Alloc>>); + + static_assert(std::is_same_v< + decltype(std::unordered_map(il.begin(), il.end(), 0, h, p, a)), + std::unordered_map<KD, VD, MyHash, MyPred, Alloc>>); + + return true; +} + +static_assert(test_lwg4223<const int, const float>()); +static_assert(test_lwg4223<int&, float&>()); +static_assert(test_lwg4223<int&&, float&&>()); +static_assert(test_lwg4223<const int&, const float&>()); diff --git a/libstdc++-v3/testsuite/23_containers/unordered_map/cons/from_range.cc b/libstdc++-v3/testsuite/23_containers/unordered_map/cons/from_range.cc index 36efc2d..cad102e 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_map/cons/from_range.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_map/cons/from_range.cc @@ -78,13 +78,11 @@ test_deduction_guide() __gnu_test::test_input_range<std::pair<const long, const float>> r2(0, 0); std::unordered_map m9(std::from_range, r2); - static_assert(std::is_same_v< - decltype(m9), - std::unordered_map<long, const float>>); + static_assert(std::is_same_v<decltype(m9), std::unordered_map<long, float>>); - // LWG4223: deduces map<const long&, float&> - // __gnu_test::test_input_range<std::pair<const long&, float&>> r3(0, 0); - // std::unordered_map m10(std::from_range, r3); + __gnu_test::test_input_range<std::pair<const long&, float&>> r3(0, 0); + std::unordered_map m10(std::from_range, r3); + static_assert(std::is_same_v<decltype(m10), std::unordered_map<long, float>>); __gnu_test::test_input_range<std::tuple<long, float>> r4(0, 0); std::unordered_map m11(std::from_range, r4); @@ -102,10 +100,10 @@ constexpr bool is_equal(std::equal_to<T>, std::equal_to<U>) { return true; } constexpr bool is_equal(StateHash lhs, StateHash rhs) -{ return lhs.state = rhs.state; } +{ return lhs.state == rhs.state; } constexpr bool is_equal(StateEq lhs, StateEq rhs) -{ return lhs.state = rhs.state; } +{ return lhs.state == rhs.state; } template<typename Range, typename Alloc, typename Hash, typename Equal> constexpr void diff --git a/libstdc++-v3/testsuite/23_containers/unordered_map/const_container.cc b/libstdc++-v3/testsuite/23_containers/unordered_map/const_container.cc new file mode 100644 index 0000000..e62f158 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/unordered_map/const_container.cc @@ -0,0 +1,19 @@ +// { dg-do compile { target c++11 } } + +#include <unordered_map> + +#include <testsuite_hooks.h> + +// PR c++/116369 +const std::unordered_map<int, int> um + { + { 0, 1 }, + { 2, 3 }, + { 4, 5 } + }; + +int main() +{ + VERIFY( um.size() == 3 ); + VERIFY( um.find(0) != um.end() ); +} diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multimap/96088.cc b/libstdc++-v3/testsuite/23_containers/unordered_multimap/96088.cc index 6f94296..3c1de37 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_multimap/96088.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_multimap/96088.cc @@ -1,6 +1,6 @@ // { dg-do run { target c++17 } } // { dg-require-effective-target std_allocator_new } -// { dg-xfail-run-if "AIX operator new" { powerpc-ibm-aix* } } +// { dg-require-effective-target c++20 { target powerpc-ibm-aix* } } // Copyright (C) 2021-2025 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multimap/cons/66055.cc b/libstdc++-v3/testsuite/23_containers/unordered_multimap/cons/66055.cc index dc0a651..eecc600 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_multimap/cons/66055.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_multimap/cons/66055.cc @@ -27,7 +27,9 @@ using alloc_type = test_type::allocator_type; test_type h1(10, alloc_type()); test_type h2(10, hasher_type(), alloc_type()); -test_type h3(h1.begin(), h1.end(), 10, alloc_type()); -test_type h4(h1.begin(), h1.end(), 10, hasher_type(), alloc_type()); -test_type h5({ { 1, 1 } }, 10, alloc_type()); -test_type h6({ { 1, 1 } }, 10, hasher_type(), alloc_type()); +test_type h3(h1.begin(), h1.end(), alloc_type()); +test_type h4(h1.begin(), h1.end(), 10, alloc_type()); +test_type h5(h1.begin(), h1.end(), 10, hasher_type(), alloc_type()); +test_type h6({ { 1, 1 } }, alloc_type()); +test_type h7({ { 1, 1 } }, 10, alloc_type()); +test_type h8({ { 1, 1 } }, 10, hasher_type(), alloc_type()); diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multimap/cons/deduction.cc b/libstdc++-v3/testsuite/23_containers/unordered_multimap/cons/deduction.cc index e7e535b..1db58a0 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_multimap/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_multimap/cons/deduction.cc @@ -16,6 +16,28 @@ static_assert(std::is_same_v< std::unordered_multimap<int, double>>); static_assert(std::is_same_v< + decltype(std::unordered_multimap{ + {std::pair{1, 2.0}, {2, 3.0}, {3, 4.0}}, + SimpleAllocator<std::pair<const int, double>>{}}), + std::unordered_multimap<int, double, std::hash<int>, + std::equal_to<int>, + SimpleAllocator<std::pair<const int, double>>>>); + +static_assert(std::is_same_v< + decltype(std::unordered_multimap{ + {std::pair{1, 2.0}, {2, 3.0}, {3, 4.0}}, + 1}), + std::unordered_multimap<int, double>>); + +static_assert(std::is_same_v< + decltype(std::unordered_multimap{ + {std::pair{1, 2.0}, {2, 3.0}, {3, 4.0}}, + 1, SimpleAllocator<std::pair<const int, double>>{}}), + std::unordered_multimap<int, double, std::hash<int>, + std::equal_to<int>, + SimpleAllocator<std::pair<const int, double>>>>); + +static_assert(std::is_same_v< decltype(std::unordered_multimap{{std::pair{1, 2.0}, {2, 3.0}, {3, 4.0}}, {}, std::hash<int>{}, std::equal_to<int>{}}), @@ -107,6 +129,18 @@ void f() static_assert(std::is_same_v< decltype(std::unordered_multimap{x.begin(), x.end(), + std::allocator<std::pair<const int, double>>{}}), + std::unordered_multimap<int, double>>); + + static_assert(std::is_same_v< + decltype(std::unordered_multimap{x.begin(), x.end(), + SimpleAllocator<std::pair<const int, double>>{}}), + std::unordered_multimap<int, double, std::hash<int>, + std::equal_to<int>, + SimpleAllocator<std::pair<const int, double>>>>); + + static_assert(std::is_same_v< + decltype(std::unordered_multimap{x.begin(), x.end(), 1, std::hash<int>{}, std::allocator<std::pair<const int, double>>{}}), std::unordered_multimap<int, double>>); @@ -171,3 +205,72 @@ test_p1518r2() std::unordered_multimap s2(std::move(m), p); check_type<UMMap>(s2); } + +struct MyHash +{ + template<typename T> + std::size_t operator()(T const&) const; +}; + +struct MyPred +{ + template<typename T, typename U> + bool operator()(T const&, U const&) const; +}; + +template<typename K, typename V> +constexpr bool test_lwg4223() +{ + using KD = std::remove_cv_t<std::remove_reference_t<K>>; + using VD = std::remove_cv_t<std::remove_reference_t<V>>; + using Alloc = __gnu_test::SimpleAllocator<std::pair<const KD, VD>>; + + std::initializer_list<std::pair<K, V>> il = {}; + Alloc a; + MyHash h; + MyPred p; + + // The remove_cvref_t is not applied here. + // static_assert(std::is_same_v< + // decltype(std::unordered_multimap(il)), + // std::unordered_multimap<KD, VD>>); + + static_assert(std::is_same_v< + decltype(std::unordered_multimap(il.begin(), il.end())), + std::unordered_multimap<KD, VD>>); + + static_assert(std::is_same_v< + decltype(std::unordered_multimap(il.begin(), il.end(), 0)), + std::unordered_multimap<KD, VD>>); + + static_assert(std::is_same_v< + decltype(std::unordered_multimap(il.begin(), il.end(), 0, h)), + std::unordered_multimap<KD, VD, MyHash>>); + + static_assert(std::is_same_v< + decltype(std::unordered_multimap(il.begin(), il.end(), 0, h, p)), + std::unordered_multimap<KD, VD, MyHash, MyPred>>); + + static_assert(std::is_same_v< + decltype(std::unordered_multimap(il.begin(), il.end(), a)), + std::unordered_multimap<KD, VD, std::hash<KD>, std::equal_to<KD>, Alloc>>); + + static_assert(std::is_same_v< + decltype(std::unordered_multimap(il.begin(), il.end(), 0, a)), + std::unordered_multimap<KD, VD, std::hash<KD>, std::equal_to<KD>, Alloc>>); + + static_assert(std::is_same_v< + decltype(std::unordered_multimap(il.begin(), il.end(), 0, h, a)), + std::unordered_multimap<KD, VD, MyHash, std::equal_to<KD>, Alloc>>); + + static_assert(std::is_same_v< + decltype(std::unordered_multimap(il.begin(), il.end(), 0, h, p, a)), + std::unordered_multimap<KD, VD, MyHash, MyPred, Alloc>>); + + return true; +} + +static_assert(test_lwg4223<const int, const float>()); +static_assert(test_lwg4223<int&, float&>()); +static_assert(test_lwg4223<int&&, float&&>()); +static_assert(test_lwg4223<const int&, const float&>()); diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multimap/cons/from_range.cc b/libstdc++-v3/testsuite/23_containers/unordered_multimap/cons/from_range.cc index b551df4..ab30e3c 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_multimap/cons/from_range.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_multimap/cons/from_range.cc @@ -86,11 +86,13 @@ test_deduction_guide() std::unordered_multimap m9(std::from_range, r2); static_assert(std::is_same_v< decltype(m9), - std::unordered_multimap<long, const float>>); + std::unordered_multimap<long, float>>); - // LWG4223: deduces map<const long&, float&> - // __gnu_test::test_input_range<std::pair<const long&, float&>> r3(0, 0); - // std::unordered_multimap m10(std::from_range, r3); + __gnu_test::test_input_range<std::pair<const long&, float&>> r3(0, 0); + std::unordered_multimap m10(std::from_range, r3); + static_assert(std::is_same_v< + decltype(m10), + std::unordered_multimap<long, float>>); __gnu_test::test_input_range<std::tuple<long, float>> r4(0, 0); std::unordered_multimap m11(std::from_range, r4); @@ -112,10 +114,10 @@ constexpr bool is_equal(std::equal_to<T>, std::equal_to<U>) { return true; } constexpr bool is_equal(StateHash lhs, StateHash rhs) -{ return lhs.state = rhs.state; } +{ return lhs.state == rhs.state; } constexpr bool is_equal(StateEq lhs, StateEq rhs) -{ return lhs.state = rhs.state; } +{ return lhs.state == rhs.state; } template<typename Range, typename Alloc, typename Hash, typename Equal> constexpr void diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multimap/const_container.cc b/libstdc++-v3/testsuite/23_containers/unordered_multimap/const_container.cc new file mode 100644 index 0000000..3da1e33 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/unordered_multimap/const_container.cc @@ -0,0 +1,22 @@ +// { dg-do compile { target c++11 } } + +#include <unordered_map> + +#include <testsuite_hooks.h> + +// PR c++/116369 +const std::unordered_multimap<int, int> umm + { + { 0, 1 }, + { 0, 1 }, + { 2, 3 }, + { 2, 3 }, + { 4, 5 }, + { 4, 5 } + }; + +int main() +{ + VERIFY( umm.size() == 6 ); + VERIFY( umm.find(0) != umm.end() ); +} diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multiset/96088.cc b/libstdc++-v3/testsuite/23_containers/unordered_multiset/96088.cc index 6f79ddf..c016c88 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_multiset/96088.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_multiset/96088.cc @@ -1,6 +1,6 @@ // { dg-do run { target c++17 } } // { dg-require-effective-target std_allocator_new } -// { dg-xfail-run-if "AIX operator new" { powerpc-ibm-aix* } } +// { dg-require-effective-target c++20 { target powerpc-ibm-aix* } } // Copyright (C) 2021-2025 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/66055.cc b/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/66055.cc index 5c34b94..3ba609f 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/66055.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/66055.cc @@ -27,7 +27,9 @@ using alloc_type = test_type::allocator_type; test_type h1(10, alloc_type()); test_type h2(10, hasher_type(), alloc_type()); -test_type h3(h1.begin(), h1.end(), 10, alloc_type()); -test_type h4(h1.begin(), h1.end(), 10, hasher_type(), alloc_type()); -test_type h5({ 1, 1 }, 10, alloc_type()); -test_type h6({ 1, 1 }, 10, hasher_type(), alloc_type()); +test_type h3(h1.begin(), h1.end(), alloc_type()); +test_type h4(h1.begin(), h1.end(), 10, alloc_type()); +test_type h5(h1.begin(), h1.end(), 10, hasher_type(), alloc_type()); +test_type h6({ 1, 1 }, alloc_type()); +test_type h7({ 1, 1 }, 10, alloc_type()); +test_type h9({ 1, 1 }, 10, hasher_type(), alloc_type()); diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/deduction.cc b/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/deduction.cc index 22b7297..46cd210 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/deduction.cc @@ -20,6 +20,22 @@ static_assert(std::is_same_v< std::unordered_multiset<int>>); static_assert(std::is_same_v< + decltype(std::unordered_multiset{{1, 2, 3}}), + std::unordered_multiset<int>>); + +static_assert(std::is_same_v< + decltype(std::unordered_multiset{{1, 2, 3}, + std::allocator<int>{}}), + std::unordered_multiset<int>>); + +static_assert(std::is_same_v< + decltype(std::unordered_multiset{{1, 2, 3}, + SimpleAllocator<int>{}}), + std::unordered_multiset<int, std::hash<int>, + std::equal_to<int>, + SimpleAllocator<int>>>); + +static_assert(std::is_same_v< decltype(std::unordered_multiset{{1, 2, 3}, {}}), std::unordered_multiset<int>>); @@ -88,6 +104,18 @@ void f() static_assert(std::is_same_v< decltype(std::unordered_multiset{x.begin(), x.end(), + std::allocator<int>{}}), + std::unordered_multiset<int>>); + + static_assert(std::is_same_v< + decltype(std::unordered_multiset{x.begin(), x.end(), + SimpleAllocator<int>{}}), + std::unordered_multiset<int, std::hash<int>, + std::equal_to<int>, + SimpleAllocator<int>>>); + + static_assert(std::is_same_v< + decltype(std::unordered_multiset{x.begin(), x.end(), {}, std::hash<int>{}, std::allocator<int>{}}), std::unordered_multiset<int>>); diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/from_range.cc b/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/from_range.cc index d44598d..ff05471 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/from_range.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_multiset/cons/from_range.cc @@ -85,11 +85,11 @@ constexpr bool is_equal(std::equal_to<T>, std::equal_to<U>) { return true; } constexpr bool is_equal(StateHash lhs, StateHash rhs) -{ return lhs.state = rhs.state; } +{ return lhs.state == rhs.state; } constexpr bool is_equal(StateEq lhs, StateEq rhs) -{ return lhs.state = rhs.state; } +{ return lhs.state == rhs.state; } template<typename Range, typename Alloc, typename Hash, typename Equal> constexpr void diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multiset/const_container.cc b/libstdc++-v3/testsuite/23_containers/unordered_multiset/const_container.cc new file mode 100644 index 0000000..841d25a --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/unordered_multiset/const_container.cc @@ -0,0 +1,15 @@ +// { dg-do compile { target c++11 } } + +#include <unordered_set> + +#include <testsuite_hooks.h> + +// PR c++/116369 +const std::unordered_multiset<int> ums + { 0, 0, 1, 1, 2, 2 }; + +int main() +{ + VERIFY( ums.size() == 6 ); + VERIFY( ums.find(0) != ums.end() ); +} diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/96088.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/96088.cc index c09e6f7..10838c4 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_set/96088.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_set/96088.cc @@ -1,6 +1,6 @@ // { dg-do run { target c++17 } } // { dg-require-effective-target std_allocator_new } -// { dg-xfail-run-if "AIX operator new" { powerpc-ibm-aix* } } +// { dg-require-effective-target c++20 { target powerpc-ibm-aix* } } // Copyright (C) 2021-2025 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/cons/66055.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/cons/66055.cc index 0d318a0..96c0ca3 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_set/cons/66055.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_set/cons/66055.cc @@ -27,7 +27,9 @@ using alloc_type = test_type::allocator_type; test_type h1(10, alloc_type()); test_type h2(10, hasher_type(), alloc_type()); -test_type h3(h1.begin(), h1.end(), 10, alloc_type()); -test_type h4(h1.begin(), h1.end(), 10, hasher_type(), alloc_type()); -test_type h5({ 1, 1 }, 10, alloc_type()); -test_type h6({ 1, 1 }, 10, hasher_type(), alloc_type()); +test_type h3(h1.begin(), h1.end(), alloc_type()); +test_type h4(h1.begin(), h1.end(), 10, alloc_type()); +test_type h5(h1.begin(), h1.end(), 10, hasher_type(), alloc_type()); +test_type h6({ 1, 1 }, alloc_type()); +test_type h7({ 1, 1 }, 10, alloc_type()); +test_type h9({ 1, 1 }, 10, hasher_type(), alloc_type()); diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/cons/deduction.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/cons/deduction.cc index db58581..9558d70 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_set/cons/deduction.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_set/cons/deduction.cc @@ -20,6 +20,22 @@ static_assert(std::is_same_v< std::unordered_set<int>>); static_assert(std::is_same_v< + decltype(std::unordered_set{{1, 2, 3}}), + std::unordered_set<int>>); + +static_assert(std::is_same_v< + decltype(std::unordered_set{{1, 2, 3}, + std::allocator<int>{}}), + std::unordered_set<int>>); + +static_assert(std::is_same_v< + decltype(std::unordered_set{{1, 2, 3}, + SimpleAllocator<int>{}}), + std::unordered_set<int, std::hash<int>, + std::equal_to<int>, + SimpleAllocator<int>>>); + +static_assert(std::is_same_v< decltype(std::unordered_set{{1, 2, 3}, {}}), std::unordered_set<int>>); @@ -92,6 +108,18 @@ void f() std::unordered_set<int>>); static_assert(std::is_same_v< + decltype(std::unordered_set{x.begin(), x.end(), + std::allocator<int>{}}), + std::unordered_set<int>>); + + static_assert(std::is_same_v< + decltype(std::unordered_set{x.begin(), x.end(), + SimpleAllocator<int>{}}), + std::unordered_set<int, std::hash<int>, + std::equal_to<int>, + SimpleAllocator<int>>>); + + static_assert(std::is_same_v< decltype(std::unordered_set{x.begin(), x.end(), 1}), std::unordered_set<int>>); diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/cons/from_range.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/cons/from_range.cc index 8259be8..e00e2fb 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_set/cons/from_range.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_set/cons/from_range.cc @@ -84,11 +84,11 @@ constexpr bool is_equal(std::equal_to<T>, std::equal_to<U>) { return true; } constexpr bool is_equal(StateHash lhs, StateHash rhs) -{ return lhs.state = rhs.state; } +{ return lhs.state == rhs.state; } constexpr bool is_equal(StateEq lhs, StateEq rhs) -{ return lhs.state = rhs.state; } +{ return lhs.state == rhs.state; } template<typename Range, typename Alloc, typename Hash, typename Equal> constexpr void diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/const_container.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/const_container.cc new file mode 100644 index 0000000..ffdbbad --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/unordered_set/const_container.cc @@ -0,0 +1,14 @@ +// { dg-do compile { target c++11 } } + +#include <unordered_set> + +#include <testsuite_hooks.h> + +// PR c++/116369 +const std::unordered_set<int> us { 0, 1, 2 }; + +int main() +{ + VERIFY( us.size() == 3 ); + VERIFY( us.find(0) != us.end() ); +} diff --git a/libstdc++-v3/testsuite/23_containers/vector/allocator/default_init.cc b/libstdc++-v3/testsuite/23_containers/vector/allocator/default_init.cc index 195cd2d..486c44c 100644 --- a/libstdc++-v3/testsuite/23_containers/vector/allocator/default_init.cc +++ b/libstdc++-v3/testsuite/23_containers/vector/allocator/default_init.cc @@ -59,6 +59,17 @@ void test02() tmp->~test_type(); } +#ifdef __cpp_lib_constexpr_vector +constexpr bool +test03() +{ + using alloc_type = default_init_allocator<T>; + std::vector<T, alloc_type> v; + return v.get_allocator().state == 0; +} +static_assert( test03() ); +#endif + int main() { test01(); diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/allocator/default_init.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/allocator/default_init.cc index 3914b7f..c95cb6b 100644 --- a/libstdc++-v3/testsuite/23_containers/vector/bool/allocator/default_init.cc +++ b/libstdc++-v3/testsuite/23_containers/vector/bool/allocator/default_init.cc @@ -59,6 +59,17 @@ void test02() tmp->~test_type(); } +#ifdef __cpp_lib_constexpr_vector +constexpr bool +test03() +{ + using alloc_type = default_init_allocator<T>; + std::vector<T, alloc_type> v; + return v.get_allocator().state == 0; +} +static_assert( test03() ); +#endif + int main() { test01(); diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/cons/from_range.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/cons/from_range.cc index 339c06bd..516d888 100644 --- a/libstdc++-v3/testsuite/23_containers/vector/bool/cons/from_range.cc +++ b/libstdc++-v3/testsuite/23_containers/vector/bool/cons/from_range.cc @@ -42,14 +42,16 @@ do_test(Alloc alloc) } template<typename Range> -void +constexpr void do_test_a() { do_test<Range>(std::allocator<bool>()); - do_test<Range>(__gnu_test::uneq_allocator<bool>(42)); + if not consteval { + do_test<Range>(__gnu_test::uneq_allocator<bool>(42)); + } } -bool +constexpr bool test_ranges() { using namespace __gnu_test; @@ -71,9 +73,9 @@ test_ranges() // Not lvalue-convertible to bool struct C { - C(bool v) : val(v) { } - operator bool() && { return val; } - bool operator==(bool b) const { return b == val; } + constexpr C(bool v) : val(v) { } + constexpr operator bool() && { return val; } + constexpr bool operator==(bool b) const { return b == val; } bool val; }; using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; @@ -82,16 +84,8 @@ test_ranges() return true; } -constexpr bool -test_constexpr() -{ - // XXX: this doesn't test the non-forward_range code paths are constexpr. - do_test<std::span<bool>>(std::allocator<bool>()); - return true; -} - int main() { test_ranges(); - static_assert( test_constexpr() ); + static_assert( test_ranges() ); } diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/format.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/format.cc index eb24b66..833727f 100644 --- a/libstdc++-v3/testsuite/23_containers/vector/bool/format.cc +++ b/libstdc++-v3/testsuite/23_containers/vector/bool/format.cc @@ -7,6 +7,7 @@ static_assert(!std::formattable<std::vector<bool>::reference, int>); static_assert(!std::formattable<std::vector<bool>::reference, char32_t>); +static_assert(std::enable_nonlocking_formatter_optimization<std::vector<bool>::reference>); template<typename... Args> bool @@ -21,7 +22,7 @@ is_format_string_for(const char* str, Args&&... args) } #define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S) -#define WIDEN(S) WIDEN_(_CharT, S) +#define WIDEN(S) WIDEN_(CharT, S) void test_format_string() @@ -34,11 +35,11 @@ test_format_string() VERIFY( !is_format_string_for("{:{}}", v[0], 1.0f) ); } -template<typename _CharT> +template<typename CharT> void test_output() { - std::basic_string<_CharT> res; + std::basic_string<CharT> res; size_t size = 0; std::vector<bool> v{true, false}; diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/assign/assign_range.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/assign/assign_range.cc index 7e58700..ced7efe 100644 --- a/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/assign/assign_range.cc +++ b/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/assign/assign_range.cc @@ -59,14 +59,14 @@ do_test() } template<typename Range> -void +constexpr void do_test_a() { do_test<Range, std::allocator<bool>>(); do_test<Range, __gnu_test::SimpleAllocator<bool>>(); } -bool +constexpr bool test_ranges() { using namespace __gnu_test; @@ -88,9 +88,9 @@ test_ranges() // Not lvalue-convertible to bool struct C { - C(bool v) : val(v) { } - operator bool() && { return val; } - bool operator==(bool b) const { return b == val; } + constexpr C(bool v) : val(v) { } + constexpr operator bool() && { return val; } + constexpr bool operator==(bool b) const { return b == val; } bool val; }; using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; @@ -99,16 +99,8 @@ test_ranges() return true; } -constexpr bool -test_constexpr() -{ - // XXX: this doesn't test the non-forward_range code paths are constexpr. - do_test<std::span<short>, std::allocator<bool>>(); - return true; -} - int main() { test_ranges(); - static_assert( test_constexpr() ); + static_assert( test_ranges() ); } diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/insert/append_range.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/insert/append_range.cc index 43a698f..c2e2186 100644 --- a/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/insert/append_range.cc +++ b/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/insert/append_range.cc @@ -38,14 +38,14 @@ do_test() } template<typename Range> -void +constexpr void do_test_a() { do_test<Range, std::allocator<bool>>(); do_test<Range, __gnu_test::SimpleAllocator<bool>>(); } -bool +constexpr bool test_ranges() { using namespace __gnu_test; @@ -67,9 +67,9 @@ test_ranges() // Not lvalue-convertible to bool struct C { - C(bool v) : val(v) { } - operator bool() && { return val; } - bool operator==(bool b) const { return b == val; } + constexpr C(bool v) : val(v) { } + constexpr operator bool() && { return val; } + constexpr bool operator==(bool b) const { return b == val; } bool val; }; using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; @@ -81,8 +81,7 @@ test_ranges() constexpr bool test_constexpr() { - // XXX: this doesn't test the non-forward_range code paths are constexpr. - do_test<std::span<short>, std::allocator<bool>>(); + test_ranges(); // Some basic tests for overlapping ranges in constant expressions. using I = std::vector<bool>::iterator; diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/insert/insert_range.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/insert/insert_range.cc index 5c65610..2ec91b0 100644 --- a/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/insert/insert_range.cc +++ b/libstdc++-v3/testsuite/23_containers/vector/bool/modifiers/insert/insert_range.cc @@ -55,14 +55,14 @@ do_test() } template<typename Range> -void +constexpr void do_test_a() { do_test<Range, std::allocator<bool>>(); do_test<Range, __gnu_test::SimpleAllocator<bool>>(); } -bool +constexpr bool test_ranges() { using namespace __gnu_test; @@ -84,9 +84,9 @@ test_ranges() // Not lvalue-convertible to bool struct C { - C(bool v) : val(v) { } - operator bool() && { return val; } - bool operator==(bool b) const { return b == val; } + constexpr C(bool v) : val(v) { } + constexpr operator bool() && { return val; } + constexpr bool operator==(bool b) const { return b == val; } bool val; }; using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; @@ -95,16 +95,8 @@ test_ranges() return true; } -constexpr bool -test_constexpr() -{ - // XXX: this doesn't test the non-forward_range code paths are constexpr. - do_test<std::span<bool>, std::allocator<bool>>(); - return true; -} - int main() { test_ranges(); - static_assert( test_constexpr() ); + static_assert( test_ranges() ); } diff --git a/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_debug_neg.cc b/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_debug_neg.cc index 61b99d3..e6689f7 100644 --- a/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_debug_neg.cc +++ b/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_debug_neg.cc @@ -43,11 +43,8 @@ test02() std::vector<PrivateDtor> v; } -// { dg-error "value type is destructible" "" { target *-*-* } 0 } +// { dg-error "deleted function .*DeletedDtor" "" { target *-*-* } 0 } +// { dg-error "PrivateDtor.* is private" "" { target *-*-* } 0 } // In Debug Mode the "required from here" errors come from <debug/vector> // { dg-error "required from here" "" { target *-*-* } 182 } - -// Needed because of PR c++/92193 -// { dg-prune-output "deleted function" } -// { dg-prune-output "private within this context" } diff --git a/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_neg.cc b/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_neg.cc index 0807d15..2bd15bc 100644 --- a/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_neg.cc +++ b/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_neg.cc @@ -42,8 +42,5 @@ test02() std::vector<PrivateDtor> v; // { dg-error "here" } } -// { dg-error "value type is destructible" "" { target *-*-* } 0 } - -// Needed because of PR c++/92193 -// { dg-prune-output "deleted function" } -// { dg-prune-output "private within this context" } +// { dg-error "deleted function .*DeletedDtor" "" { target *-*-* } 0 } +// { dg-error "PrivateDtor.* is private" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/23_containers/vector/cons/from_range.cc b/libstdc++-v3/testsuite/23_containers/vector/cons/from_range.cc index 7a62645..be3e699 100644 --- a/libstdc++-v3/testsuite/23_containers/vector/cons/from_range.cc +++ b/libstdc++-v3/testsuite/23_containers/vector/cons/from_range.cc @@ -58,14 +58,16 @@ do_test(Alloc alloc) } template<typename Range> -void +constexpr void do_test_a() { do_test<Range>(std::allocator<int>()); - do_test<Range>(__gnu_test::uneq_allocator<int>(42)); + if not consteval { + do_test<Range>(__gnu_test::uneq_allocator<int>(42)); + } } -bool +constexpr bool test_ranges() { using namespace __gnu_test; @@ -87,9 +89,9 @@ test_ranges() // Not lvalue-convertible to int struct C { - C(int v) : val(v) { } - operator int() && { return val; } - bool operator==(int b) const { return b == val; } + constexpr C(int v) : val(v) { } + constexpr operator int() && { return val; } + constexpr bool operator==(int b) const { return b == val; } int val; }; using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; @@ -98,16 +100,30 @@ test_ranges() return true; } -constexpr bool -test_constexpr() +void +test_pr120367() { - // XXX: this doesn't test the non-forward_range code paths are constexpr. - do_test<std::span<short>>(std::allocator<int>()); - return true; +#ifdef __cpp_exceptions + struct X + { + X(int) { throw 1; } // Cannot successfully construct an X. + ~X() { VERIFY(false); } // So should never need to destroy one. + }; + + try + { + int i[1]{}; + std::vector<X> v(std::from_range, i); + } + catch (int) + { + } +#endif } int main() { test_ranges(); - static_assert( test_constexpr() ); + static_assert( test_ranges() ); + test_pr120367(); } diff --git a/libstdc++-v3/testsuite/23_containers/vector/debug/erase.cc b/libstdc++-v3/testsuite/23_containers/vector/debug/erase.cc new file mode 100644 index 0000000..d2b412d --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/vector/debug/erase.cc @@ -0,0 +1,27 @@ +// { dg-do run { target c++20 } } +// { dg-require-debug-mode "" } + +#include <vector> +#include <testsuite_hooks.h> + +void test01() +{ + std::vector<int> v; + + for (int i = 0; i != 10; ++i) + v.push_back(i); + + auto before = v.begin() + 4; + auto last = v.end() - 1; + + VERIFY( std::erase(v, 6) == 1 ); + + VERIFY(before._M_dereferenceable()); + VERIFY(last._M_singular()); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/vector/debug/invalidation/erase.cc b/libstdc++-v3/testsuite/23_containers/vector/debug/invalidation/erase.cc new file mode 100644 index 0000000..38326a7 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/vector/debug/invalidation/erase.cc @@ -0,0 +1,28 @@ +// { dg-do run { target c++20 } } + +#include <debug/vector> +#include <testsuite_hooks.h> + +using __gnu_debug::vector; + +void test01() +{ + vector<int> v; + + for (int i = 0; i != 10; ++i) + v.push_back(i); + + auto before = v.begin() + 4; + auto last = v.end() -1; + + VERIFY( std::erase(v, 6) == 1 ); + + VERIFY(before._M_dereferenceable()); + VERIFY(last._M_singular()); +} + +int main() +{ + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/vector/debug/mutex_association.cc b/libstdc++-v3/testsuite/23_containers/vector/debug/mutex_association.cc index ba2ede0..792ed45 100644 --- a/libstdc++-v3/testsuite/23_containers/vector/debug/mutex_association.cc +++ b/libstdc++-v3/testsuite/23_containers/vector/debug/mutex_association.cc @@ -25,7 +25,7 @@ class container : public __gnu_debug::_Safe_sequence<container> { public: __gnu_cxx::__mutex& - get_mutex() + get_mutex() const { return this->_M_get_mutex(); } }; diff --git a/libstdc++-v3/testsuite/23_containers/vector/modifiers/append_range.cc b/libstdc++-v3/testsuite/23_containers/vector/modifiers/append_range.cc index be097e2..f5b21df 100644 --- a/libstdc++-v3/testsuite/23_containers/vector/modifiers/append_range.cc +++ b/libstdc++-v3/testsuite/23_containers/vector/modifiers/append_range.cc @@ -42,14 +42,14 @@ do_test() } template<typename Range> -void +constexpr void do_test_a() { do_test<Range, std::allocator<int>>(); do_test<Range, __gnu_test::SimpleAllocator<int>>(); } -bool +constexpr bool test_ranges() { using namespace __gnu_test; @@ -71,9 +71,9 @@ test_ranges() // Not lvalue-convertible to int struct C { - C(int v) : val(v) { } - operator int() && { return val; } - bool operator==(int b) const { return b == val; } + constexpr C(int v) : val(v) { } + constexpr operator int() && { return val; } + constexpr bool operator==(int b) const { return b == val; } int val; }; using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; @@ -82,7 +82,7 @@ test_ranges() return true; } -void +constexpr void test_overlapping() { using __gnu_test::test_input_range; @@ -199,64 +199,14 @@ test_overlapping() } } -constexpr bool -test_constexpr() +int main() { - // XXX: this doesn't test the non-forward_range code paths are constexpr. - do_test<std::span<short>, std::allocator<int>>(); - - // Some basic tests for overlapping ranges in constant expressions. - struct InputRange - { - struct Sent { const void* end; }; - - struct Iter - { - using value_type = int; - using difference_type = int; - constexpr explicit Iter(int* p) : ptr(p) { } - constexpr Iter& operator++() { ++ptr; return *this; } - constexpr Iter operator++(int) { auto i = *this; ++ptr; return i; } - constexpr int operator*() const { return *ptr; } - constexpr bool operator==(const Iter&) const = default; - constexpr bool operator==(const Sent& s) const { return ptr == s.end; } - int* ptr; - }; - - Iter iter; - Sent sent; - - constexpr InputRange(int* f, int* l) : iter{f}, sent{l} { } - constexpr Iter begin() const { return iter; } - constexpr Sent end() const { return sent; } + auto test_all = [] { + test_ranges(); + test_overlapping(); + return true; }; - static_assert( std::ranges::input_range<InputRange> ); - static_assert( ! std::ranges::forward_range<InputRange> ); - - std::vector<int> vec(5); - - // Test overlapping input ranges - vec.resize(vec.capacity()); - vec.append_range(InputRange(vec.data(), vec.data() + 3)); // no capacity - vec.reserve(vec.capacity() + 2); - vec.append_range(InputRange(vec.data(), vec.data() + 4)); // some capacity - vec.reserve(vec.capacity() + 6); - vec.append_range(InputRange(vec.data(), vec.data() + 5)); // enough capacity - - // Test overlapping forward ranges - vec.resize(vec.capacity()); - vec.append_range(std::span<int>(vec)); // no capacity - vec.reserve(vec.size() + 2); - vec.append_range(std::span<int>(vec).subspan(1, 4)); // some capacity - vec.reserve(vec.size() + 6); - vec.append_range(std::span<int>(vec).subspan(1, 5)); // enough capacity - return true; -} - -int main() -{ - test_ranges(); - test_overlapping(); - static_assert( test_constexpr() ); + test_all(); + static_assert( test_all() ); } diff --git a/libstdc++-v3/testsuite/23_containers/vector/modifiers/assign/assign_range.cc b/libstdc++-v3/testsuite/23_containers/vector/modifiers/assign/assign_range.cc index db3b06c..26d33bc 100644 --- a/libstdc++-v3/testsuite/23_containers/vector/modifiers/assign/assign_range.cc +++ b/libstdc++-v3/testsuite/23_containers/vector/modifiers/assign/assign_range.cc @@ -63,14 +63,14 @@ do_test() } template<typename Range> -void +constexpr void do_test_a() { do_test<Range, std::allocator<int>>(); do_test<Range, __gnu_test::SimpleAllocator<int>>(); } -bool +constexpr bool test_ranges() { using namespace __gnu_test; @@ -92,9 +92,9 @@ test_ranges() // Not lvalue-convertible to int struct C { - C(int v) : val(v) { } - operator int() && { return val; } - bool operator==(int b) const { return b == val; } + constexpr C(int v) : val(v) { } + constexpr operator int() && { return val; } + constexpr bool operator==(int b) const { return b == val; } int val; }; using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; @@ -103,16 +103,8 @@ test_ranges() return true; } -constexpr bool -test_constexpr() -{ - // XXX: this doesn't test the non-forward_range code paths are constexpr. - do_test<std::span<short>, std::allocator<int>>(); - return true; -} - int main() { test_ranges(); - static_assert( test_constexpr() ); + static_assert( test_ranges() ); } diff --git a/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert/insert_range.cc b/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert/insert_range.cc index 5907143..e4b5982 100644 --- a/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert/insert_range.cc +++ b/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert/insert_range.cc @@ -59,14 +59,14 @@ do_test() } template<typename Range> -void +constexpr void do_test_a() { do_test<Range, std::allocator<int>>(); do_test<Range, __gnu_test::SimpleAllocator<int>>(); } -bool +constexpr bool test_ranges() { using namespace __gnu_test; @@ -88,9 +88,9 @@ test_ranges() // Not lvalue-convertible to int struct C { - C(int v) : val(v) { } - operator int() && { return val; } - bool operator==(int b) const { return b == val; } + constexpr C(int v) : val(v) { } + constexpr operator int() && { return val; } + constexpr bool operator==(int b) const { return b == val; } int val; }; using rvalue_input_range = test_range<C, input_iterator_wrapper_rval>; @@ -99,16 +99,58 @@ test_ranges() return true; } -constexpr bool -test_constexpr() +struct SelfAssignChecker { + static int moveCounter; + static int copyCounter; + + SelfAssignChecker() = default; + constexpr SelfAssignChecker(int v) : val(v) { } + SelfAssignChecker(const SelfAssignChecker&) = default; + SelfAssignChecker(SelfAssignChecker&&) = default; + + SelfAssignChecker operator=(const SelfAssignChecker& rhs) + { + if (this == &rhs) + ++copyCounter; + this->val = rhs.val; + return *this; + } + + SelfAssignChecker operator=(SelfAssignChecker&& rhs) + { + if (this == &rhs) + ++moveCounter; + this->val = rhs.val; + return *this; + } + + int val; + + friend bool operator==(SelfAssignChecker, SelfAssignChecker) = default; +}; + +int SelfAssignChecker::moveCounter = 0; +int SelfAssignChecker::copyCounter = 0; + +void +test_pr121313() { - // XXX: this doesn't test the non-forward_range code paths are constexpr. - do_test<std::span<short>, std::allocator<int>>(); - return true; + using namespace __gnu_test; + + SelfAssignChecker::copyCounter = SelfAssignChecker::moveCounter = 0; + do_test<test_forward_range<int>, std::allocator<SelfAssignChecker>>(); + VERIFY( SelfAssignChecker::moveCounter == 0 ); + VERIFY( SelfAssignChecker::copyCounter == 0 ); + + SelfAssignChecker::copyCounter = SelfAssignChecker::moveCounter = 0; + do_test<test_input_range<int>, std::allocator<SelfAssignChecker>>(); + VERIFY( SelfAssignChecker::moveCounter == 0 ); + VERIFY( SelfAssignChecker::copyCounter == 0 ); } int main() { test_ranges(); - static_assert( test_constexpr() ); + test_pr121313(); + static_assert( test_ranges() ); } diff --git a/libstdc++-v3/testsuite/23_containers/vector/modifiers/moveable.cc b/libstdc++-v3/testsuite/23_containers/vector/modifiers/moveable.cc index 8d0f9ae..343a298 100644 --- a/libstdc++-v3/testsuite/23_containers/vector/modifiers/moveable.cc +++ b/libstdc++-v3/testsuite/23_containers/vector/modifiers/moveable.cc @@ -109,9 +109,11 @@ test05() // when it doesn't reallocate the buffer. VERIFY(copycounter::copycount == 20 + 1); a.insert(a.end(), 50, c); - VERIFY(copycounter::copycount == 70 + 2); + // expect when inserting at the end (appending), where existing + // elements are not modified + VERIFY(copycounter::copycount == 70 + 1); a.insert(a.begin() + 50, 100, c); - VERIFY(copycounter::copycount == 170 + 3); + VERIFY(copycounter::copycount == 170 + 2); } diff --git a/libstdc++-v3/testsuite/23_containers/vector/modifiers/resize.cc b/libstdc++-v3/testsuite/23_containers/vector/modifiers/resize.cc new file mode 100644 index 0000000..026b0f7 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/vector/modifiers/resize.cc @@ -0,0 +1,69 @@ +// { dg-do run } + +#include <vector> +#include <testsuite_hooks.h> + +struct NoAssign +{ + NoAssign(int p) : val(p) {} + const int val; +}; + +struct PrivateAssign +{ + PrivateAssign(int p) : val(p) {} + PrivateAssign(const PrivateAssign& rhs) : val(rhs.val) {} + + int val; + +private: + PrivateAssign& operator=(const PrivateAssign&); +}; + +#if __cplusplus >= 201102L +struct DeletedAssign +{ + DeletedAssign(int p) : val(p) {} + DeletedAssign(const DeletedAssign& rhs) : val(rhs.val) {} + + DeletedAssign& operator=(const DeletedAssign&) = delete; + + int val; +}; +#endif + +template<typename T> +void +testPR90129() +{ + std::vector<T> v; + v.resize(5, T(5)); + VERIFY( v.size() == 5 ); + VERIFY( v.front().val == 5 ); + VERIFY( v.back().val == 5 ); + + v.resize(10, T(10)); + VERIFY( v.size() == 10 ); + VERIFY( v.front().val == 5 ); + VERIFY( v.back().val == 10 ); + + v.resize(7, T(7)); + VERIFY( v.size() == 7 ); + VERIFY( v.front().val == 5 ); + VERIFY( v.back().val == 10 ); + + v.resize(3, T(3)); + VERIFY( v.size() == 3 ); + VERIFY( v.front().val == 5 ); + VERIFY( v.back().val == 5 ); +} + +int main() +{ + testPR90129<NoAssign>(); + testPR90129<PrivateAssign>(); +#if __cplusplus >= 201102L + testPR90129<DeletedAssign>(); +#endif + return 0; +} diff --git a/libstdc++-v3/testsuite/24_iterators/const_iterator/1.cc b/libstdc++-v3/testsuite/24_iterators/const_iterator/1.cc index fe952bf..f2bcad4 100644 --- a/libstdc++-v3/testsuite/24_iterators/const_iterator/1.cc +++ b/libstdc++-v3/testsuite/24_iterators/const_iterator/1.cc @@ -42,12 +42,13 @@ test01() } } -template<class Range, bool Const> +template<class Range, bool Const, bool Constable = Const> void test02() { if constexpr (Const) { + static_assert(Constable); static_assert( ranges::constant_range<Range> ); static_assert( std::same_as<ranges::const_iterator_t<Range>, ranges::iterator_t<Range>> ); static_assert( std::same_as<ranges::const_sentinel_t<Range>, ranges::sentinel_t<Range>> ); @@ -64,9 +65,21 @@ test02() static_assert( !ranges::constant_range<Range> ); using Wrapped = std::basic_const_iterator<ranges::iterator_t<Range>>; - static_assert( std::same_as<ranges::const_iterator_t<Range>, Wrapped> ); - if constexpr (ranges::common_range<Range>) - static_assert( std::same_as<ranges::const_sentinel_t<Range>, Wrapped> ); + if constexpr (Constable) + { + // Verify LWG 3946 changes to const_iterator/sentinel_t (PR122842). + static_assert( std::same_as<ranges::const_iterator_t<Range>, + ranges::iterator_t<const Range>> ); + static_assert( std::same_as<ranges::const_sentinel_t<Range>, + ranges::sentinel_t<const Range>> ); + } + else + { + static_assert( std::same_as<ranges::const_iterator_t<Range>, Wrapped> ); + if constexpr (ranges::common_range<Range>) + static_assert( std::same_as<ranges::const_sentinel_t<Range>, Wrapped> ); + } + static_assert( std::same_as<ranges::range_const_reference_t<Range>, std::iter_reference_t<Wrapped>> ); @@ -138,13 +151,14 @@ main() test01<std::string_view::iterator, true>(); test01<std::vector<bool>::const_iterator, true>(); - test02<int[42], false>(); + test02<int[42], false, true>(); test02<test_input_range<int>, false>(); test02<test_forward_range<int>, false>(); test02<test_bidirectional_range<int>, false>(); test02<test_random_access_range<int>, false>(); - test02<std::array<int, 3>, false>(); - test02<std::vector<bool>, false>(); + test02<std::array<int, 3>, false, true>(); + test02<std::vector<bool>, false, true>(); + test02<std::string, false, true>(); test02<const int[42], true>(); test02<test_input_range<const int>, true>(); @@ -155,6 +169,7 @@ main() test02<const std::array<int, 3>, true>(); test02<std::string_view, true>(); test02<const std::vector<bool>, true>(); + test02<const std::string, true>(); test03(); test04(); diff --git a/libstdc++-v3/testsuite/24_iterators/indirect_callable/projected.cc b/libstdc++-v3/testsuite/24_iterators/indirect_callable/projected.cc index 0b18616..e2fbf7d 100644 --- a/libstdc++-v3/testsuite/24_iterators/indirect_callable/projected.cc +++ b/libstdc++-v3/testsuite/24_iterators/indirect_callable/projected.cc @@ -22,12 +22,7 @@ template<typename T> using PI = std::projected<T, std::identity>; -#if __GLIBCXX__ -// Verify our projected<I, identity> optimization. -static_assert(std::same_as<PI<int*>, int*>); -#else static_assert(std::same_as<PI<int*>::value_type, int>); -#endif static_assert(std::same_as<decltype(*std::declval<const PI<int*>&>()), int&>); struct X diff --git a/libstdc++-v3/testsuite/24_iterators/operations/cxx20_iterators.cc b/libstdc++-v3/testsuite/24_iterators/operations/cxx20_iterators.cc new file mode 100644 index 0000000..b613c37 --- /dev/null +++ b/libstdc++-v3/testsuite/24_iterators/operations/cxx20_iterators.cc @@ -0,0 +1,60 @@ +// { dg-do run { target c++20 } } + +#include <ranges> +#include <testsuite_iterators.h> +#include <testsuite_hooks.h> + +// Bug 102181 std::advance and std::views::iota<std::int64_t> don't work +void +test_pr102181() +{ +#ifdef __SIZEOF_INT128__ + using type = unsigned __int128; +#else + using type = unsigned long; +#endif + auto v = std::ranges::iota_view(type(0), type(10)); + auto b = v.begin(); + VERIFY( std::distance(b, std::next(b)) == 1 ); + std::advance(b, std::iter_difference_t<decltype(b)>(1)); + VERIFY( *b == 1 ); + VERIFY( std::distance(b, v.end()) == 9 ); +} + +// https://stackoverflow.com/questions/68100775/rangesviewtransform-produces-an-inputiterator-preventing-the-use-of-stdpre +void +test_transform_view_iterator() +{ + int a[] = {0, 1, 2, 3}; + __gnu_test::random_access_container<int> rr(a); + auto rx = std::ranges::views::transform(rr, std::identity{}); + auto re = rx.end(); + VERIFY( *std::prev(re) == 3 ); + VERIFY( std::distance(rx.begin(), re) == 4 ); + + __gnu_test::bidirectional_container<int> br(a); + auto bx = std::ranges::views::transform(br, std::identity{}); + auto be = bx.end(); + VERIFY( *std::prev(be) == 3 ); + VERIFY( std::distance(bx.begin(), be) == 4 ); + + __gnu_test::forward_container<int> fr(a); + auto fx = std::ranges::views::transform(br, std::identity{}); + auto fb = fx.begin(); + VERIFY( *std::next(fb) == 1 ); + VERIFY( std::distance(fb, fx.end()) == 4 ); + + __gnu_test::test_input_range<int> ir(a); + auto ix = std::ranges::views::transform(ir, std::identity{}); + auto ii = ix.begin(); + std::advance(ii, 1); + VERIFY( *ii == 1 ); + // N.B. cannot use std::distance or std::next here because there is no + // iterator_traits<decltype(ii)>::difference_type for this iterator. +} + +int main() +{ + test_pr102181(); + test_transform_view_iterator(); +} diff --git a/libstdc++-v3/testsuite/24_iterators/operations/prev_neg.cc b/libstdc++-v3/testsuite/24_iterators/operations/prev_neg.cc index f9de894..5fdfa9e 100644 --- a/libstdc++-v3/testsuite/24_iterators/operations/prev_neg.cc +++ b/libstdc++-v3/testsuite/24_iterators/operations/prev_neg.cc @@ -38,5 +38,5 @@ test02() { const Y array[1] = { }; (void) std::prev(array + 1); - // { dg-error "forward_iterator" "" { target *-*-* } 241 } + // { dg-error "forward_iterator" "" { target *-*-* } 242 } } diff --git a/libstdc++-v3/testsuite/24_iterators/random_access/string_vector_iterators.cc b/libstdc++-v3/testsuite/24_iterators/random_access/string_vector_iterators.cc index a8dbe51..d6b95d6 100644 --- a/libstdc++-v3/testsuite/24_iterators/random_access/string_vector_iterators.cc +++ b/libstdc++-v3/testsuite/24_iterators/random_access/string_vector_iterators.cc @@ -17,238 +17,162 @@ // along with this program; see the file COPYING3. If not see // <http://www.gnu.org/licenses/>. - #include <string> #include <vector> #include <testsuite_hooks.h> -int +void string_stuff() { - int failures(0); - std::string s("abcde"); std::string::iterator i1(s.begin()); - if (*i1 != 'a') - ++failures; + VERIFY( *i1 == 'a' ); ++i1; - if (*i1 != 'b') - ++failures; + VERIFY( *i1 == 'b' ); - if (*i1++ != 'b') - ++failures; - if (*i1 != 'c') - ++failures; + VERIFY( *i1++ == 'b' ); + VERIFY( *i1 == 'c' ); ++ ++i1; - if (*i1 != 'e') - ++failures; + VERIFY( *i1 == 'e' ); --i1; - if (*i1 != 'd') - ++failures; + VERIFY( *i1 == 'd' ); - if (*i1-- != 'd') - ++failures; - if (*i1 != 'c') - ++failures; + VERIFY( *i1-- == 'd' ); + VERIFY( *i1 == 'c' ); -- --i1; - if (*i1 != 'a') - ++failures; + VERIFY( *i1 == 'a' ); std::string::iterator i2; i2 = s.end(); std::iterator_traits<std::string::iterator>::difference_type d1; d1 = i2 - i1; - if (d1 != 5) - ++failures; + VERIFY( d1 == 5 ); std::iterator_traits<std::string::iterator>::value_type v1; v1 = i1[0]; - if (v1 != 'a') - ++failures; + VERIFY( v1 == 'a' ); std::iterator_traits<std::string::iterator>::reference r1(i1[0]); - if (r1 != 'a') - ++failures; + VERIFY( r1 == 'a' ); r1 = 'x'; - if (r1 != 'x') - ++failures; + VERIFY( r1 == 'x' ); r1 = 'a'; - if ((i1 != i2) != true) - ++failures; - if ((i1 == i2) != false) - ++failures; - if ((i1 < i2) != true) - ++failures; - if ((i1 > i2) != false) - ++failures; - if ((i1 <= i2) != true) - ++failures; - if ((i1 >= i2) != false) - ++failures; + VERIFY( (i1 != i2) == true ); + VERIFY( (i1 == i2) == false ); + VERIFY( (i1 < i2) == true ); + VERIFY( (i1 > i2) == false ); + VERIFY( (i1 <= i2) == true ); + VERIFY( (i1 >= i2) == false ); std::string::iterator i3; i3 = i1; - if ((i3 == i1) != true) - ++failures; + VERIFY( (i3 == i1) == true ); i3 += 5; - if ((i3 == i2) != true) - ++failures; + VERIFY( (i3 == i2) == true ); i3 -= 5; - if ((i3 == i1) != true) - ++failures; - - if (i3 + 5 != i2) - ++failures; - - if (5 + i3 != i2) - ++failures; + VERIFY( (i3 == i1) == true ); - if (i2 - 5 != i3) - ++failures; + VERIFY( i3 + 5 == i2 ); + VERIFY( 5 + i3 == i2 ); + VERIFY( i2 - 5 == i3 ); - if (i1[0] != 'a') - ++failures; + VERIFY( i1[0] == 'a' ); i1[4] = 'x'; - if (i2[-1] != 'x') - ++failures; + VERIFY( i2[-1] == 'x' ); i1[4] = 'e'; i1[2] = 'x'; - if (i2[-3] != 'x') - ++failures; + VERIFY( i2[-3] == 'x' ); i1[2] = 'c'; std::string::const_iterator ci1(s.begin()); - if (*ci1 != 'a') - ++failures; + VERIFY( *ci1 == 'a' ); ++ci1; - if (*ci1 != 'b') - ++failures; + VERIFY( *ci1 == 'b' ); - if (*ci1++ != 'b') - ++failures; - if (*ci1 != 'c') - ++failures; + VERIFY( *ci1++ == 'b' ); + VERIFY( *ci1 == 'c' ); ++ ++ci1; - if (*ci1 != 'e') - ++failures; + VERIFY( *ci1 == 'e' ); --ci1; - if (*ci1 != 'd') - ++failures; + VERIFY( *ci1 == 'd' ); - if (*ci1-- != 'd') - ++failures; - if (*ci1 != 'c') - ++failures; + VERIFY( *ci1-- == 'd' ); + VERIFY( *ci1 == 'c' ); -- --ci1; - if (*ci1 != 'a') - ++failures; + VERIFY( *ci1 == 'a' ); std::string::const_iterator ci2; ci2 = s.end(); std::iterator_traits<std::string::const_iterator>::difference_type d2; d2 = ci2 - ci1; - if (d2 != 5) - ++failures; + VERIFY( d2 == 5 ); std::iterator_traits<std::string::const_iterator>::value_type v2; v2 = ci1[0]; - if (v2 != 'a') - ++failures; + VERIFY( v2 == 'a' ); std::iterator_traits<std::string::const_iterator>::reference r2(ci1[0]); - if (r2 != 'a') - ++failures; - - if ((ci1 != ci2) != true) - ++failures; - if ((ci1 == ci2) != false) - ++failures; - if ((ci1 < ci2) != true) - ++failures; - if ((ci1 > ci2) != false) - ++failures; - if ((ci1 <= ci2) != true) - ++failures; - if ((ci1 >= ci2) != false) - ++failures; + VERIFY( r2 == 'a' ); + + VERIFY( (ci1 != ci2) == true ); + VERIFY( (ci1 == ci2) == false ); + VERIFY( (ci1 < ci2) == true ); + VERIFY( (ci1 > ci2) == false ); + VERIFY( (ci1 <= ci2) == true ); + VERIFY( (ci1 >= ci2) == false ); std::string::const_iterator ci3; ci3 = ci1; - if ((ci3 == ci1) != true) - ++failures; + VERIFY( ci3 == ci1 ); ci3 += 5; - if ((ci3 == ci2) != true) - ++failures; + VERIFY( ci3 == ci2 ); ci3 -= 5; - if ((ci3 == ci1) != true) - ++failures; - - if (ci3 + 5 != ci2) - ++failures; - - if (5 + ci3 != ci2) - ++failures; - - if (ci2 - 5 != ci3) - ++failures; + VERIFY( ci3 == ci1 ); - if (ci1[2] != 'c') - ++failures; + VERIFY( ci3 + 5 == ci2 ); + VERIFY( 5 + ci3 == ci2 ); + VERIFY( ci2 - 5 == ci3 ); - if (ci2[-1] != 'e') - ++failures; + VERIFY( ci1[2] == 'c' ); + VERIFY( ci2[-1] == 'e' ); // iterator and const_iterator std::string::const_iterator ci4(i1); - if ((ci4 == i1) != true) - ++failures; - if ((ci4 != i1) != false) - ++failures; - if ((ci4 < i1) != false) - ++failures; - if ((ci4 > i1) != false) - ++failures; - if ((ci4 <= i1) != true) - ++failures; - if ((ci4 >= i1) != true) - ++failures; + VERIFY( (ci4 == i1) == true ); + VERIFY( (ci4 != i1) == false ); + VERIFY( (ci4 < i1) == false ); + VERIFY( (ci4 > i1) == false ); + VERIFY( (ci4 <= i1) == true ); + VERIFY( (ci4 >= i1) == true ); ci4 = i2; - if ((i2 == ci4) != true) - ++failures; - if ((i2 < ci4) != false) - ++failures; - if ((i2 > ci4) != false) - ++failures; - if ((i2 <= ci4) != true) - ++failures; - if ((i2 >= ci4) != true) - ++failures; + VERIFY( (i2 == ci4) == true ); + VERIFY( (i2 < ci4) == false ); + VERIFY( (i2 > ci4) == false ); + VERIFY( (i2 <= ci4) == true ); + VERIFY( (i2 >= ci4) == true ); const std::string cs("ABCDE"); std::string::const_iterator ci5(cs.begin()); - if (ci5[0] != 'A') - ++failures; - - return failures; + VERIFY( ci5[0] == 'A' ); } -int +void vector_stuff() { int failures(0); @@ -261,347 +185,191 @@ vector_stuff() v.push_back(int(5)); std::vector<int>::iterator i1(v.begin()); - if (*i1 != 1) - ++failures; + VERIFY( *i1 == 1 ); ++i1; - if (*i1 != 2) - ++failures; + VERIFY( *i1 == 2 ); - if (*i1++ != 2) - ++failures; - if (*i1 != 3) - ++failures; + VERIFY( *i1++ == 2 ); + VERIFY( *i1 == 3 ); ++ ++i1; - if (*i1 != 5) - ++failures; + VERIFY( *i1 == 5 ); --i1; - if (*i1 != 4) - ++failures; + VERIFY( *i1 == 4 ); - if (*i1-- != 4) - ++failures; - if (*i1 != 3) - ++failures; + VERIFY( *i1-- == 4 ); + VERIFY( *i1 == 3 ); -- --i1; - if (*i1 != 1) - ++failures; + VERIFY( *i1 == 1 ); std::vector<int>::iterator i2; i2 = v.end(); std::iterator_traits<std::vector<int>::iterator>::difference_type d1; d1 = i2 - i1; - if (d1 != 5) - ++failures; + VERIFY( d1 == 5 ); std::iterator_traits<std::vector<int>::iterator>::value_type v1; v1 = i1[0]; - if (v1 != 1) - ++failures; + VERIFY( v1 == 1 ); std::iterator_traits<std::vector<int>::iterator>::reference r1(i1[0]); - if (r1 != 1) - ++failures; + VERIFY( r1 == 1 ); r1 = 9; - if (r1 != 9) - ++failures; + VERIFY( r1 == 9 ); r1 = 1; - if ((i1 != i2) != true) - ++failures; - if ((i1 == i2) != false) - ++failures; - if ((i1 < i2) != true) - ++failures; - if ((i1 > i2) != false) - ++failures; - if ((i1 <= i2) != true) - ++failures; - if ((i1 >= i2) != false) - ++failures; + VERIFY( (i1 != i2) == true ); + VERIFY( (i1 == i2) == false ); + VERIFY( (i1 < i2) == true ); + VERIFY( (i1 > i2) == false ); + VERIFY( (i1 <= i2) == true ); + VERIFY( (i1 >= i2) == false ); std::vector<int>::iterator i3; i3 = i1; - if ((i3 == i1) != true) - ++failures; + VERIFY( (i3 == i1) == true ); i3 += 5; - if ((i3 == i2) != true) - ++failures; + VERIFY( (i3 == i2) == true ); i3 -= 5; - if ((i3 == i1) != true) - ++failures; - - if (i3 + 5 != i2) - ++failures; - - if (5 + i3 != i2) - ++failures; + VERIFY( (i3 == i1) == true ); - if (i2 - 5 != i3) - ++failures; + VERIFY( i3 + 5 == i2 ); + VERIFY( 5 + i3 == i2 ); + VERIFY( i2 - 5 == i3 ); - if (i1[0] != 1) - ++failures; + VERIFY( i1[0] == 1 ); i1[4] = 9; - if (i2[-1] != 9) - ++failures; + VERIFY( i2[-1] == 9 ); i1[4] = 5; i1[2] = 9; - if (i2[-3] != 9) - ++failures; + VERIFY( i2[-3] == 9 ); i1[2] = 3; std::vector<int>::const_iterator ci1(v.begin()); - if (*ci1 != 1) - ++failures; + VERIFY( *ci1 == 1 ); ++ci1; - if (*ci1 != 2) - ++failures; + VERIFY( *ci1 == 2 ); - if (*ci1++ != 2) - ++failures; - if (*ci1 != 3) - ++failures; + VERIFY( *ci1++ == 2 ); + VERIFY( *ci1 == 3 ); ++ ++ci1; - if (*ci1 != 5) - ++failures; + VERIFY( *ci1 == 5 ); --ci1; - if (*ci1 != 4) - ++failures; + VERIFY( *ci1 == 4 ); - if (*ci1-- != 4) - ++failures; - if (*ci1 != 3) - ++failures; + VERIFY( *ci1-- == 4 ); + VERIFY( *ci1 == 3 ); -- --ci1; - if (*ci1 != 1) - ++failures; + VERIFY( *ci1 == 1 ); std::vector<int>::const_iterator ci2; ci2 = v.end(); std::iterator_traits<std::vector<int>::const_iterator>::difference_type d2; d2 = ci2 - ci1; - if (d2 != 5) - ++failures; + VERIFY( d2 == 5 ); std::iterator_traits<std::vector<int>::const_iterator>::value_type v2; v2 = ci1[0]; - if (v2 != 1) - ++failures; + VERIFY( v2 == 1 ); std::iterator_traits<std::vector<int>::const_iterator>::reference r2(ci1[0]); - if (r2 != 1) - ++failures; - - if ((ci1 != ci2) != true) - ++failures; - if ((ci1 == ci2) != false) - ++failures; - if ((ci1 < ci2) != true) - ++failures; - if ((ci1 > ci2) != false) - ++failures; - if ((ci1 <= ci2) != true) - ++failures; - if ((ci1 >= ci2) != false) - ++failures; + VERIFY( r2 == 1 ); + + VERIFY( (ci1 != ci2) == true ); + VERIFY( (ci1 == ci2) == false ); + VERIFY( (ci1 < ci2) == true ); + VERIFY( (ci1 > ci2) == false ); + VERIFY( (ci1 <= ci2) == true ); + VERIFY( (ci1 >= ci2) == false ); std::vector<int>::const_iterator ci3; ci3 = ci1; - if ((ci3 == ci1) != true) - ++failures; + VERIFY( (ci3 == ci1) == true ); ci3 += 5; - if ((ci3 == ci2) != true) - ++failures; + VERIFY( (ci3 == ci2) == true ); ci3 -= 5; - if ((ci3 == ci1) != true) - ++failures; - - if (ci3 + 5 != ci2) - ++failures; + VERIFY( (ci3 == ci1) == true ); - if (5 + ci3 != ci2) - ++failures; + VERIFY( ci3 + 5 == ci2 ); + VERIFY( 5 + ci3 == ci2 ); + VERIFY( ci2 - 5 == ci3 ); - if (ci2 - 5 != ci3) - ++failures; + VERIFY( ci1[2] == 3 ); - if (ci1[2] != 3) - ++failures; - - if (ci2[-1] != 5) - ++failures; + VERIFY( ci2[-1] == 5 ); // iterator to const_iterator std::vector<int>::const_iterator ci4(i1); - if ((ci4 == i1) != true) - ++failures; - if ((ci4 != i1) != false) - ++failures; - if ((ci4 < i1) != false) - ++failures; - if ((ci4 > i1) != false) - ++failures; - if ((ci4 <= i1) != true) - ++failures; - if ((ci4 >= i1) != true) - ++failures; + VERIFY( (ci4 == i1) == true ); + VERIFY( (ci4 != i1) == false ); + VERIFY( (ci4 < i1) == false ); + VERIFY( (ci4 > i1) == false ); + VERIFY( (ci4 <= i1) == true ); + VERIFY( (ci4 >= i1) == true ); ci4 = i2; - if ((i2 == ci4) != true) - ++failures; - if ((i2 < ci4) != false) - ++failures; - if ((i2 > ci4) != false) - ++failures; - if ((i2 <= ci4) != true) - ++failures; - if ((i2 >= ci4) != true) - ++failures; + VERIFY( (i2 == ci4) == true ); + VERIFY( (i2 < ci4) == false ); + VERIFY( (i2 > ci4) == false ); + VERIFY( (i2 <= ci4) == true ); + VERIFY( (i2 >= ci4) == true ); const std::vector<int> cv(v); std::vector<int>::const_iterator ci5(cv.begin()); - if (ci5[0] != 1) - ++failures; + VERIFY( ci5[0] == 1 ); std::vector<std::string> vs; vs.push_back(std::string("abc")); std::vector<std::string>::iterator ivs(vs.begin()); - if (ivs->c_str()[1] != 'b') - ++failures; - - return failures; + VERIFY( ivs->c_str()[1] == 'b' ); } -int +void reverse_stuff() { - int failures(0); - std::string s("abcde"); std::string::reverse_iterator ri(s.rbegin()); - if (*ri != 'e') - ++failures; + VERIFY( *ri == 'e' ); std::iterator_traits<std::string::reverse_iterator>::difference_type d; d = s.rend() - ri; - if (d != 5) - ++failures; + VERIFY( d == 5 ); const std::string cs("abcde"); std::string::const_reverse_iterator cri(cs.rend()); - if (cri - 5 != cs.rbegin()) - ++failures; - - return failures; -} - -// the following should be compiler errors -// flag runtime errors in case they slip through the compiler -int -wrong_stuff() -{ - int failures(0); - -#ifdef ITER24_F1 - extern void f(std::vector<std::string*>::iterator); - std::vector<std::string*> vs[2]; - f(vs); // address of array is not an iterator - failures++; -#endif - -#ifdef ITER24_F2 - std::string s; - char *i = s.begin(); // begin() doesn't return a pointer - failures++; -#endif - -#ifdef ITER24_F3 - std::string::const_iterator ci; - std::string::iterator i; - if (i - ci) // remove const_ is a warning - i++; - // failures++; only a warning -#endif - -#ifdef ITER24_F4 - std::vector<char>::iterator iv; - std::string::iterator is(iv);// vector<char> is not string - failures++; -#endif - -#ifdef ITER24_F5 - std::vector<char>::iterator iv; - std::string::iterator is; - if (iv == is) // vector<char> is not string - ++iv; - failures++; -#endif - -#ifdef ITER24_F6 - std::vector<char>::const_iterator ci; - std::vector<char>::iterator i = ci; // remove const_ is a warning - ++i; - // failures++; only a warning -#endif - -#ifdef ITER24_F7 - std::vector<int> v(1); - std::vector<int>::const_iterator ci(v.begin()); - *ci = 1; // cannot assign through const_iterator - failures++; -#endif - -#ifdef ITER24_F8 - std::vector<const int> v(1); - std::vector<const int>::reference r(v.begin()[0]); - r = 1; // cannot assign through reference to const - failures++; -#endif - - return failures; + VERIFY( cri - 5 == cs.rbegin() ); } // libstdc++/6642 -int +void test6642() { std::string s; std::string::iterator it = s.begin(); std::string::const_iterator cit = s.begin(); - - return it - cit; + VERIFY( (it - cit) == 0 ); } int main() { - int failures(0); - - failures += string_stuff(); - - failures += vector_stuff(); - - failures += reverse_stuff(); - - failures += wrong_stuff(); - - failures += test6642(); - - VERIFY(failures == 0); + string_stuff(); + vector_stuff(); + reverse_stuff(); + test6642(); return 0; } diff --git a/libstdc++-v3/testsuite/24_iterators/range_generators/lwg3899.cc b/libstdc++-v3/testsuite/24_iterators/range_generators/lwg3899.cc new file mode 100644 index 0000000..5a812ec --- /dev/null +++ b/libstdc++-v3/testsuite/24_iterators/range_generators/lwg3899.cc @@ -0,0 +1,57 @@ +// { dg-do run { target c++23 } } + +// LWG 3899. +// co_yielding elements of an lvalue generator is unnecessarily inefficient + +#include <generator> +#include <memory_resource> +#include <testsuite_hooks.h> + +struct memory_resource : std::pmr::memory_resource +{ + std::size_t count = 0; + + void* do_allocate(std::size_t n, std::size_t a) override + { + count += n; + return std::pmr::new_delete_resource()->allocate(n, a); + } + + void do_deallocate(void* p, std::size_t n, std::size_t a) override + { + return std::pmr::new_delete_resource()->deallocate(p, n, a); + } + + bool do_is_equal(const std::pmr::memory_resource& mr) const noexcept override + { return this == &mr; } +}; + +std::pmr::generator<int> +f(std::allocator_arg_t, std::pmr::polymorphic_allocator<>, int init) +{ + co_yield init + 0; + co_yield init + 1; +} + +std::pmr::generator<int> +g(std::allocator_arg_t, std::pmr::polymorphic_allocator<> alloc) +{ + auto gen = f(std::allocator_arg, alloc, 0); + auto gen2 = f(std::allocator_arg, alloc, 2); + co_yield std::ranges::elements_of(std::move(gen), alloc); + co_yield std::ranges::elements_of(gen2, alloc); +} + +int +main() +{ + std::size_t counts[4]; + memory_resource mr; + for (auto d : g(std::allocator_arg , &mr)) + counts[d] = mr.count; + VERIFY(counts[0] != 0); + // No allocations after the first one: + VERIFY(counts[1] == counts[0]); + VERIFY(counts[2] == counts[0]); + VERIFY(counts[3] == counts[0]); +} diff --git a/libstdc++-v3/testsuite/24_iterators/range_generators/lwg3900.cc b/libstdc++-v3/testsuite/24_iterators/range_generators/lwg3900.cc index 957879e..08fd5c2 100644 --- a/libstdc++-v3/testsuite/24_iterators/range_generators/lwg3900.cc +++ b/libstdc++-v3/testsuite/24_iterators/range_generators/lwg3900.cc @@ -13,4 +13,5 @@ bar(std::allocator_arg_t, std::pmr::memory_resource& mr) // { dg-error "here" } } // { dg-error "static assertion failed" "" { target *-*-* } 0 } -// { dg-error "no matching function .*memory_resource&" "" { target *-*-* } 0 } +// { dg-error "could not convert 'const std::pmr::memory_resource'" "" { target *-*-* } 0 } +// { dg-error "no matching function \[^\n\r\]*memory_resource&" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/24_iterators/range_operations/122224.cc b/libstdc++-v3/testsuite/24_iterators/range_operations/122224.cc new file mode 100644 index 0000000..62bea92 --- /dev/null +++ b/libstdc++-v3/testsuite/24_iterators/range_operations/122224.cc @@ -0,0 +1,100 @@ +// { dg-do run { target c++11 } } +// { dg-add-options no_pch } + +// Undefine these if present in runtest flags. +#undef _GLIBCXX_ASSERTIONS +#undef _GLIBCXX_DEBUG + +// Prevent assertions from being automatically enabled at -O0 +#define _GLIBCXX_NO_ASSERTIONS + +#include <iterator> +#include <testsuite_iterators.h> +#include <testsuite_hooks.h> + +template<typename Container> +void +test_advance() +{ + int a[] = { 1, 2, 3 }; + Container c(a); + auto iter = c.begin(); + + // This call violates the precondition for std::advance, + // but with assertions disabled we do not diagnose it. + std::advance(iter, -1); + + // However we do guarantee that erroneously decrementing + // an input iterator is a no-op and does no harm. + VERIFY( *iter == 1 ); + + ++iter; + std::advance(iter, -999); + VERIFY( *iter == 2 ); + + std::advance(iter, 0); + VERIFY( *iter == 2 ); + std::advance(iter, 1); + VERIFY( *iter == 3 ); +} + +template<typename Container> +void +test_prev() +{ + int a[] = { 1, 2, 3 }; + Container c(a); + auto iter = c.begin(); + + // This calls std::advance(iter, -1), which violates the precondition. + iter = std::prev(iter); + + // As above, we turn the std::prev call into a no-op. + VERIFY( *iter == 1 ); + + ++iter; + iter = std::prev(iter, 999); + VERIFY( *iter == 2 ); + + iter = std::prev(iter, 0); + VERIFY( *iter == 2 ); + iter = std::prev(iter, -1); + VERIFY( *iter == 3 ); +} + +template<typename Container> +void +test_next() +{ + int a[] = { 1, 2, 3 }; + Container c(a); + auto iter = c.begin(); + + // This calls std::advance(iter, -1), which violates the precondition. + iter = std::next(iter, -1); + + // As above, we turn the std::prev call into a no-op. + VERIFY( *iter == 1 ); + + ++iter; + iter = std::next(iter, -999); + VERIFY( *iter == 2 ); + + iter = std::next(iter, 0); + VERIFY( *iter == 2 ); + iter = std::next(iter); + VERIFY( *iter == 3 ); +} + +int main() +{ + using InputContainer = __gnu_test::input_container<int>; + test_advance<InputContainer>(); + test_prev<InputContainer>(); + test_next<InputContainer>(); + + using ForwardContainer = __gnu_test::forward_container<int>; + test_advance<ForwardContainer>(); + test_prev<ForwardContainer>(); + test_next<ForwardContainer>(); +} diff --git a/libstdc++-v3/testsuite/24_iterators/range_operations/advance.cc b/libstdc++-v3/testsuite/24_iterators/range_operations/advance.cc index 2f48181..8229b1e 100644 --- a/libstdc++-v3/testsuite/24_iterators/range_operations/advance.cc +++ b/libstdc++-v3/testsuite/24_iterators/range_operations/advance.cc @@ -42,7 +42,7 @@ test01() std::ranges::advance(iter, -2); VERIFY( iter == r.begin() ); - std::ranges::advance(iter, r.begin() + 1); + std::ranges::advance(iter, std::ranges::next(r.begin())); VERIFY( iter != r.begin() ); VERIFY( iter != r.end() ); std::ranges::advance(iter, r.begin()); diff --git a/libstdc++-v3/testsuite/25_algorithms/copy/debug/constexpr_neg.cc b/libstdc++-v3/testsuite/25_algorithms/copy/debug/constexpr_neg.cc index 0e80977..3840524 100644 --- a/libstdc++-v3/testsuite/25_algorithms/copy/debug/constexpr_neg.cc +++ b/libstdc++-v3/testsuite/25_algorithms/copy/debug/constexpr_neg.cc @@ -33,7 +33,7 @@ test1() } static_assert(test1()); // { dg-error "non-constant condition" } -// { dg-error "_Error_formatter::_M_error()" "" { target *-*-* } 0 } +// { dg-error "_Error_formatter::(_M_error|_S_at)" "" { target *-*-* } 0 } constexpr bool test2() diff --git a/libstdc++-v3/testsuite/25_algorithms/copy_backward/debug/constexpr_neg.cc b/libstdc++-v3/testsuite/25_algorithms/copy_backward/debug/constexpr_neg.cc index 410c235..d5d84b1 100644 --- a/libstdc++-v3/testsuite/25_algorithms/copy_backward/debug/constexpr_neg.cc +++ b/libstdc++-v3/testsuite/25_algorithms/copy_backward/debug/constexpr_neg.cc @@ -35,4 +35,4 @@ test() static_assert(test()); // { dg-error "non-constant condition" } -// { dg-prune-output "_Error_formatter::_M_error()" } +// { dg-prune-output "_Error_formatter::(_M_error|_S_at)" } diff --git a/libstdc++-v3/testsuite/25_algorithms/ends_with/1.cc b/libstdc++-v3/testsuite/25_algorithms/ends_with/1.cc new file mode 100644 index 0000000..612c27a --- /dev/null +++ b/libstdc++-v3/testsuite/25_algorithms/ends_with/1.cc @@ -0,0 +1,165 @@ +// { dg-do run { target c++23 } } + +#include <algorithm> +#include <ranges> + +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> + +namespace ranges = std::ranges; + +template<typename Range1, typename Range2> +void +test01() +{ + int n[] = {1,2,3,4,5,6,7,8,9,10}; + + Range1 haystack(n, n+10); + Range2 needle(n+7, n+10); + VERIFY( ranges::ends_with(haystack, needle) ); + + haystack = Range1(n); + needle = Range2(n, n+10); + VERIFY( ranges::ends_with(haystack, needle) ); + + haystack = Range1(n); + needle = Range2(n+6, n+9); + VERIFY( !ranges::ends_with(haystack, needle) ); + + haystack = Range1(n); + needle = Range2(n+6, n+9); + VERIFY( ranges::ends_with(haystack, needle, + [](int n, int m) { return std::abs(n - m) <= 1; }) ); + + haystack = Range1(n); + needle = Range2(n+6, n+9); + VERIFY( ranges::ends_with(haystack, needle, + ranges::equal_to{}, + [](int n) { return n - 1; }) ); + + haystack = Range1(n); + needle = Range2(n+6, n+9); + VERIFY( ranges::ends_with(haystack, needle, + ranges::equal_to{}, + std::identity{}, + [](int n) { return n + 1; }) ); + + haystack = Range1(n, n+5); + needle = Range2(n, n+10); + VERIFY( !ranges::ends_with(haystack, needle) ); +} + +template<typename Range1, typename Range2> +void +test02() +{ + int n[] = {1,2,3,4,5,6,7,8,9,10}; + + Range1 haystack(n, n+10); + Range2 needle(n+7, n+10); + VERIFY( ranges::ends_with(haystack.begin(), haystack.end(), + needle.begin(), needle.end()) ); + + haystack = Range1(n); + needle = Range2(n, n+10); + VERIFY( ranges::ends_with(haystack.begin(), haystack.end(), + needle.begin(), needle.end()) ); + + haystack = Range1(n); + needle = Range2(n+6, n+9); + VERIFY( !ranges::ends_with(haystack.begin(), haystack.end(), + needle.begin(), needle.end()) ); + + haystack = Range1(n); + needle = Range2(n+6, n+9); + VERIFY( ranges::ends_with(haystack.begin(), haystack.end(), + needle.begin(), needle.end(), + [](int n, int m) { return std::abs(n - m) <= 1; }) ); + + haystack = Range1(n); + needle = Range2(n+6, n+9); + VERIFY( ranges::ends_with(haystack.begin(), haystack.end(), + needle.begin(), needle.end(), + ranges::equal_to{}, + [](int n) { return n - 1; }) ); + + haystack = Range1(n); + needle = Range2(n+6, n+9); + VERIFY( ranges::ends_with(haystack.begin(), haystack.end(), + needle.begin(), needle.end(), + ranges::equal_to{}, + std::identity{}, + [](int n) { return n + 1; }) ); + + haystack = Range1(n, n+5); + needle = Range2(n, n+10); + VERIFY( !ranges::ends_with(haystack.begin(), haystack.end(), + needle.begin(), needle.end()) ); + + haystack = Range1(n, n+5); + needle = Range2(n+10, n+10); + VERIFY( ranges::ends_with(haystack.begin(), haystack.end(), + needle.begin(), needle.end()) ); +} + +void +test03() +{ + auto haystack = std::views::iota(0, 10); + auto needle = std::views::iota(5, 10); + +#if __SIZEOF_INT128__ + auto haystack_ict = std::views::iota(__int128(0), __int128(10)); + auto needle_ict = std::views::iota(__int128(5), __int128(10)); +#else + auto haystack_ict = std::views::iota(0ll, 10ll); + auto needle_ict = std::views::iota(5ll, 10ll); +#endif + + VERIFY( ranges::ends_with(haystack, needle_ict) ); + VERIFY( ranges::ends_with(haystack.begin(), haystack.end(), + needle_ict.begin(), needle_ict.end()) ); + + VERIFY( ranges::ends_with(haystack_ict, needle) ); + VERIFY( ranges::ends_with(haystack_ict.begin(), haystack_ict.end(), + needle.begin(), needle.end()) ); + + VERIFY( ranges::ends_with(haystack_ict, needle_ict) ); + VERIFY( ranges::ends_with(haystack_ict.begin(), haystack_ict.end(), + needle_ict.begin(), needle_ict.end()) ); +} + +int +main() +{ + using namespace __gnu_test; + using forward = test_forward_range<int>; + using bidirectional_common = bidirectional_container<int>; + using input_sized = test_input_sized_range<int>; + using input_sized_sent = test_sized_range_sized_sent<int, input_iterator_wrapper>; + using random_access = test_random_access_range<int>; + using random_access_sized = test_random_access_sized_range<int>; + using random_access_sized_sent = test_sized_range_sized_sent<int, random_access_iterator_wrapper>; + + test01<forward, forward>(); + test01<random_access, random_access>(); + test02<forward, forward>(); + test02<random_access, random_access>(); + + test01<bidirectional_common, bidirectional_common>(); + test02<bidirectional_common, bidirectional_common>(); + test01<bidirectional_common, forward>(); + test02<bidirectional_common, forward>(); + + test01<input_sized, input_sized>(); + test01<random_access_sized, random_access_sized>(); + // test02<input_sized, input_sized>(); constraint violation + test02<random_access_sized, random_access_sized>(); + + test01<input_sized_sent, input_sized_sent>(); + test01<random_access_sized_sent, random_access_sized_sent>(); + test02<input_sized_sent, input_sized_sent>(); + test02<random_access_sized_sent, random_access_sized_sent>(); + + test03(); +} diff --git a/libstdc++-v3/testsuite/25_algorithms/equal/debug/constexpr_neg.cc b/libstdc++-v3/testsuite/25_algorithms/equal/debug/constexpr_neg.cc index cbc7509..6c1531d 100644 --- a/libstdc++-v3/testsuite/25_algorithms/equal/debug/constexpr_neg.cc +++ b/libstdc++-v3/testsuite/25_algorithms/equal/debug/constexpr_neg.cc @@ -32,7 +32,7 @@ test01() } static_assert(test01()); // { dg-error "non-constant condition" } -// { dg-error "_Error_formatter::_M_error()" "" { target *-*-* } 0 } +// { dg-error "_Error_formatter::(_M_error|_S_at)" "" { target *-*-* } 0 } constexpr bool test02() diff --git a/libstdc++-v3/testsuite/25_algorithms/fill_n/diff_type.cc b/libstdc++-v3/testsuite/25_algorithms/fill_n/diff_type.cc new file mode 100644 index 0000000..7265d39 --- /dev/null +++ b/libstdc++-v3/testsuite/25_algorithms/fill_n/diff_type.cc @@ -0,0 +1,13 @@ +// { dg-do compile { target c++11 } } + +#include <algorithm> +#include <testsuite_iterators.h> + +void +test_pr121890() +{ + // algorithms do not use iterator's difference_type for arithmetic + int a[1]; + __gnu_test::random_access_container<int> c(a); + std::fill_n(c.begin(), 1U, 0); +} diff --git a/libstdc++-v3/testsuite/25_algorithms/heap/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/heap/constrained.cc index 8037a2d..2f818e2 100644 --- a/libstdc++-v3/testsuite/25_algorithms/heap/constrained.cc +++ b/libstdc++-v3/testsuite/25_algorithms/heap/constrained.cc @@ -20,6 +20,7 @@ #include <algorithm> #include <random> +#include <ranges> #include <testsuite_hooks.h> #include <testsuite_iterators.h> @@ -52,17 +53,17 @@ test01() iter = ranges::pop_heap(rx, pred, proj); VERIFY( iter == rx.end() ); - VERIFY( *(iter-1) == 50 ); - VERIFY( ranges::is_heap_until(rx, pred, proj) == iter-1 ); + VERIFY( *ranges::prev(iter) == 50 ); + VERIFY( ranges::is_heap_until(rx, pred, proj) == ranges::prev(iter) ); - iter = ranges::pop_heap(rx.begin(), iter-1, pred, proj); - VERIFY( iter+1 == rx.end() ); - VERIFY( *(iter-1) == 49 ); - VERIFY( ranges::is_heap_until(rx, pred, proj) == iter-1 ); + iter = ranges::pop_heap(rx.begin(), ranges::prev(iter), pred, proj); + VERIFY( ranges::next(iter) == rx.end() ); + VERIFY( *ranges::prev(iter) == 49 ); + VERIFY( ranges::is_heap_until(rx, pred, proj) == ranges::prev(iter) ); - *(iter-1) = i; + *ranges::prev(iter) = i; iter = ranges::push_heap(rx.begin(), iter, pred, proj); - VERIFY( iter+1 == rx.end() ); + VERIFY( ranges::next(iter) == rx.end() ); VERIFY( ranges::is_heap_until(rx, pred, proj) == iter ); *iter = 2*i; @@ -70,9 +71,9 @@ test01() VERIFY( iter == rx.end() ); VERIFY( ranges::is_heap_until(rx, pred, proj) == iter ); - *(rx.begin()+1) *= -1; + *ranges::next(rx.begin()) *= -1; VERIFY( !ranges::is_heap(rx, pred, proj) ); - *(rx.begin()+1) *= -1; + *ranges::next(rx.begin()) *= -1; VERIFY( ranges::is_heap(rx, pred, proj) ); iter = ranges::sort_heap(rx, pred, proj); @@ -97,10 +98,55 @@ test02() return ok; } +constexpr bool +test03() +{ + // PR libstdc++/100795 - ranges::heap algos should not use std::heap directly +#if __SIZEOF_INT128__ + auto v = std::views::iota(__int128(0), __int128(20)); +#else + auto v = std::views::iota(0ll, 20ll); +#endif + + int storage[20] = {2,5,4,3,1,6,7,9,10,8,11,14,12,13,15,16,18,0,19,17}; + auto w = v | std::views::transform([&](auto i) -> int& { return storage[i]; }); + using type = decltype(w); + using cat = std::iterator_traits<std::ranges::iterator_t<type>>::iterator_category; + static_assert( std::same_as<cat, std::output_iterator_tag> ); + static_assert( std::ranges::random_access_range<type> ); + + for (int i = 1; i < 20; i++) + ranges::push_heap(w.begin(), w.begin() + i); + ranges::sort_heap(w); + VERIFY( ranges::equal(w, v) ); + ranges::make_heap(w); + auto it = ranges::pop_heap(w); + VERIFY( it[-1] == 19 ); + + for (int i = 1; i < 20; i++) + ranges::push_heap(w.begin(), w.begin() + i, std::ranges::greater{}); + ranges::sort_heap(w, std::ranges::greater{}); + VERIFY( ranges::equal(w, v | std::views::reverse) ); + ranges::make_heap(w, std::ranges::greater{}); + it = ranges::pop_heap(w, std::ranges::greater{}); + VERIFY( it[-1] == 0 ); + + for (int i = 1; i < 20; i++) + ranges::push_heap(w.begin(), w.begin() + i, std::ranges::greater{}, std::negate{}); + ranges::sort_heap(w, std::ranges::greater{}, std::negate{}); + VERIFY( ranges::equal(w, v) ); + ranges::make_heap(w, std::ranges::greater{}, std::negate{}); + it = ranges::pop_heap(w, std::ranges::greater{}, std::negate{}); + VERIFY( it[-1] == 19 ); + + return true; +} + int main() { test01<test_range>(); test01<test_container>(); static_assert(test02()); + static_assert(test03()); } diff --git a/libstdc++-v3/testsuite/25_algorithms/inplace_merge/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/inplace_merge/constrained.cc index b569c91..5634588 100644 --- a/libstdc++-v3/testsuite/25_algorithms/inplace_merge/constrained.cc +++ b/libstdc++-v3/testsuite/25_algorithms/inplace_merge/constrained.cc @@ -18,6 +18,7 @@ // { dg-do run { target c++20 } } #include <algorithm> +#include <ranges> #include <vector> #include <testsuite_hooks.h> #include <testsuite_iterators.h> @@ -60,9 +61,44 @@ test02() } +void +test03() +{ + // PR libstdc++/100795 - ranges::inplace_merge should not use + // std::inplace_merge directly +#if __SIZEOF_INT128__ + auto v = std::views::iota(__int128(0), __int128(20)); +#else + auto v = std::views::iota(0ll, 20ll); +#endif + + int storage[20] = {2,5,4,3,1,6,7,9,10,8,11,14,12,13,15,16,18,0,19,17}; + auto w = v | std::views::transform([&](auto i) -> int& { return storage[i]; }); + using type = decltype(w); + using cat = std::iterator_traits<std::ranges::iterator_t<type>>::iterator_category; + static_assert( std::same_as<cat, std::output_iterator_tag> ); + static_assert( std::ranges::random_access_range<type> ); + + ranges::sort(w | std::views::take(10)); + ranges::sort(w | std::views::drop(10)); + ranges::inplace_merge(w, w.begin() + 10); + VERIFY( ranges::equal(w, v) ); + + ranges::sort(w | std::views::take(10), std::ranges::greater{}); + ranges::sort(w | std::views::drop(10), std::ranges::greater{}); + ranges::inplace_merge(w, w.begin() + 10, std::ranges::greater{}); + VERIFY( ranges::equal(w, v | std::views::reverse) ); + + ranges::sort(w | std::views::take(10), std::ranges::greater{}, std::negate{}); + ranges::sort(w | std::views::drop(10), std::ranges::greater{}, std::negate{}); + ranges::inplace_merge(w, w.begin() + 10, std::ranges::greater{}, std::negate{}); + VERIFY( ranges::equal(w, v) ); +} + int main() { test01(); test02(); + test03(); } diff --git a/libstdc++-v3/testsuite/25_algorithms/lexicographical_compare/diff_type.cc b/libstdc++-v3/testsuite/25_algorithms/lexicographical_compare/diff_type.cc new file mode 100644 index 0000000..b790197 --- /dev/null +++ b/libstdc++-v3/testsuite/25_algorithms/lexicographical_compare/diff_type.cc @@ -0,0 +1,57 @@ +// { dg-do compile { target c++11 } } + +#include <algorithm> +#include <testsuite_iterators.h> + +struct Iter +{ + using value_type = int; + using difference_type = short; + using iterator_category = std::random_access_iterator_tag; + using pointer = const value_type*; + using reference = const value_type&; + + Iter() : p(nullptr) { } + explicit Iter(pointer p) : p(p) { } + reference operator*() const { return *p; } + pointer operator->() const { return p; } + reference operator[](difference_type n) const { return p[n]; } + Iter& operator++() { ++p; return *this; } + Iter& operator--() { --p; return *this; } + Iter operator++(int) { return Iter(p++); } + Iter operator--(int) { return Iter(p--); } + Iter& operator+=(difference_type n) { p += n; return *this; } + Iter& operator-=(difference_type n) { p -= n; return *this; } + + friend Iter operator+(Iter i, difference_type n) { return i += n; } + friend Iter operator+(difference_type n, Iter i) { return i += n; } + friend Iter operator-(Iter i, difference_type n) { return i -= n; } + friend difference_type operator-(Iter i, Iter j) { return i.p - j.p; } + + template<typename D> void operator[](D) const = delete; + template<typename D> void operator+=(D) = delete; + template<typename D> void operator-=(D) = delete; + template<typename D> friend void operator+(Iter, difference_type) = delete; + template<typename D> friend void operator+(difference_type, Iter) = delete; + template<typename D> friend void operator-(Iter, difference_type) = delete; + + friend bool operator==(Iter i, Iter j) { return i.p == j.p; } + friend bool operator!=(Iter i, Iter j) { return i.p != j.p; } + friend bool operator<(Iter i, Iter j) { return i.p < j.p; } + friend bool operator<=(Iter i, Iter j) { return i.p <= j.p; } + friend bool operator>(Iter i, Iter j) { return i.p > j.p; } + friend bool operator>=(Iter i, Iter j) { return i.p >= j.p; } + +private: + pointer p; +}; + +void +test_pr121890() +{ + // algorithms do not use iterator's difference_type for arithmetic + int a[1] = { }; + __gnu_test::random_access_container<int> c(a); + (void) std::lexicographical_compare(c.begin(), c.end(), Iter(a), Iter(a+1)); + (void) std::lexicographical_compare(Iter(a), Iter(a+1), c.begin(), c.end()); +} diff --git a/libstdc++-v3/testsuite/25_algorithms/lower_bound/debug/constexpr_partitioned_neg.cc b/libstdc++-v3/testsuite/25_algorithms/lower_bound/debug/constexpr_partitioned_neg.cc index c07145c..b44cb4b 100644 --- a/libstdc++-v3/testsuite/25_algorithms/lower_bound/debug/constexpr_partitioned_neg.cc +++ b/libstdc++-v3/testsuite/25_algorithms/lower_bound/debug/constexpr_partitioned_neg.cc @@ -43,5 +43,5 @@ test() static_assert(test()); // { dg-error "" } -// { dg-prune-output "_Error_formatter::_M_error()" } +// { dg-prune-output "_Error_formatter::(_M_error|_S_at)" } // { dg-prune-output "in 'constexpr'" } diff --git a/libstdc++-v3/testsuite/25_algorithms/lower_bound/debug/constexpr_partitioned_pred_neg.cc b/libstdc++-v3/testsuite/25_algorithms/lower_bound/debug/constexpr_partitioned_pred_neg.cc index 09ae26f..7835b30 100644 --- a/libstdc++-v3/testsuite/25_algorithms/lower_bound/debug/constexpr_partitioned_pred_neg.cc +++ b/libstdc++-v3/testsuite/25_algorithms/lower_bound/debug/constexpr_partitioned_pred_neg.cc @@ -33,4 +33,4 @@ test() static_assert(test()); // { dg-error "" } -// { dg-prune-output "_Error_formatter::_M_error()" } +// { dg-prune-output "_Error_formatter::(_M_error|_S_at)" } diff --git a/libstdc++-v3/testsuite/25_algorithms/lower_bound/debug/constexpr_valid_range_neg.cc b/libstdc++-v3/testsuite/25_algorithms/lower_bound/debug/constexpr_valid_range_neg.cc index 20eb026..911880b 100644 --- a/libstdc++-v3/testsuite/25_algorithms/lower_bound/debug/constexpr_valid_range_neg.cc +++ b/libstdc++-v3/testsuite/25_algorithms/lower_bound/debug/constexpr_valid_range_neg.cc @@ -46,5 +46,5 @@ test2() static_assert(test2()); // { dg-error "" } -// { dg-prune-output "_Error_formatter::_M_error()" } +// { dg-prune-output "_Error_formatter::(_M_error|_S_at)" } diff --git a/libstdc++-v3/testsuite/25_algorithms/max/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/max/constrained.cc index c3cd288..c6759f8 100644 --- a/libstdc++-v3/testsuite/25_algorithms/max/constrained.cc +++ b/libstdc++-v3/testsuite/25_algorithms/max/constrained.cc @@ -87,12 +87,12 @@ test04() int m; }; A r[5] = {5, 4, 3, 2, 1}; - ranges::max(r, ranges::less{}, &A::m); + (void)ranges::max(r, ranges::less{}, &A::m); VERIFY( copies == 1 ); VERIFY( moves == 0 ); copies = moves = 0; A s[5] = {1, 2, 3, 4, 5}; - ranges::max(s, ranges::less{}, &A::m); + (void)ranges::max(s, ranges::less{}, &A::m); VERIFY( copies == 5 ); VERIFY( moves == 0 ); } diff --git a/libstdc++-v3/testsuite/25_algorithms/min/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/min/constrained.cc index d5de040..7d4fa58 100644 --- a/libstdc++-v3/testsuite/25_algorithms/min/constrained.cc +++ b/libstdc++-v3/testsuite/25_algorithms/min/constrained.cc @@ -87,12 +87,12 @@ test04() int m; }; A r[5] = {5, 4, 3, 2, 1}; - ranges::min(r, ranges::less{}, &A::m); + (void)ranges::min(r, ranges::less{}, &A::m); VERIFY( copies == 5 ); VERIFY( moves == 0 ); copies = moves = 0; A s[5] = {1, 2, 3, 4, 5}; - ranges::min(s, ranges::less{}, &A::m); + (void)ranges::min(s, ranges::less{}, &A::m); VERIFY( copies == 1 ); VERIFY( moves == 0 ); } diff --git a/libstdc++-v3/testsuite/25_algorithms/minmax/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/minmax/constrained.cc index 5a5d341..8f8df43 100644 --- a/libstdc++-v3/testsuite/25_algorithms/minmax/constrained.cc +++ b/libstdc++-v3/testsuite/25_algorithms/minmax/constrained.cc @@ -99,20 +99,28 @@ test04() struct counted_less { bool operator()(int a, int b) { ++counter; return a < b; } }; - ranges::minmax({1,2}, counted_less{}); + auto p = ranges::minmax({1,2}, counted_less{}); VERIFY( counter == 1 ); + VERIFY( p.min == 1 ); + VERIFY( p.max == 2 ); counter = 0; - ranges::minmax({1,2,3}, counted_less{}); + p = ranges::minmax({1,2,3}, counted_less{}); VERIFY( counter == 3 ); + VERIFY( p.min == 1 ); + VERIFY( p.max == 3 ); counter = 0; - ranges::minmax({1,2,3,4,5,6,7,8,9,10}, counted_less{}); + p = ranges::minmax({1,2,3,4,5,6,7,8,9,10}, counted_less{}); VERIFY( counter <= 15 ); + VERIFY( p.min == 1 ); + VERIFY( p.max == 10 ); counter = 0; - ranges::minmax({10,9,8,7,6,5,4,3,2,1}, counted_less{}); + p = ranges::minmax({10,9,8,7,6,5,4,3,2,1}, counted_less{}); VERIFY( counter <= 15 ); + VERIFY( p.min == 1 ); + VERIFY( p.max == 10 ); } void diff --git a/libstdc++-v3/testsuite/25_algorithms/minmax_element/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/minmax_element/constrained.cc index 99ebf03..1eaaf07 100644 --- a/libstdc++-v3/testsuite/25_algorithms/minmax_element/constrained.cc +++ b/libstdc++-v3/testsuite/25_algorithms/minmax_element/constrained.cc @@ -70,21 +70,29 @@ test02() { bool operator()(int a, int b) { ++counter; return a < b; } }; int x[] = {1,2,3,4,5,6,7,8,9,10}; - ranges::minmax_element(x, x+2, counted_less{}); + auto p = ranges::minmax_element(x, x+2, counted_less{}); VERIFY( counter == 1 ); + VERIFY( p.min == x+0 ); + VERIFY( p.max == x+1 ); counter = 0; - ranges::minmax_element(x, x+3, counted_less{}); + p = ranges::minmax_element(x, x+3, counted_less{}); VERIFY( counter == 3 ); + VERIFY( p.min == x+0 ); + VERIFY( p.max == x+2 ); counter = 0; - ranges::minmax_element(x, counted_less{}); + p = ranges::minmax_element(x, counted_less{}); VERIFY( counter <= 15 ); + VERIFY( p.min == x+0 ); + VERIFY( p.max == x+9 ); ranges::reverse(x); counter = 0; - ranges::minmax_element(x, counted_less{}); + p = ranges::minmax_element(x, counted_less{}); VERIFY( counter <= 15 ); + VERIFY( p.min == x+9 ); + VERIFY( p.max == x+0 ); } int diff --git a/libstdc++-v3/testsuite/25_algorithms/nth_element/58800.cc b/libstdc++-v3/testsuite/25_algorithms/nth_element/58800.cc index 3989ee0..ff86c63 100644 --- a/libstdc++-v3/testsuite/25_algorithms/nth_element/58800.cc +++ b/libstdc++-v3/testsuite/25_algorithms/nth_element/58800.cc @@ -43,7 +43,7 @@ void test01() Container con(v.data(), v.data() + 7); - std::nth_element(con.begin(), con.begin() + 3, con.end()); + std::nth_element(con.begin(), std::next(con.begin(), 3), con.end()); } int main() diff --git a/libstdc++-v3/testsuite/25_algorithms/nth_element/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/nth_element/constrained.cc index 3189b22..8d3a057 100644 --- a/libstdc++-v3/testsuite/25_algorithms/nth_element/constrained.cc +++ b/libstdc++-v3/testsuite/25_algorithms/nth_element/constrained.cc @@ -20,6 +20,7 @@ #include <algorithm> #include <random> +#include <ranges> #include <testsuite_hooks.h> #include <testsuite_iterators.h> @@ -37,7 +38,7 @@ test01() auto pred = std::greater{}; auto proj = [] (int a) { return -a; }; - for (int i = 0; i < 50; i++) + for (std::ptrdiff_t i = 0; i < 50; i++) { test_range<int, random_access_iterator_wrapper> rx(x); std::ranlux48_base g(i); @@ -67,9 +68,39 @@ test02() return x[3] == 4; } +constexpr bool +test03() +{ + // PR libstdc++/100795 - ranges::sort should not use std::sort directly +#if __SIZEOF_INT128__ + auto v = std::views::iota(__int128(0), __int128(20)); +#else + auto v = std::views::iota(0ll, 20ll); +#endif + + int storage[20] = {2,5,4,3,1,6,7,9,10,8,11,14,12,13,15,16,18,0,19,17}; + auto w = v | std::views::transform([&](auto i) -> int& { return storage[i]; }); + using type = decltype(w); + using cat = std::iterator_traits<std::ranges::iterator_t<type>>::iterator_category; + static_assert( std::same_as<cat, std::output_iterator_tag> ); + static_assert( std::ranges::random_access_range<type> ); + + ranges::nth_element(w, w.begin() + 10); + VERIFY( w[10] == 10 ); + + ranges::nth_element(w, w.begin() + 5, std::ranges::greater{}); + VERIFY( w[5] == 19 - 5 ); + + ranges::nth_element(w, w.begin() + 15, std::ranges::greater{}, std::negate{}); + VERIFY( w[15] == 15 ); + + return true; +} + int main() { test01(); static_assert(test02()); + static_assert(test03()); } diff --git a/libstdc++-v3/testsuite/25_algorithms/nth_element/random_test.cc b/libstdc++-v3/testsuite/25_algorithms/nth_element/random_test.cc index 2e9d4b3..9eaef61 100644 --- a/libstdc++-v3/testsuite/25_algorithms/nth_element/random_test.cc +++ b/libstdc++-v3/testsuite/25_algorithms/nth_element/random_test.cc @@ -37,9 +37,9 @@ struct testNthElement template<typename Container, typename RandomGen> void operator()(Container con, RandomGen& rg) { - const int size = con.end() - con.begin(); + const auto size = con.end() - con.begin(); auto dist = std::uniform_int_distribution<>(0, size); - const int element = dist(rg); + const decltype(size) element = dist(rg); std::nth_element(con.begin(), con.begin() + element, con.end()); diff --git a/libstdc++-v3/testsuite/25_algorithms/partial_sort/check_compare_by_value.cc b/libstdc++-v3/testsuite/25_algorithms/partial_sort/check_compare_by_value.cc index 05f4f1c..e1ba840 100644 --- a/libstdc++-v3/testsuite/25_algorithms/partial_sort/check_compare_by_value.cc +++ b/libstdc++-v3/testsuite/25_algorithms/partial_sort/check_compare_by_value.cc @@ -43,7 +43,7 @@ test01() 17, 8, 18, 9, 19 }; const int N = sizeof(s1) / sizeof(V); Container con(s1, s1 + N); - std::partial_sort(con.begin(), con.begin() + 10, con.end()); + std::partial_sort(con.begin(), std::next(con.begin(), 10), con.end()); VERIFY( s1[0].ok ); for(int i = 1; i < 10; ++i) VERIFY( s1[i].val > s1[i - 1].val && s1[i].ok ); @@ -59,7 +59,7 @@ test02() 17, 8, 18, 9, 19 }; const int N = sizeof(s1) / sizeof(V); Container con(s1, s1 + N); - std::partial_sort(con.begin(), con.begin() + 10, con.end(), + std::partial_sort(con.begin(), std::next(con.begin(), 10), con.end(), __gnu_test::order); VERIFY( s1[0].ok ); for(int i = 1; i < 10; ++i) diff --git a/libstdc++-v3/testsuite/25_algorithms/partial_sort/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/partial_sort/constrained.cc index 032ffe7..554a8d7 100644 --- a/libstdc++-v3/testsuite/25_algorithms/partial_sort/constrained.cc +++ b/libstdc++-v3/testsuite/25_algorithms/partial_sort/constrained.cc @@ -47,7 +47,8 @@ test01() ranges::shuffle(c, g1); ranges::shuffle(ranges::begin(r), ranges::end(r), g2); - for (unsigned middle = 0; middle < std::min(size, 10U); ++middle) + for (std::ptrdiff_t middle = 0, end = std::min(size, 10U); + middle < end; ++middle) { auto res1 = ranges::partial_sort(c.begin(), c.begin()+middle, c.end(), {}, std::negate<>{}); diff --git a/libstdc++-v3/testsuite/25_algorithms/partial_sort/random_test.cc b/libstdc++-v3/testsuite/25_algorithms/partial_sort/random_test.cc index 89eb992..4e69000 100644 --- a/libstdc++-v3/testsuite/25_algorithms/partial_sort/random_test.cc +++ b/libstdc++-v3/testsuite/25_algorithms/partial_sort/random_test.cc @@ -37,9 +37,9 @@ struct testPartialSort template<typename Container, typename RandomGen> void operator()(Container con, RandomGen& rg) { - const int size = con.end() - con.begin(); + const auto size = con.end() - con.begin(); auto dist = std::uniform_int_distribution<>(0, size); - const int element = dist(rg); + const decltype(size) element = dist(rg); std::partial_sort(con.begin(), con.begin() + element, con.end()); diff --git a/libstdc++-v3/testsuite/25_algorithms/partial_sort_copy/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/partial_sort_copy/constrained.cc index a0bd48b..38b2dda 100644 --- a/libstdc++-v3/testsuite/25_algorithms/partial_sort_copy/constrained.cc +++ b/libstdc++-v3/testsuite/25_algorithms/partial_sort_copy/constrained.cc @@ -35,7 +35,7 @@ namespace ranges = std::ranges; void test01() { - for (unsigned size = 0; size < 50; ++size) + for (std::ptrdiff_t size = 0; size < 50; ++size) { std::vector<int> vref(size); std::iota(vref.begin(), vref.end(), 0); @@ -45,7 +45,7 @@ test01() ranges::shuffle(v1, g1); ranges::shuffle(v2, g2); - for (unsigned middle = 0; middle < 10; ++middle) + for (std::ptrdiff_t middle = 0; middle < 10; ++middle) { test_container<int, forward_iterator_wrapper> c = {v1.data(), v1.data() + size}; diff --git a/libstdc++-v3/testsuite/25_algorithms/partial_sort_copy/random_test.cc b/libstdc++-v3/testsuite/25_algorithms/partial_sort_copy/random_test.cc index 2c0b8bc..be033a2 100644 --- a/libstdc++-v3/testsuite/25_algorithms/partial_sort_copy/random_test.cc +++ b/libstdc++-v3/testsuite/25_algorithms/partial_sort_copy/random_test.cc @@ -38,9 +38,9 @@ struct testPartialSortCopy template<typename Container, typename RandomGen> void operator()(Container con, RandomGen& rg) { - const int size = con.end() - con.begin(); + const auto size = con.end() - con.begin(); auto dist = std::uniform_int_distribution<>(0, size); - const int element = dist(rg); + const decltype(size) element = dist(rg); std::vector<int> outvec(element + 1); // add +1 to avoid empty issues diff --git a/libstdc++-v3/testsuite/25_algorithms/remove_if/120789.cc b/libstdc++-v3/testsuite/25_algorithms/remove_if/120789.cc new file mode 100644 index 0000000..c1f4eeb --- /dev/null +++ b/libstdc++-v3/testsuite/25_algorithms/remove_if/120789.cc @@ -0,0 +1,36 @@ +// PR libstdc++/120789 - ranges::remove_if should use ranges::iter_move +// { dg-do compile { target c++20 } } + +#include <algorithm> + +struct A +{ + bool operator==(const A&) const; +}; + +struct B +{ + B(B&&) = delete; + B& operator=(const A&) const; + + operator A() const; + bool operator==(const B&) const; +}; + +struct I +{ + using value_type = A; + using difference_type = int; + B operator*() const; + I& operator++(); + I operator++(int); + bool operator==(const I&) const; + friend A iter_move(const I&); +}; + +void +test01() +{ + std::ranges::subrange<I, I> r; + auto [begin, end] = std::ranges::remove_if(r, [](auto&&) { return true; }); +} diff --git a/libstdc++-v3/testsuite/25_algorithms/rotate/121913.cc b/libstdc++-v3/testsuite/25_algorithms/rotate/121913.cc new file mode 100644 index 0000000..d648699 --- /dev/null +++ b/libstdc++-v3/testsuite/25_algorithms/rotate/121913.cc @@ -0,0 +1,45 @@ +// { dg-do compile { target c++20 } } + +// Bug libstdc++/121913 ranges::rotate should use ranges::iter_move + +#include <algorithm> + +struct A { }; + +struct B +{ + B& operator=(const B&) = delete; + B& operator=(const A&) const; + + operator A() const; +}; + +struct I +{ + using value_type = A; + using difference_type = int; + B operator*() const; + B operator[](int) const; + I& operator++(); + I operator++(int); + I& operator--(); + I operator--(int); + I& operator+=(int); + I& operator-=(int); + + auto operator<=>(const I&) const = default; + + friend A iter_move(const I&); + friend I operator+(I, int); + friend I operator-(I, int); + friend I operator+(int, I); + friend int operator-(I, I); +}; + +static_assert( std::random_access_iterator<I> ); + +void +test_pr121913() +{ + std::ranges::rotate(I{}, I{}, I{}); +} diff --git a/libstdc++-v3/testsuite/25_algorithms/sample/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/sample/constrained.cc index b9945b1..0ec3e0b 100644 --- a/libstdc++-v3/testsuite/25_algorithms/sample/constrained.cc +++ b/libstdc++-v3/testsuite/25_algorithms/sample/constrained.cc @@ -20,6 +20,7 @@ #include <algorithm> #include <random> +#include <ranges> #include <testsuite_hooks.h> #include <testsuite_iterators.h> @@ -59,9 +60,34 @@ test01() } } +void +test02() +{ + // PR libstdc++/100795 - ranges::sample should not use std::sample +#if 0 // FIXME: ranges::sample rejects integer-class difference types. +#if __SIZEOF_INT128__ + auto v = std::views::iota(__int128(0), __int128(20)); +#else + auto v = std::views::iota(0ll, 20ll); +#endif +#else + auto v = std::views::iota(0, 20); +#endif + + int storage[20] = {2,5,4,3,1,6,7,9,10,8,11,14,12,13,15,16,18,0,19,17}; + auto w = v | std::views::transform([&](auto i) -> int& { return storage[i]; }); + using type = decltype(w); + static_assert( std::ranges::random_access_range<type> ); + + ranges::sample(v, w.begin(), 20, rng); + ranges::sort(w); + VERIFY( ranges::equal(w, v) ); +} + int main() { test01<forward_iterator_wrapper, output_iterator_wrapper>(); test01<input_iterator_wrapper, random_access_iterator_wrapper>(); + test02(); } diff --git a/libstdc++-v3/testsuite/25_algorithms/shift_left/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/shift_left/constrained.cc new file mode 100644 index 0000000..73d0614 --- /dev/null +++ b/libstdc++-v3/testsuite/25_algorithms/shift_left/constrained.cc @@ -0,0 +1,105 @@ +// Copyright (C) 2020-2025 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-do run { target c++23 } } +// This test is based on shift_left/1.cc. + +#include <algorithm> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> + +using __gnu_test::test_range; +using __gnu_test::forward_iterator_wrapper; +using __gnu_test::bidirectional_iterator_wrapper; +using __gnu_test::random_access_iterator_wrapper; + +struct X +{ + int a = -1; + bool moved_from = false; + + X() = default; + + X(int a) + : a(a) + { } + + X(const X&) = delete; + X& operator=(const X&) = delete; + + X(X&& other) + { + if (this != &other) + *this = std::move(other); + } + + X& + operator=(X&& other) + { + a = other.a; + other.moved_from = true; + moved_from = false; + return *this; + } +}; + +template<int N, template<typename> typename Wrapper> +void +test01() +{ + for (int n = 0; n < N+5; n++) + { + X x[N]; + for (int i = 0; i < N; i++) + x[i] = X{i}; + test_range<X, Wrapper> rx(x); + auto [first,out] = std::ranges::shift_left(rx.begin(), rx.end(), n); + VERIFY( first == rx.begin() ); + if (n < N) + { + VERIFY( out.ptr == x+(N-n) ); + for (int i = 0; i < N-n; i++) + { + VERIFY( x[i].a == n+i ); + VERIFY( !x[i].moved_from ); + } + for (int i = std::max(n, N-n); i < N; i++) + VERIFY( x[i].moved_from ); + } + else + { + VERIFY( out.ptr == x ); + for (int i = 0; i < N; i++) + { + VERIFY( x[i].a == i ); + VERIFY( !x[i].moved_from ); + } + } + } +} + +int +main() +{ + test01<23, forward_iterator_wrapper>(); + test01<23, bidirectional_iterator_wrapper>(); + test01<23, random_access_iterator_wrapper>(); + + test01<24, forward_iterator_wrapper>(); + test01<24, bidirectional_iterator_wrapper>(); + test01<24, random_access_iterator_wrapper>(); +} diff --git a/libstdc++-v3/testsuite/25_algorithms/shift_right/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/shift_right/constrained.cc new file mode 100644 index 0000000..0d53707 --- /dev/null +++ b/libstdc++-v3/testsuite/25_algorithms/shift_right/constrained.cc @@ -0,0 +1,104 @@ +// Copyright (C) 2020-2025 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-do run { target c++23 } } +// This test is based on shift_right/1.cc. + +#include <algorithm> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> + +using __gnu_test::test_range; +using __gnu_test::forward_iterator_wrapper; +using __gnu_test::bidirectional_iterator_wrapper; +using __gnu_test::random_access_iterator_wrapper; + +struct X +{ + int a = -1; + bool moved_from = false; + + X() = default; + + X(int a) + : a(a) + { } + + X(const X&) = delete; + X& operator=(const X&) = delete; + + X(X&& other) + { + if (this != &other) + *this = std::move(other); + } + + X& + operator=(X&& other) + { + a = other.a; + other.moved_from = true; + moved_from = false; + return *this; + } +}; + +template<int N, template<typename> typename Wrapper> +void +test01() +{ + for (int n = 0; n < N+5; n++) + { + X x[N]; + for (int i = 0; i < N; i++) + x[i] = X(i); + test_range<X, Wrapper> rx(x); + auto [out,last] = std::ranges::shift_right(rx.begin(), rx.end(), n); + VERIFY( last == rx.end() ); + if (n < N) + { + VERIFY( out.ptr == x+n ); + for (int i = n; i < N; i++) + VERIFY( x[i].a == i-n ); + for (int i = 0; i < std::min(n, N-n); i++) + VERIFY( x[i].moved_from ); + for (int i = std::min(n, N-n); i < std::max(n, N-n); i++) + VERIFY( !x[i].moved_from ); + } + else + { + VERIFY( out.ptr == x+N ); + for (int i = 0; i < N; i++) + { + VERIFY( x[i].a == i ); + VERIFY( !x[i].moved_from ); + } + } + } +} + +int +main() +{ + test01<23, forward_iterator_wrapper>(); + test01<23, bidirectional_iterator_wrapper>(); + test01<23, random_access_iterator_wrapper>(); + + test01<24, forward_iterator_wrapper>(); + test01<24, bidirectional_iterator_wrapper>(); + test01<24, random_access_iterator_wrapper>(); +} diff --git a/libstdc++-v3/testsuite/25_algorithms/shuffle/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/shuffle/constrained.cc index d0977a2..4d63356 100644 --- a/libstdc++-v3/testsuite/25_algorithms/shuffle/constrained.cc +++ b/libstdc++-v3/testsuite/25_algorithms/shuffle/constrained.cc @@ -20,6 +20,7 @@ #include <algorithm> #include <random> +#include <ranges> #include <vector> #include <testsuite_hooks.h> #include <testsuite_iterators.h> @@ -62,8 +63,50 @@ test01() } } +void +test02() +{ + // PR libstdc++/100795 - ranges::shuffle should not use std::shuffle directly +#if 0 // FIXME: ranges::shuffle rejects integer-class difference types. +#if __SIZEOF_INT128__ + auto v = std::views::iota(__int128(0), __int128(20)); +#else + auto v = std::views::iota(0ll, 20ll); +#endif +#else + auto v = std::views::iota(0, 20); +#endif + + int storage[20] = {2,5,4,3,1,6,7,9,10,8,11,14,12,13,15,16,18,0,19,17}; + auto w = v | std::views::transform([&](auto i) -> int& { return storage[i]; }); + using type = decltype(w); + static_assert( std::ranges::random_access_range<type> ); + + std::ranlux48_base g; + ranges::shuffle(w, g); +} + +struct non_default_sentinel_t { }; + +template<std::input_or_output_iterator I> +bool operator==(const I& i, non_default_sentinel_t) +{ return i == std::default_sentinel; } + +void +test03() +{ + // PR libstdc++/121917 - ranges::shuffle incorrectly requires its arguments + // to model sized_sentinel_for + int a[2]{}; + std::counted_iterator iter(a, 2); + std::default_random_engine e; + std::ranges::shuffle(iter, non_default_sentinel_t{}, e); +} + int main() { test01(); + test02(); + test03(); } diff --git a/libstdc++-v3/testsuite/25_algorithms/sort/118209.cc b/libstdc++-v3/testsuite/25_algorithms/sort/118209.cc new file mode 100644 index 0000000..6dedbde --- /dev/null +++ b/libstdc++-v3/testsuite/25_algorithms/sort/118209.cc @@ -0,0 +1,23 @@ +// PR libstdc++ - ranges::sort doesn't use iter_move, cannot sort zip of move-only type +// { dg-do compile { target c++23 } } + +#include <algorithm> +#include <ranges> +#include <vector> + +struct F { + int a = -1; + explicit F(int d) : a(d) { } + F(const F&) = delete; + F(F&& o) : a(o.a) { } + void operator=(const F&) = delete; + F& operator=(F&& o) { return *this; } + auto operator<=>(const F&) const = default; +}; + +int main () { + int a[] = {3,2,1}; + std::vector<F> v(a, a+3); + std::ranges::sort(v); // OK + std::ranges::sort(std::views::zip(v)); // didn't compile +} diff --git a/libstdc++-v3/testsuite/25_algorithms/sort/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/sort/constrained.cc index 82754af..930dbd7 100644 --- a/libstdc++-v3/testsuite/25_algorithms/sort/constrained.cc +++ b/libstdc++-v3/testsuite/25_algorithms/sort/constrained.cc @@ -20,6 +20,7 @@ #include <algorithm> #include <random> +#include <ranges> #include <vector> #include <testsuite_hooks.h> #include <testsuite_iterators.h> @@ -72,9 +73,39 @@ test02() && ranges::equal(x, y, {}, &X::i, &X::i)); } +constexpr bool +test03() +{ + // PR libstdc++/100795 - ranges::sort should not use std::sort directly +#if __SIZEOF_INT128__ + auto v = std::views::iota(__int128(0), __int128(20)); +#else + auto v = std::views::iota(0ll, 20ll); +#endif + + int storage[20] = {2,5,4,3,1,6,7,9,10,8,11,14,12,13,15,16,18,0,19,17}; + auto w = v | std::views::transform([&](auto i) -> int& { return storage[i]; }); + using type = decltype(w); + using cat = std::iterator_traits<std::ranges::iterator_t<type>>::iterator_category; + static_assert( std::same_as<cat, std::output_iterator_tag> ); + static_assert( std::ranges::random_access_range<type> ); + + ranges::sort(w); + VERIFY( ranges::equal(w, v) ); + + ranges::sort(w, std::ranges::greater{}); + VERIFY( ranges::equal(w, v | std::views::reverse) ); + + ranges::sort(w, std::ranges::greater{}, std::negate{}); + VERIFY( ranges::equal(w, v) ); + + return true; +} + int main() { test01(); static_assert(test02()); + static_assert(test03()); } diff --git a/libstdc++-v3/testsuite/25_algorithms/stable_partition/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/stable_partition/constrained.cc index fc11c64..4dc2678 100644 --- a/libstdc++-v3/testsuite/25_algorithms/stable_partition/constrained.cc +++ b/libstdc++-v3/testsuite/25_algorithms/stable_partition/constrained.cc @@ -21,6 +21,7 @@ // { dg-require-effective-target hosted } #include <algorithm> +#include <ranges> #include <testsuite_hooks.h> #include <testsuite_iterators.h> @@ -70,9 +71,34 @@ test02() } } +void +test03() +{ + // PR libstdc++/100795 - ranges::stable_partition should not use + // std::stable_partition directly +#if __SIZEOF_INT128__ + auto v = std::views::iota(__int128(0), __int128(20)); +#else + auto v = std::views::iota(0ll, 20ll); +#endif + + int storage[20] = {2,5,4,3,1,6,7,9,10,8,11,14,12,13,15,16,18,0,19,17}; + auto w = v | std::views::transform([&](auto i) -> int& { return storage[i]; }); + using type = decltype(w); + using cat = std::iterator_traits<std::ranges::iterator_t<type>>::iterator_category; + static_assert( std::same_as<cat, std::output_iterator_tag> ); + static_assert( std::ranges::random_access_range<type> ); + + auto pred = [] (int a) { return a%2==0; }; + ranges::stable_partition(w, pred); + VERIFY( ranges::all_of(w.begin(), w.begin() + 10, pred) ); + VERIFY( ranges::none_of(w.begin() + 10, w.end(), pred) ); +} + int main() { test01(); test02(); + test03(); } diff --git a/libstdc++-v3/testsuite/25_algorithms/stable_sort/constrained.cc b/libstdc++-v3/testsuite/25_algorithms/stable_sort/constrained.cc index 0bd438f..5504344 100644 --- a/libstdc++-v3/testsuite/25_algorithms/stable_sort/constrained.cc +++ b/libstdc++-v3/testsuite/25_algorithms/stable_sort/constrained.cc @@ -20,6 +20,7 @@ #include <algorithm> #include <random> +#include <ranges> #include <vector> #include <testsuite_hooks.h> #include <testsuite_iterators.h> @@ -62,8 +63,37 @@ test01() } } +void +test02() +{ + // PR libstdc++/100795 - ranges::stable_sort should not use std::stable_sort + // directly +#if __SIZEOF_INT128__ + auto v = std::views::iota(__int128(0), __int128(20)); +#else + auto v = std::views::iota(0ll, 20ll); +#endif + + int storage[20] = {2,5,4,3,1,6,7,9,10,8,11,14,12,13,15,16,18,0,19,17}; + auto w = v | std::views::transform([&](auto i) -> int& { return storage[i]; }); + using type = decltype(w); + using cat = std::iterator_traits<std::ranges::iterator_t<type>>::iterator_category; + static_assert( std::same_as<cat, std::output_iterator_tag> ); + static_assert( std::ranges::random_access_range<type> ); + + ranges::stable_sort(w); + VERIFY( ranges::equal(w, v) ); + + ranges::stable_sort(w, std::ranges::greater{}); + VERIFY( ranges::equal(w, v | std::views::reverse) ); + + ranges::stable_sort(w, std::ranges::greater{}, std::negate{}); + VERIFY( ranges::equal(w, v) ); +} + int main() { test01(); + test02(); } diff --git a/libstdc++-v3/testsuite/25_algorithms/starts_with/1.cc b/libstdc++-v3/testsuite/25_algorithms/starts_with/1.cc new file mode 100644 index 0000000..0c288d8 --- /dev/null +++ b/libstdc++-v3/testsuite/25_algorithms/starts_with/1.cc @@ -0,0 +1,158 @@ +// { dg-do run { target c++23 } } + +#include <algorithm> +#include <ranges> + +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> + +namespace ranges = std::ranges; + +template<typename Range1, typename Range2> +void +test01() +{ + int n[] = {1,2,3,4,5,6,7,8,9,10}; + + Range1 haystack(n, n+10); + Range2 needle(n, n+3); + VERIFY( ranges::starts_with(haystack, needle) ); + + haystack = Range1(n); + needle = Range2(n, n+10); + VERIFY( ranges::starts_with(haystack, needle) ); + + haystack = Range1(n); + needle = Range2(n+1, n+4); + VERIFY( !ranges::starts_with(haystack, needle) ); + + haystack = Range1(n); + needle = Range2(n+1, n+4); + VERIFY( ranges::starts_with(haystack, needle, + [](int n, int m) { return std::abs(n - m) <= 1; }) ); + + haystack = Range1(n); + needle = Range2(n+1, n+4); + VERIFY( ranges::starts_with(haystack, needle, + ranges::equal_to{}, + [](int n) { return n + 1; }) ); + + haystack = Range1(n); + needle = Range2(n+1, n+4); + VERIFY( ranges::starts_with(haystack, needle, + ranges::equal_to{}, + std::identity{}, + [](int n) { return n - 1; }) ); + + haystack = Range1(n, n+5); + needle = Range2(n, n+10); + VERIFY( !ranges::starts_with(haystack, needle) ); + + haystack = Range1(n, n+5); + needle = Range2(n+10, n+10); + VERIFY( ranges::starts_with(haystack, needle) ); +} + +template<typename Range1, typename Range2> +void +test02() +{ + int n[] = {1,2,3,4,5,6,7,8,9,10}; + + Range1 haystack(n, n+10); + Range2 needle(n, n+3); + VERIFY( ranges::starts_with(haystack.begin(), haystack.end(), + needle.begin(), needle.end()) ); + + haystack = Range1(n); + needle = Range2(n, n+10); + VERIFY( ranges::starts_with(haystack.begin(), haystack.end(), + needle.begin(), needle.end()) ); + + haystack = Range1(n); + needle = Range2(n+1, n+4); + VERIFY( !ranges::starts_with(haystack.begin(), haystack.end(), + needle.begin(), needle.end()) ); + + haystack = Range1(n); + needle = Range2(n+1, n+4); + VERIFY( ranges::starts_with(haystack.begin(), haystack.end(), + needle.begin(), needle.end(), + [](int n, int m) { return std::abs(n - m) <= 1; }) ); + + haystack = Range1(n); + needle = Range2(n+1, n+4); + VERIFY( ranges::starts_with(haystack.begin(), haystack.end(), + needle.begin(), needle.end(), + ranges::equal_to{}, + [](int n) { return n + 1; }) ); + + haystack = Range1(n); + needle = Range2(n+1, n+4); + VERIFY( ranges::starts_with(haystack.begin(), haystack.end(), + needle.begin(), needle.end(), + ranges::equal_to{}, + std::identity{}, + [](int n) { return n - 1; }) ); + + haystack = Range1(n, n+5); + needle = Range2(n, n+10); + VERIFY( !ranges::starts_with(haystack.begin(), haystack.end(), + needle.begin(), needle.end()) ); +} + +void +test03() +{ + auto haystack = std::views::iota(0, 10); + auto needle = std::views::iota(0, 5); + +#if __SIZEOF_INT128__ + auto haystack_ict = std::views::iota(__int128(0), __int128(10)); + auto needle_ict = std::views::iota(__int128(0), __int128(5)); +#else + auto haystack_ict = std::views::iota(0ll, 10ll); + auto needle_ict = std::views::iota(0ll, 5ll); +#endif + + VERIFY( ranges::starts_with(haystack, needle_ict) ); + VERIFY( ranges::starts_with(haystack.begin(), haystack.end(), + needle_ict.begin(), needle_ict.end()) ); + + VERIFY( ranges::starts_with(haystack_ict, needle) ); + VERIFY( ranges::starts_with(haystack_ict.begin(), haystack_ict.end(), + needle.begin(), needle.end()) ); + + VERIFY( ranges::starts_with(haystack_ict, needle_ict) ); + VERIFY( ranges::starts_with(haystack_ict.begin(), haystack_ict.end(), + needle_ict.begin(), needle_ict.end()) ); +} + +int +main() +{ + using namespace __gnu_test; + using input = test_input_range<int>; + using input_sized = test_input_sized_range<int>; + using input_sized_sent = test_sized_range_sized_sent<int, input_iterator_wrapper>; + using random_access = test_random_access_range<int>; + using random_access_sized = test_random_access_sized_range<int>; + using random_access_sized_sent = test_sized_range_sized_sent<int, random_access_iterator_wrapper>; + + test01<input, input>(); + test01<random_access, random_access>(); + test02<input, input>(); + test02<random_access, random_access>(); + + test01<input_sized, input_sized>(); + test01<random_access_sized, random_access_sized>(); + test02<input_sized, input_sized>(); + test02<random_access_sized, random_access_sized>(); + + test01<input_sized_sent, input_sized_sent>(); + test01<random_access_sized_sent, random_access_sized_sent>(); + test02<input_sized_sent, input_sized_sent>(); + test02<random_access_sized_sent, random_access_sized_sent>(); + + test03(); +} diff --git a/libstdc++-v3/testsuite/25_algorithms/unique/120789.cc b/libstdc++-v3/testsuite/25_algorithms/unique/120789.cc new file mode 100644 index 0000000..24b1071 --- /dev/null +++ b/libstdc++-v3/testsuite/25_algorithms/unique/120789.cc @@ -0,0 +1,36 @@ +// PR libstdc++/120789 - ranges::unique should use ranges::iter_move +// { dg-do compile { target c++20 } } + +#include <algorithm> + +struct A +{ + bool operator==(const A&) const; +}; + +struct B +{ + B(B&&) = delete; + B& operator=(const A&) const; + + operator A() const; + bool operator==(const B&) const; +}; + +struct I +{ + using value_type = A; + using difference_type = int; + B operator*() const; + I& operator++(); + I operator++(int); + bool operator==(const I&) const; + friend A iter_move(const I&); +}; + +void +test01() +{ + std::ranges::subrange<I, I> r; + auto [begin, end] = std::ranges::unique(r); +} diff --git a/libstdc++-v3/testsuite/25_algorithms/unique_copy/120384.cc b/libstdc++-v3/testsuite/25_algorithms/unique_copy/120384.cc new file mode 100644 index 0000000..27cd337 --- /dev/null +++ b/libstdc++-v3/testsuite/25_algorithms/unique_copy/120384.cc @@ -0,0 +1,12 @@ +// { dg-options "-D_GLIBCXX_CONCEPT_CHECKS" } +// { dg-do compile } + +// PR 120384 _BinaryPredicateConcept checks in std::unique_copy are wrong + +#include <algorithm> + +void +test_pr120384(const int* first, const int* last, int* out) +{ + std::unique_copy(first, last, out); +} diff --git a/libstdc++-v3/testsuite/25_algorithms/unique_copy/lwg2439.cc b/libstdc++-v3/testsuite/25_algorithms/unique_copy/lwg2439.cc new file mode 100644 index 0000000..f2ec3e3 --- /dev/null +++ b/libstdc++-v3/testsuite/25_algorithms/unique_copy/lwg2439.cc @@ -0,0 +1,127 @@ +// { dg-do run } + +#include <algorithm> +#include <testsuite_iterators.h> + +using namespace __gnu_test; + +int out[4]; +short out_shrt[4]; +short in[7] = { 1, 2, 2, 2, 3, 4, 4 }; + +template<typename T> +void +check_and_reset(T* p) +{ + VERIFY( p[0] == 1 ); + VERIFY( p[1] == 2 ); + VERIFY( p[2] == 3 ); + VERIFY( p[3] == 4 ); + std::fill_n(p, 4, 0); +} + +struct Eq +{ + bool operator()(short i, short j) const { return i == j; } + bool operator()(short, int) const { VERIFY(false); } + bool operator()(int, short) const { VERIFY(false); } +}; + +struct Eq2 +{ + bool operator()(const short& i, const short& j) const + { + // Both arguments should be elements of the 'in' array. + VERIFY( in+0 <= &i && &i < in+7 ); + VERIFY( in+0 <= &j && &j < in+7 ); + VERIFY( &i < &j ); + return i == j; + } + bool operator()(short, int) const { VERIFY(false); } + bool operator()(int, short) const { VERIFY(false); } +}; + +struct Eq3 +{ + bool operator()(const short& i, const short& j) const + { + // First argument should be element of the 'out' array. + // Second argument should be element of the 'in' array. + VERIFY( out_shrt+0 <= &i && &i < out_shrt+4 ); + VERIFY( in+0 <= &j && &j < in+7 ); + return i == j; + } + bool operator()(short, int) const { VERIFY(false); } + bool operator()(int, short) const { VERIFY(false); } +}; + +void +test_forward_source() +{ + // The input range uses forward iterators + test_container<short, forward_iterator_wrapper> inc(in); + test_container<int, output_iterator_wrapper> outc(out); + std::unique_copy(inc.begin(), inc.end(), outc.begin()); + check_and_reset(out); + + test_container<short, forward_iterator_wrapper> inc2(in); + test_container<int, output_iterator_wrapper> outc2(out); + std::unique_copy(inc2.begin(), inc2.end(), outc2.begin(), Eq2()); + check_and_reset(out); +} + +void +test_output_dest() +{ + // The input range uses input iterators. + // The output range uses output iterators. + test_container<short, input_iterator_wrapper> inc(in); + test_container<int, output_iterator_wrapper> outc(out); + std::unique_copy(inc.begin(), inc.end(), outc.begin()); + check_and_reset(out); + + test_container<short, input_iterator_wrapper> inc2(in); + test_container<int, output_iterator_wrapper> outc2(out); + std::unique_copy(inc2.begin(), inc2.end(), outc2.begin(), Eq()); + check_and_reset(out); +} + +void +test_forward_dest_diff_type() +{ + // The input range uses input iterators. + // The output range uses forward iterators, but with different value type. + test_container<short, input_iterator_wrapper> inc(in); + test_container<int, forward_iterator_wrapper> outc(out); + std::unique_copy(inc.begin(), inc.end(), outc.begin()); + check_and_reset(out); + + test_container<short, input_iterator_wrapper> inc2(in); + test_container<int, forward_iterator_wrapper> outc2(out); + std::unique_copy(inc2.begin(), inc2.end(), outc2.begin(), Eq()); + check_and_reset(out); +} + +void +test_forward_dest_same_type() +{ + // The input range uses input iterators. + // The output range uses forward iterators, with same value type. + test_container<short, input_iterator_wrapper> inc(in); + test_container<short, forward_iterator_wrapper> outc(out_shrt); + std::unique_copy(inc.begin(), inc.end(), outc.begin()); + check_and_reset(out_shrt); + + test_container<short, input_iterator_wrapper> inc2(in); + test_container<short, forward_iterator_wrapper> outc2(out_shrt); + std::unique_copy(inc2.begin(), inc2.end(), outc2.begin(), Eq3()); + check_and_reset(out_shrt); +} + +int main() +{ + test_forward_source(); + test_output_dest(); + test_forward_dest_diff_type(); + test_forward_dest_same_type(); +} diff --git a/libstdc++-v3/testsuite/25_algorithms/upper_bound/debug/constexpr_partitioned_neg.cc b/libstdc++-v3/testsuite/25_algorithms/upper_bound/debug/constexpr_partitioned_neg.cc index ffe5d77..950d432 100644 --- a/libstdc++-v3/testsuite/25_algorithms/upper_bound/debug/constexpr_partitioned_neg.cc +++ b/libstdc++-v3/testsuite/25_algorithms/upper_bound/debug/constexpr_partitioned_neg.cc @@ -43,4 +43,4 @@ test() static_assert(test()); // { dg-error "" } -// { dg-prune-output "_Error_formatter::_M_error()" } +// { dg-prune-output "_Error_formatter::(_M_error|_S_at)" } diff --git a/libstdc++-v3/testsuite/25_algorithms/upper_bound/debug/constexpr_partitioned_pred_neg.cc b/libstdc++-v3/testsuite/25_algorithms/upper_bound/debug/constexpr_partitioned_pred_neg.cc index a1c2ce6..41eb00c 100644 --- a/libstdc++-v3/testsuite/25_algorithms/upper_bound/debug/constexpr_partitioned_pred_neg.cc +++ b/libstdc++-v3/testsuite/25_algorithms/upper_bound/debug/constexpr_partitioned_pred_neg.cc @@ -33,4 +33,4 @@ test() static_assert(test()); // { dg-error "" } -// { dg-prune-output "_Error_formatter::_M_error()" } +// { dg-prune-output "_Error_formatter::(_M_error|_S_at)" } diff --git a/libstdc++-v3/testsuite/25_algorithms/upper_bound/debug/constexpr_valid_range_neg.cc b/libstdc++-v3/testsuite/25_algorithms/upper_bound/debug/constexpr_valid_range_neg.cc index c7c9e3b..032c8d3 100644 --- a/libstdc++-v3/testsuite/25_algorithms/upper_bound/debug/constexpr_valid_range_neg.cc +++ b/libstdc++-v3/testsuite/25_algorithms/upper_bound/debug/constexpr_valid_range_neg.cc @@ -46,4 +46,4 @@ test2() static_assert(test2()); // { dg-error "" } -// { dg-prune-output "_Error_formatter::_M_error()" } +// { dg-prune-output "_Error_formatter::(_M_error|_S_at)" } diff --git a/libstdc++-v3/testsuite/26_numerics/complex/fabs_neg.cc b/libstdc++-v3/testsuite/26_numerics/complex/fabs_neg.cc new file mode 100644 index 0000000..36c483e --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/complex/fabs_neg.cc @@ -0,0 +1,13 @@ +// { dg-do compile } +// Bug 120235 std::fabs(const std::complex<T>&) should not be defined + +#include <complex> + +void test_pr120235(std::complex<double> c) +{ + (void) std::fabs(c); + // { dg-error "no matching function" "" { target c++98_only } 8 } + // { dg-warning "deprecated: use 'std::abs'" "" { target c++11 } 8 } +} + +// { dg-prune-output "no type named '__type'" } diff --git a/libstdc++-v3/testsuite/26_numerics/headers/cmath/82644.cc b/libstdc++-v3/testsuite/26_numerics/headers/cmath/82644.cc index 3274f05..40abb2c 100644 --- a/libstdc++-v3/testsuite/26_numerics/headers/cmath/82644.cc +++ b/libstdc++-v3/testsuite/26_numerics/headers/cmath/82644.cc @@ -15,8 +15,9 @@ // with this library; see the file COPYING3. If not see // <http://www.gnu.org/licenses/>. -// { dg-options "-D__STDCPP_WANT_MATH_SPEC_FUNCS__ -D__STRICT_ANSI__" } +// { dg-options "-D__STDCPP_WANT_MATH_SPEC_FUNCS__" } // { dg-do compile { target c++11 } } +// // { dg-add-options strict_std } #define conf_hyperg 1 #define conf_hypergf 2 diff --git a/libstdc++-v3/testsuite/26_numerics/headers/cstdlib/abs128.cc b/libstdc++-v3/testsuite/26_numerics/headers/cstdlib/abs128.cc new file mode 100644 index 0000000..cfb0562 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/headers/cstdlib/abs128.cc @@ -0,0 +1,16 @@ +// { dg-do compile } +// { dg-add-options strict_std } + +#include <cstdlib> + +template<typename T> T same_type(T, T) { return T(); } + +#ifdef __SIZEOF_INT128__ +__int128 i = 0; +__int128 j = same_type(std::abs(i), i); +#endif + +#ifdef __SIZEOF_FLOAT128__ +__float128 f = 0.0; +__float128 g = same_type(std::abs(f), f); +#endif diff --git a/libstdc++-v3/testsuite/26_numerics/pstl/numeric_ops/108236.cc b/libstdc++-v3/testsuite/26_numerics/pstl/numeric_ops/108236.cc index e0e3027..cbef8ab 100644 --- a/libstdc++-v3/testsuite/26_numerics/pstl/numeric_ops/108236.cc +++ b/libstdc++-v3/testsuite/26_numerics/pstl/numeric_ops/108236.cc @@ -8,30 +8,6 @@ #include <execution> #include <testsuite_hooks.h> -struct Mint -{ - Mint(int i = 0) : val(i) { } - Mint(Mint&&) = default; - Mint& operator=(Mint&&) = default; - - operator int() const { return val; } - -private: - int val; -}; - -void -test_move_only() -{ - const int input[]{10, 20, 30}; - int output[3]; - std::exclusive_scan(std::execution::seq, input, input+3, output, Mint(5), - std::plus<int>{}); - VERIFY( output[0] == 5 ); - VERIFY( output[1] == 15 ); - VERIFY( output[2] == 35 ); -} - void test_pr108236() { @@ -45,6 +21,5 @@ test_pr108236() int main() { - test_move_only(); test_pr108236(); } diff --git a/libstdc++-v3/testsuite/26_numerics/pstl/numeric_ops/move_only.cc b/libstdc++-v3/testsuite/26_numerics/pstl/numeric_ops/move_only.cc new file mode 100644 index 0000000..38ad3c2 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/pstl/numeric_ops/move_only.cc @@ -0,0 +1,90 @@ +// { dg-options "-ltbb" } +// { dg-do run { target c++17 } } +// { dg-require-effective-target tbb_backend } + +#include <numeric> +#include <execution> +#include <testsuite_hooks.h> + +struct Mint +{ + Mint(int i = 0) : val(i) { } + + Mint(Mint&& m) : val(m.val) { m.val = -1; } + + Mint& operator=(Mint&& m) + { + val = m.val; + m.val = -1; + return *this; + } + + operator int() const + { + VERIFY(val >= 0); // Check we don't read value of a moved-from instance. + return val; + } + + friend Mint operator+(const Mint& lhs, const Mint& rhs) + { return Mint(lhs.val + rhs.val); } + +private: + int val; +}; + +void +test_reduce() +{ + Mint input[]{1, 2, 3}; + Mint m = std::reduce(std::execution::seq, input, input+3); + VERIFY( static_cast<int>(m) == 6 ); + + m = std::reduce(std::execution::seq, input, input+3, Mint(100)); + VERIFY( static_cast<int>(m) == 106 ); + + m = std::reduce(std::execution::seq, input, input+3, Mint(200), + std::plus<>{}); + VERIFY( static_cast<int>(m) == 206 ); +} + +void +test_transform_reduce() +{ +} + +void +test_exclusive_scan() +{ + const int input[]{10, 20, 30}; + int output[3]; + std::exclusive_scan(std::execution::seq, input, input+3, output, Mint(5), + std::plus<int>{}); + VERIFY( output[0] == 5 ); + VERIFY( output[1] == 15 ); + VERIFY( output[2] == 35 ); +} + +void +test_inclusive_scan() +{ +} + +void +test_transform_exclusive_scan() +{ +} + +void +test_transform_inclusive_scan() +{ +} + +int main() +{ + test_reduce(); + test_transform_reduce(); + test_exclusive_scan(); + test_inclusive_scan(); + test_transform_exclusive_scan(); + test_transform_inclusive_scan(); +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc b/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc new file mode 100644 index 0000000..33842bb --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox4x32.cc @@ -0,0 +1,26 @@ +// { dg-do run { target c++26 } } +// { dg-require-cpp-feature-test __cpp_lib_philox_engine } + +// N5014 29.5.6 Engines and engine adaptors with predefined parameters + +#include <random> +#include <testsuite_hooks.h> + +using test_type = std::philox_engine<std::uint_fast32_t, 32, 4, 10, + 0xCD9E8D57, 0x9E3779B9, + 0xD2511F53, 0xBB67AE85>; +static_assert( std::is_same_v<test_type, std::philox4x32> ); + +void +test01() +{ + std::philox4x32 a; + a.discard(9999); + + VERIFY( a() == 1955073260 ); +} + +int main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc b/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc new file mode 100644 index 0000000..11a5691 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox4x64.cc @@ -0,0 +1,26 @@ +// { dg-do run { target c++26 } } +// { dg-require-cpp-feature-test __cpp_lib_philox_engine } + +// N5014 29.5.6 Engines and engine adaptors with predefined parameters + +#include <random> +#include <testsuite_hooks.h> + +using test_type = std::philox_engine<std::uint_fast64_t, 64, 4, 10, + 0xCA5A826395121157, 0x9E3779B97F4A7C15, + 0xD2E7470EE14C6C93, 0xBB67AE8584CAA73B>; +static_assert( std::is_same_v<test_type, std::philox4x64> ); + +void +test01() +{ + std::philox4x64 a; + a.discard(9999); + + VERIFY( a() == 3409172418970261260 ); +} + +int main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc new file mode 100644 index 0000000..163aec0 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/copy.cc @@ -0,0 +1,27 @@ +// { dg-do run { target c++26 } } +// { dg-require-cpp-feature-test __cpp_lib_philox_engine } + +// N5014 29.5.4 Random Number Engine Class Templates +// N5014 29.5.4.5 Class Template philox_engine + +#include <random> +#include <testsuite_hooks.h> + +void +test01(unsigned long seed) +{ + + std::philox_engine<std::uint_fast32_t, 32, 4, 10, 0xCD9E8D57, + 0x9E3779B9, 0xD2511F53, 0xBB67AE85> e(seed); + + const auto f(e); + VERIFY( f == e ); + auto g(f); + VERIFY( g == f ); +} + +int main() +{ + test01(1ul); + test01(111ul); +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc new file mode 100644 index 0000000..183ca82 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/default.cc @@ -0,0 +1,49 @@ +// { dg-do run { target c++26 } } +// { dg-require-cpp-feature-test __cpp_lib_philox_engine } + +// N5014 29.5.4.5 Class Template philox_engine + +#include <random> +#include <testsuite_hooks.h> + +void +test_default_ctor() +{ + std::philox_engine<std::uint_fast32_t, + 32, 4, 10, 0xCD9E8D57, + 0x9E3779B9, 0xD2511F53, + 0xBB67AE85> philox4x32nullkey(0); + + VERIFY( philox4x32nullkey.min() == 0 ); + VERIFY( philox4x32nullkey.max() == (1ul << 31 | (1ul << 31) - 1) ); + VERIFY( philox4x32nullkey() == 0x6627e8d5ul ); +} + +void +test_seed() +{ + unsigned long seed = 2; + std::philox4x32 seeded(seed); + + std::philox4x32 default_init; + VERIFY( seeded != default_init ); + + std::philox4x32 default_seeded(std::philox4x32::default_seed); + VERIFY( default_seeded == default_init ); +} + +void +test_seed_seq() +{ + std::seed_seq sseq{ 1, 2, 3, 4 }; + std::philox4x32 seeded(sseq); + std::philox4x32 default_init; + VERIFY( seeded != default_init ); +} + +int main() +{ + test_default_ctor(); + test_seed(); + test_seed_seq(); +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc new file mode 100644 index 0000000..2b667b2 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/cons/seed.cc @@ -0,0 +1,51 @@ +// { dg-do run { target c++26 } } +// { dg-require-cpp-feature-test __cpp_lib_philox_engine } + +#include <random> +#include <testsuite_hooks.h> + +int f(int x) +{ + std::seed_seq sq(&x, &x + 1); + auto rnd = std::philox4x32(sq); + return std::uniform_int_distribution<int>()(rnd); +} + +int g(int x) +{ + std::seed_seq sq(&x, &x + 1); + auto rnd = std::philox4x32(); + rnd.seed(sq); + return std::uniform_int_distribution<int>()(rnd); +} + +void +test01() +{ + const int f1 = f(0); + const int f2 = f(0); + + const int g1 = g(0); + const int g2 = g(0); + + VERIFY( f1 == f2 ); + VERIFY( g1 == g2 ); + VERIFY( f1 == g1 ); +} + +void +test02() +{ + std::philox4x64 e1(25); + std::philox4x64 e2; + VERIFY( e2 != e1 ); + e2.seed(25); + VERIFY( e2 == e1 ); + +} + +int main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc new file mode 100644 index 0000000..cca1654 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/equal.cc @@ -0,0 +1,33 @@ +// { dg-do run { target c++26 } } +// { dg-require-cpp-feature-test __cpp_lib_philox_engine } + +// N5014 29.5.4.5 Class Template philox_engine + +#include <random> +#include <testsuite_hooks.h> + +void +test01() +{ + std::philox_engine<std::uint_fast32_t, + 32, 4, 10, 0xCD9E8D57, + 0x9E3779B9, 0xD2511F53, + 0xBB67AE85> x, y; + + VERIFY ( x == y); + x.discard(100); + y.discard(100); + + VERIFY (x == y); + + x.discard(2); + VERIFY (x != y); + y.discard(2); + VERIFY (x == y); +} + +int +main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc new file mode 100644 index 0000000..4bd40e7 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/operators/serialize.cc @@ -0,0 +1,46 @@ +// { dg-do run { target c++26 } } +// { dg-require-cpp-feature-test __cpp_lib_philox_engine } + +// N5014 29.5.4.5 Class Template philox_engine + +#include <sstream> +#include <random> +#include <testsuite_hooks.h> + +void +test01() +{ + std::stringstream str; + std::philox_engine<std::uint_fast32_t, + 32, 4, 10, 0xCD9E8D57, + 0x9E3779B9, 0xD2511F53, + 0xBB67AE85> x, y; + + x(); + str << x; + + VERIFY ( !(x == y) ); + str >> y; + VERIFY ( x == y ); + for (unsigned long i = 0; i < 100; ++i) + { + VERIFY (x() == y()); + } + str.clear(); + str << y; + x(); + x(); + x(); + str >> x; + VERIFY ( x == y ); + for (unsigned long i = 0; i < 1000; ++i) + { + VERIFY (x() == y()); + } +} + +int +main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc new file mode 100644 index 0000000..a3cb24e --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/constants.cc @@ -0,0 +1,48 @@ +// { dg-do compile { target c++26 } } +// { dg-require-cpp-feature-test __cpp_lib_philox_engine } + +// N5014 29.5.4.5 Class Template philox_engine + +#include <random> + +namespace test1 +{ + using P = std::philox_engine<std::uint32_t, 32, 4, 5, 9, 99, 999, 9999>; + constexpr std::same_as<std::uint32_t> auto min = P::min(); + static_assert( min == 0 ); + constexpr std::same_as<std::uint32_t> auto max = P::max(); + static_assert( max == 0xffffffff ); + constexpr std::same_as<std::size_t> auto w = P::word_size; + static_assert( w == 32 ); + constexpr std::same_as<std::size_t> auto n = P::word_count; + static_assert( n == 4 ); + constexpr std::same_as<std::size_t> auto r = P::round_count; + static_assert( r == 5 ); + constexpr std::array<std::uint32_t, 2> muls = P::multipliers; + static_assert( muls[0] == 9 && muls[1] == 999 ); + constexpr std::array<std::uint32_t, 2> consts = P::round_consts; + static_assert( consts[0] == 99 && consts[1] == 9999 ); + constexpr std::same_as<std::uint32_t> auto def = P::default_seed; + static_assert( def == 20111115u ); +} + +namespace test2 +{ + using P = std::philox_engine<std::uint64_t, 64, 2, 12, 77, 777>; + constexpr std::same_as<std::uint64_t> auto min = P::min(); + static_assert( min == 0 ); + constexpr std::same_as<std::uint64_t> auto max = P::max(); + static_assert( max == 0xffffffffffffffff ); + constexpr std::same_as<std::size_t> auto w = P::word_size; + static_assert( w == 64 ); + constexpr std::same_as<std::size_t> auto n = P::word_count; + static_assert( n == 2 ); + constexpr std::same_as<std::size_t> auto r = P::round_count; + static_assert( r == 12 ); + constexpr std::array<std::uint64_t, 1> muls = P::multipliers; + static_assert( muls[0] == 77 ); + constexpr std::array<std::uint64_t, 1> consts = P::round_consts; + static_assert( consts[0] == 777 ); + constexpr std::same_as<std::uint64_t> auto def = P::default_seed; + static_assert( def == 20111115u ); +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc new file mode 100644 index 0000000..1fc8fed --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/philox_engine/requirements/typedefs.cc @@ -0,0 +1,30 @@ +// { dg-do compile { target c++26 } } +// { dg-require-cpp-feature-test __cpp_lib_philox_engine } + +// N5014 29.5.4.5 Class Template philox_engine + +#include <random> + +void +test01() +{ + typedef std::philox_engine<std::uint_fast32_t, + 32, 4, 10, 0xCD9E8D57, + 0x9E3779B9, 0xD2511F53, + 0xBB67AE85> testType; + + typedef testType::result_type result_type; + static_assert( std::is_same_v<result_type, std::uint_fast32_t> ); +} + +void +test02() +{ + typedef std::philox_engine<std::uint_fast64_t, + 64, 4, 10, 0xCA5A826395121157, + 0x9E3779B97F4A7C15, 0xD2E7470EE14C6C93, + 0xBB67AE8584CAA73B> testType; + + typedef testType::result_type result_type; + static_assert( std::is_same_v<result_type, std::uint_fast64_t> ); +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/piecewise_linear_distribution/cons/122062.cc b/libstdc++-v3/testsuite/26_numerics/random/piecewise_linear_distribution/cons/122062.cc new file mode 100644 index 0000000..0f0caa7 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/random/piecewise_linear_distribution/cons/122062.cc @@ -0,0 +1,16 @@ +// { dg-do compile { target c++11 } } + +// PR libstdc++/122062 +// piecewise_linear_distribution(firstB, lastB, firstW) invokes comma operator + +#include <random> +#include <testsuite_iterators.h> + +void +test_pr122062() +{ + double b[1]{}; + double w[1]{}; + __gnu_test::random_access_container<double> B(b), W(w); + std::piecewise_linear_distribution<double> p(B.begin(), B.end(), W.begin()); +} diff --git a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc index 3c5aa7f..4b36f75 100644 --- a/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc +++ b/libstdc++-v3/testsuite/26_numerics/random/pr60037-neg.cc @@ -10,6 +10,6 @@ std::__detail::_Adaptor<std::mt19937, unsigned long> aurng(urng); auto x = std::generate_canonical<std::size_t, std::numeric_limits<std::size_t>::digits>(urng); -// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 270 } +// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 271 } -// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 3351 } +// { dg-error "static assertion failed: template argument must be a floating point type" "" { target *-*-* } 3480 } diff --git a/libstdc++-v3/testsuite/26_numerics/saturation/extended.cc b/libstdc++-v3/testsuite/26_numerics/saturation/extended.cc new file mode 100644 index 0000000..fbef628 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/saturation/extended.cc @@ -0,0 +1,55 @@ +// { dg-do compile { target c++26 } } + +#include <numeric> +#include <limits> + +template<typename T> +constexpr bool +test() +{ + using S = std::make_signed_t<T>; + using U = std::make_unsigned_t<T>; + + constexpr S smax = std::numeric_limits<S>::max(); + constexpr S smin = std::numeric_limits<S>::min(); + constexpr U umax = std::numeric_limits<U>::max(); + + static_assert( std::add_sat(smax, (S)1) == smax ); + static_assert( std::add_sat(smin, (S)-2) == smin ); + static_assert( std::add_sat(umax, (U)3) == umax ); + + static_assert( std::sub_sat(smax, (S)-1) == smax ); + static_assert( std::sub_sat(smin, (S)2) == smin ); + static_assert( std::sub_sat((U)0, (U)3) == (U)0 ); + + static_assert( std::mul_sat((S)(smax >> 1), (S)3) == smax ); + static_assert( std::mul_sat((S)(smin >> 1), (S)5) == smin ); + static_assert( std::mul_sat((U)(umax >> 1), (U)7) == umax ); + + static_assert( std::div_sat(smax, (S)2) == (smax >> 1) ); + static_assert( std::div_sat(smin, (S)4) == (smin >> 2) ); + static_assert( std::div_sat(smin, (S)-1) == smax ); + static_assert( std::div_sat(umax, (U)8) == (umax >> 3) ); + + return true; +} + +#ifdef __SIZEOF_INT128__ +static_assert(test<__int128>()); +#endif + +#ifdef __GLIBCXX_TYPE_INT_N_0 +static_assert(test<__GLIBCXX_TYPE_INT_N_0>()); +#endif + +#ifdef __GLIBCXX_TYPE_INT_N_1 +static_assert(test<__GLIBCXX_TYPE_INT_N_1>()); +#endif + +#ifdef __GLIBCXX_TYPE_INT_N_2 +static_assert(test<__GLIBCXX_TYPE_INT_N_2>()); +#endif + +#ifdef __GLIBCXX_TYPE_INT_N_3 +static_assert(test<__GLIBCXX_TYPE_INT_N_3>()); +#endif diff --git a/libstdc++-v3/testsuite/26_numerics/stdckdint/extended.cc b/libstdc++-v3/testsuite/26_numerics/stdckdint/extended.cc new file mode 100644 index 0000000..efc0792 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/stdckdint/extended.cc @@ -0,0 +1,65 @@ +// { dg-do run { target c++26 } } + +#include <stdckdint.h> +#include <limits> +#include <testsuite_hooks.h> + +template<typename T> +void +test() +{ + using S = std::make_signed_t<T>; + using U = std::make_unsigned_t<T>; + + constexpr S smax = std::numeric_limits<S>::max(); + constexpr S smin = std::numeric_limits<S>::min(); + constexpr U umax = std::numeric_limits<U>::max(); + S sout{}; + U uout{}; + + VERIFY( ckd_add(&sout, smax, (S)1) ); + VERIFY( ! ckd_add(&uout, smax, (U)1) && uout == ((U)smax + (U)1) ); + VERIFY( ! ckd_add(&sout, smin, (S)99) && sout == (smin + 99) ); + VERIFY( ckd_add(&uout, smin, (S)99) ); + VERIFY( ckd_add(&sout, smin, (S)-2) ); + VERIFY( ckd_add(&uout, umax, (U)3) ); + VERIFY( ! ckd_add(&sout, (U)9, (U)3) && sout == 12 ); + + VERIFY( ckd_sub(&sout, smax, (S)-1) ); + VERIFY( ! ckd_sub(&uout, smax, (S)-1) && uout == ((U)smax + (U)1) ); + VERIFY( ckd_sub(&sout, smin, (S)2) ); + VERIFY( ! ckd_sub(&sout, smin, (S)-2) && sout == (smin + 2) ); + VERIFY( ! ckd_sub(&sout, (U)0, (U)3) && sout == -3 ); + VERIFY( ckd_sub(&uout, (U)0, (U)3) ); + + VERIFY( ! ckd_mul(&sout, (S)(smax >> 2), (S)3) && sout == (smax/4*3) ); + VERIFY( ckd_mul(&sout, (S)(smax >> 1), (S)3) ); + VERIFY( ! ckd_mul(&uout, (S)(smax >> 1), (S)3) ); + VERIFY( ckd_mul(&sout, (S)(smin >> 1), (S)5) ); + VERIFY( ! ckd_mul(&uout, (U)(umax >> 2), (U)3) ); + VERIFY( ckd_mul(&sout, (U)(umax >> 2), (U)3) ); + VERIFY( ckd_mul(&uout, (U)(umax >> 1), (U)7) ); +} + +int main() +{ +#ifdef __SIZEOF_INT128__ + test<__int128>(); +#endif + +#ifdef __GLIBCXX_TYPE_INT_N_0 + test<__GLIBCXX_TYPE_INT_N_0>(); +#endif + +#ifdef __GLIBCXX_TYPE_INT_N_1 + test<__GLIBCXX_TYPE_INT_N_1>(); +#endif + +#ifdef __GLIBCXX_TYPE_INT_N_2 + test<__GLIBCXX_TYPE_INT_N_2>(); +#endif + +#ifdef __GLIBCXX_TYPE_INT_N_3 + test<__GLIBCXX_TYPE_INT_N_3>(); +#endif +} diff --git a/libstdc++-v3/testsuite/26_numerics/valarray/108951.cc b/libstdc++-v3/testsuite/26_numerics/valarray/108951.cc new file mode 100644 index 0000000..929a1d4 --- /dev/null +++ b/libstdc++-v3/testsuite/26_numerics/valarray/108951.cc @@ -0,0 +1,22 @@ +// { dg-do run { target c++11 } } +// { dg-additional-options "-faligned-new" { target c++14_down } } + +#include <valarray> +#include <cstdint> +#include <testsuite_hooks.h> + +struct alignas(64) Num +{ + Num() + { + VERIFY(reinterpret_cast<std::uintptr_t>(this) % alignof(*this) == 0); + } + + double val{}; +}; + +int main() +{ + std::valarray<Num> v(2); + v.resize(4, {}); +} diff --git a/libstdc++-v3/testsuite/27_io/basic_filebuf/seekoff/wchar_t/9875_seekoff.cc b/libstdc++-v3/testsuite/27_io/basic_filebuf/seekoff/wchar_t/9875_seekoff.cc index 052d80f..474a49f 100644 --- a/libstdc++-v3/testsuite/27_io/basic_filebuf/seekoff/wchar_t/9875_seekoff.cc +++ b/libstdc++-v3/testsuite/27_io/basic_filebuf/seekoff/wchar_t/9875_seekoff.cc @@ -66,7 +66,7 @@ protected: virtual int do_length(std::mbstate_t&, const char* from, const char* end, - std::size_t max) + std::size_t max) const { std::size_t len = (end - from) / sizeof(wchar_t); return std::min(len, max) * sizeof(wchar_t); diff --git a/libstdc++-v3/testsuite/27_io/basic_filebuf/seekpos/wchar_t/9875_seekpos.cc b/libstdc++-v3/testsuite/27_io/basic_filebuf/seekpos/wchar_t/9875_seekpos.cc index 5767f8a..f0d3b59 100644 --- a/libstdc++-v3/testsuite/27_io/basic_filebuf/seekpos/wchar_t/9875_seekpos.cc +++ b/libstdc++-v3/testsuite/27_io/basic_filebuf/seekpos/wchar_t/9875_seekpos.cc @@ -66,7 +66,7 @@ protected: virtual int do_length(std::mbstate_t&, const char* from, const char* end, - std::size_t max) + std::size_t max) const { std::size_t len = (end - from) / sizeof(wchar_t); return std::min(len, max) * sizeof(wchar_t); diff --git a/libstdc++-v3/testsuite/27_io/basic_filebuf/underflow/char/1.cc b/libstdc++-v3/testsuite/27_io/basic_filebuf/underflow/char/1.cc index a0396a4..6d4c71f 100644 --- a/libstdc++-v3/testsuite/27_io/basic_filebuf/underflow/char/1.cc +++ b/libstdc++-v3/testsuite/27_io/basic_filebuf/underflow/char/1.cc @@ -35,7 +35,7 @@ protected: virtual result do_in(state_type&, const char* from, const char*, const char*& from_next, - char* to, char*, char*& to_next) + char* to, char*, char*& to_next) const { from_next = from; to_next = to; diff --git a/libstdc++-v3/testsuite/27_io/basic_filebuf/underflow/wchar_t/11603.cc b/libstdc++-v3/testsuite/27_io/basic_filebuf/underflow/wchar_t/11603.cc index fcd95a9..c62ad02 100644 --- a/libstdc++-v3/testsuite/27_io/basic_filebuf/underflow/wchar_t/11603.cc +++ b/libstdc++-v3/testsuite/27_io/basic_filebuf/underflow/wchar_t/11603.cc @@ -119,7 +119,7 @@ protected: { return width; } virtual int - do_length(const StateT&, const extern_type* from, + do_length(StateT&, const extern_type* from, const extern_type* end, size_t max) const { size_t len = std::min(max, diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/4.cc b/libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/4.cc new file mode 100644 index 0000000..6f0b643 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/4.cc @@ -0,0 +1,22 @@ +// { dg-do run { target c++26 } } + +#include <istream> +#include <sstream> +#include <string> +#include <testsuite_hooks.h> + +void test01() { + std::istringstream in("\xF0\x9F\xA4\xA1 Clown Face"); + in.ignore(100, '\xA1'); + VERIFY(in.gcount() == 4); + VERIFY(in.peek() == ' '); + + std::string str; + in >> str; + VERIFY(str == "Clown"); +} + +int main() { + test01(); + return 0; +} diff --git a/libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/93672.cc b/libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/93672.cc index 9673748..608b794 100644 --- a/libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/93672.cc +++ b/libstdc++-v3/testsuite/27_io/basic_istream/ignore/char/93672.cc @@ -20,9 +20,9 @@ test_pr93672() // std::basic_istream::ignore hangs if delim MSB is set VERIFY( in.gcount() == 3 ); VERIFY( ! in.eof() ); - // This only works if char is unsigned. + // Prior to C++26 (P3223R2), this only works if char is unsigned. in.ignore(100, '\xfe'); - if (std::numeric_limits<char>::is_signed) + if (std::numeric_limits<char>::is_signed && __cplusplus <= 202302L) { // When char is signed, '\xfe' != traits_type::to_int_type('\xfe') // so the delimiter does not match the character in the input sequence, @@ -69,9 +69,9 @@ test_primary_template() VERIFY( in.gcount() == 3 ); VERIFY( ! in.eof() ); - // This only works if char is unsigned. + // Prior to C++26 (P3223R2), this only works if char is unsigned. in.ignore(100, '\xfe'); - if (std::numeric_limits<char>::is_signed) + if (std::numeric_limits<char>::is_signed && __cplusplus <= 202302L) { // When char is signed, '\xfe' != traits_type::to_int_type('\xfe') // so the delimiter does not match the character in the input sequence, diff --git a/libstdc++-v3/testsuite/27_io/basic_istringstream/cons/char/string_view.cc b/libstdc++-v3/testsuite/27_io/basic_istringstream/cons/char/string_view.cc new file mode 100644 index 0000000..f25b538 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/basic_istringstream/cons/char/string_view.cc @@ -0,0 +1,201 @@ +// C++26 [istringstream.general] + +// { dg-do run { target c++26 } } + +#include <sstream> +#include <string> +#include <string_view> +#include <testsuite_allocator.h> +#include <testsuite_hooks.h> + +// Check C++26 P2495 istringstream ctors and members str(s) that accept a +// string_view, or anything convertible to a string_view, in place of a +// string object. Mostly just verify plumbing. + +#ifndef C +# define C char +# define L(a) a +#endif + +using string = std::basic_string<C>; +using string_view = std::basic_string_view<C>; +using istringstream = std::basic_istringstream<C>; + +struct convertible_to_string_view { + string s; + operator string_view() const { return s; } +}; + +const string str(L("This is a test string")); +convertible_to_string_view cstr{str}; // a copy +const convertible_to_string_view ccstr{str}; // another copy + +template <typename istringstream = std::basic_istringstream<C>> +void +test01() +{ + // Test C++26 constructor and str(s) taking a generalized string_view + + static_assert(! requires { istringstream(1); }, + "istringstream ctor should reject what cannot be converted to a string_view"); + static_assert(! requires { istringstream().str(1); }, + "istringstream::str(s) should reject what cannot be converted to a string_view"); + + static_assert(!std::is_convertible_v<string_view, istringstream>, + "istringstream(string_view, ios::openmode) is explicit"); + static_assert(!std::is_convertible_v<const string_view, istringstream>, + "istringstream(string_view, ios::openmode) is explicit"); + static_assert(!std::is_convertible_v<convertible_to_string_view, istringstream>, + "istringstream(convertible_to_string_view, ios::openmode) is explicit"); + static_assert(!std::is_convertible_v<const convertible_to_string_view, istringstream>, + "istringstream(convertible_to_string_view, ios::openmode) is explicit"); + + { + istringstream istr(cstr); + VERIFY( istr.str() == cstr.s ); + VERIFY( istr.get() == cstr.s[0] ); + } + { + istringstream istr(ccstr); + VERIFY( istr.str() == ccstr.s ); + VERIFY( istr.get() == ccstr.s[0] ); + } + { + istringstream istr(cstr, std::ios_base::in); + VERIFY( istr.str() == cstr.s ); + VERIFY( istr.get() == cstr.s[0] ); + VERIFY( istr.rdbuf()->sputc('X') != 'X' ); + } + { + istringstream istr(cstr, std::ios_base::out); + VERIFY( istr.str() == cstr.s ); + VERIFY( istr.get() == cstr.s[0] ); + VERIFY( istr.rdbuf()->sputc('X') == 'X' ); + } +} + +void +test02() +{ + // Test various C++26 constructors taking string views + // and mix of other arguments + + auto const mode = std::ios_base::in | std::ios_base::out; + + { + // template <typename T> + // basic_istringstream(const T&, ios_base::openmode, const allocator_type&) + + istringstream::allocator_type a; + { + istringstream istr(cstr, mode, a); // ={} checks for non-explicit ctor + VERIFY( istr.str() == cstr.s ); + } + { + istringstream istr(cstr, std::ios::in, a); + VERIFY( istr.str() == cstr.s ); + VERIFY( istr.get() == cstr.s[0] ); + VERIFY( istr.rdbuf()->sputc('X') != 'X' ); + } + { + istringstream istr(cstr, std::ios::out, a); + VERIFY( istr.str() == cstr.s ); + VERIFY( istr.get() == cstr.s[0] ); + VERIFY( istr.rdbuf()->sputc('X') == 'X' ); + } + } + + { + // template <typename T> + // basic_istringstream(const T&, ios_base::openmode) + { + istringstream istr(cstr, mode); + VERIFY( istr.str() == cstr.s ); + VERIFY( istr.get() == cstr.s[0] ); + VERIFY( istr.rdbuf()->sputc('X') == 'X' ); + } + { + istringstream istr(cstr, std::ios::in); + VERIFY( istr.str() == cstr.s ); + VERIFY( istr.get() == cstr.s[0] ); + VERIFY( istr.rdbuf()->sputc('X') != 'X' ); + } + { + istringstream istr(cstr, std::ios::out); + VERIFY( istr.str() == cstr.s ); + VERIFY( istr.get() == cstr.s[0] ); + VERIFY( istr.rdbuf()->sputc('X') == 'X' ); + } + } + + { + // template <typename T> + // explicit + // basic_istringstream(const T&, ios_base::openmode = ios_base::in) + + istringstream istr(cstr); + VERIFY( istr.str() == cstr.s ); + VERIFY( istr.get() == cstr.s[0] ); + VERIFY( istr.rdbuf()->sputc('X') != 'X' ); + } +} + +using alloc_type = __gnu_test::uneq_allocator<C>; + +template<typename Alloc, typename CC = typename Alloc::value_type> + using istringstream_with_alloc + = std::basic_istringstream<CC, std::char_traits<CC>, Alloc>; + +void test03() +{ + alloc_type a{1}; + { + istringstream_with_alloc<alloc_type> istr(cstr, a); +#if _GLIBCXX_USE_CXX11_ABI + VERIFY( istr.rdbuf()->get_allocator() == a ); +#endif + VERIFY( string_view{istr.str()} == cstr ); + VERIFY( istr.get() == cstr.s[0] ); + } + { + istringstream_with_alloc<alloc_type> istr(cstr, std::ios::in, a); +#if _GLIBCXX_USE_CXX11_ABI + VERIFY( istr.rdbuf()->get_allocator() == a ); +#endif + VERIFY( string_view{istr.str()} == cstr ); + VERIFY( istr.get() == cstr.s[0] ); + VERIFY( istr.rdbuf()->sputc('X') != 'X' ); + } + { + istringstream_with_alloc<alloc_type> istr(cstr, std::ios::out, a); +#if _GLIBCXX_USE_CXX11_ABI + VERIFY( istr.rdbuf()->get_allocator() == a ); +#endif + VERIFY( string_view{istr.str()} == cstr ); + VERIFY( istr.get() == cstr.s[0] ); + VERIFY( istr.rdbuf()->sputc('X') == 'X' ); + } +} + +void test04() +{ + { + istringstream istr; + istr.str( cstr ); + VERIFY( istr.str() == cstr.s ); + } + { + istringstream istr; + istr.str( ccstr ); + VERIFY( istr.str() == ccstr.s ); + } +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); +} diff --git a/libstdc++-v3/testsuite/27_io/basic_istringstream/cons/wchar_t/string_view.cc b/libstdc++-v3/testsuite/27_io/basic_istringstream/cons/wchar_t/string_view.cc new file mode 100644 index 0000000..62fb03c --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/basic_istringstream/cons/wchar_t/string_view.cc @@ -0,0 +1,6 @@ +// C++26 [istringstream.general] +// { dg-do run { target c++26 } } + +#define C wchar_t +#define L(a) L##a +#include "../char/string_view.cc" diff --git a/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_arithmetic/char/hexfloat.cc b/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_arithmetic/char/hexfloat.cc index b1bc7fb..f694730 100644 --- a/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_arithmetic/char/hexfloat.cc +++ b/libstdc++-v3/testsuite/27_io/basic_ostream/inserters_arithmetic/char/hexfloat.cc @@ -95,6 +95,11 @@ test01() void test02() { + /* If these conditions are not met, basic_string.h doesn't define + std::stold(const string&, size_t* = 0), and then the test would + fail to compile. */ +#if (_GLIBCXX_HAVE_STRTOLD && ! _GLIBCXX_HAVE_BROKEN_STRTOLD) \ + || __DBL_MANT_DIG__ == __LDBL_MANT_DIG__ ostringstream os; long double d = 272.L; // 0x1.1p+8L; os << hexfloat << setprecision(1); @@ -140,6 +145,7 @@ test02() cout << "got: " << os.str() << endl; #endif VERIFY( os && os.str() == "15" ); +#endif } int diff --git a/libstdc++-v3/testsuite/27_io/basic_ostringstream/cons/char/string_view.cc b/libstdc++-v3/testsuite/27_io/basic_ostringstream/cons/char/string_view.cc new file mode 100644 index 0000000..6279c19 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/basic_ostringstream/cons/char/string_view.cc @@ -0,0 +1,200 @@ +// C++26 [ostringstream.general] + +// { dg-do run { target c++26 } } + +#include <sstream> +#include <string> +#include <string_view> +#include <testsuite_allocator.h> +#include <testsuite_hooks.h> + +// Check C++26 P2495 ostringstream ctors and members str(s) that accept a +// string_view, or anything convertible to a string_view, in place of a +// string object. Mostly just verify plumbing. + +#ifndef C +# define C char +# define L(a) a +#endif + +using string = std::basic_string<C>; +using string_view = std::basic_string_view<C>; +using ostringstream = std::basic_ostringstream<C>; + +struct convertible_to_string_view { + string s; + operator string_view() const { return s; } +}; + +const string str(L("This is a test string")); +convertible_to_string_view cstr{str}; // a copy +const convertible_to_string_view ccstr{str}; // another copy + +template <typename ostringstream = std::basic_ostringstream<C>> +void +test01() +{ + // Test C++26 constructor and str(s) taking a generalized string_view + + static_assert(! requires { ostringstream(1); }, + "ostringstream ctor should reject what cannot be converted to a string_view"); + static_assert(! requires { ostringstream().str(1); }, + "ostringstream::str(s) should reject what cannot be converted to a string_view"); + + static_assert(!std::is_convertible_v<string_view, ostringstream>, + "ostringstream(string_view, ios::openmode) is explicit"); + static_assert(!std::is_convertible_v<const string_view, ostringstream>, + "ostringstream(string_view, ios::openmode) is explicit"); + static_assert(!std::is_convertible_v<convertible_to_string_view, ostringstream>, + "ostringstream(convertible_to_string_view, ios::openmode) is explicit"); + static_assert(!std::is_convertible_v<const convertible_to_string_view, ostringstream>, + "ostringstream(convertible_to_string_view, ios::openmode) is explicit"); + + { + ostringstream ostrstr(cstr); + VERIFY( ostrstr.str() == cstr.s ); + VERIFY( ostrstr.rdbuf()->sgetc() == ostringstream::traits_type::eof() ); + } + { + ostringstream ostrstr(ccstr); + VERIFY( ostrstr.str() == ccstr.s ); + VERIFY( ostrstr.rdbuf()->sgetc() == ostringstream::traits_type::eof() ); + } + { + ostringstream ostrstr(cstr, std::ios_base::in); + VERIFY( ostrstr.str() == cstr.s ); + VERIFY( ostrstr.rdbuf()->sgetc() == cstr.s[0]); + VERIFY( ostrstr.put('Y').rdstate() == ostrstr.goodbit ); + } + { + ostringstream ostrstr(cstr, std::ios_base::out); + VERIFY( ostrstr.str() == cstr.s ); + VERIFY( ostrstr.rdbuf()->sgetc() == ostringstream::traits_type::eof() ); + VERIFY( ostrstr.put('Y').rdstate() == ostrstr.goodbit ); + } +} + +void +test02() +{ + // Test plumbing of C++26 various constructors taking string views + + auto const mode = std::ios_base::in | std::ios_base::out; + + { + ostringstream::allocator_type a; + // template <typename T> + // basic_ostringstream(const T&, ios_base::openmode, const allocator_type&) + { + ostringstream ostrstr(cstr, mode, a); // ={} checks for non-explicit ctor + VERIFY( ostrstr.str() == cstr.s ); + } + { + ostringstream ostrstr(cstr, std::ios::in, a); + VERIFY( ostrstr.str() == cstr.s ); + VERIFY( ostrstr.rdbuf()->sgetc() == cstr.s[0]); + VERIFY( ostrstr.put('Y').rdstate() == ostrstr.goodbit ); + } + { + ostringstream ostrstr(cstr, std::ios::out, a); + VERIFY( ostrstr.str() == cstr.s ); + VERIFY( ostrstr.rdbuf()->sgetc() == ostringstream::traits_type::eof() ); + VERIFY( ostrstr.put('Y').rdstate() == ostrstr.goodbit ); + } + } + + { + // template <typename T> + // basic_ostringstream(const T&, ios_base::openmode) + { + ostringstream ostrstr(cstr, mode); + VERIFY( ostrstr.str() == cstr.s ); + VERIFY( ostrstr.rdbuf()->sgetc() == cstr.s[0]); + VERIFY( ostrstr.put('Y').good() ); + } + { + ostringstream ostrstr(cstr, std::ios::in); + VERIFY( ostrstr.str() == cstr.s ); + VERIFY( ostrstr.rdbuf()->sgetc() == cstr.s[0]); + VERIFY( ostrstr.put('X').good() ); + } + { + ostringstream ostrstr(cstr, std::ios::out); + VERIFY( ostrstr.str() == cstr.s ); + VERIFY( ostrstr.rdbuf()->sgetc() == ostringstream::traits_type::eof() ); + VERIFY( ostrstr.put('Y').rdstate() == ostrstr.goodbit ); + } + } + + { + // template <typename T> + // explicit + // basic_ostringstream(const T&, ios_base::openmode = ios_base::out) + + ostringstream ostrstr(cstr); + VERIFY( ostrstr.str() == cstr.s ); + VERIFY( ostrstr.rdbuf()->sgetc() == ostringstream::traits_type::eof() ); + VERIFY( ostrstr.put('Y').good() ); + } +} + +using alloc_type = __gnu_test::uneq_allocator<C>; + +template<typename Alloc, typename CC = typename Alloc::value_type> + using ostringstream_with_alloc + = std::basic_ostringstream<CC, std::char_traits<CC>, Alloc>; + +void test03() +{ + alloc_type a{1}; + { + ostringstream_with_alloc<alloc_type> ostrstr(cstr, a); +#if _GLIBCXX_USE_CXX11_ABI + VERIFY( ostrstr.rdbuf()->get_allocator() == a ); +#endif + VERIFY( string_view{ostrstr.str()} == cstr ); + VERIFY( ostrstr.rdbuf()->sgetc() == ostringstream::traits_type::eof() ); + VERIFY( ostrstr.put('X').good() ); + } + { + ostringstream_with_alloc<alloc_type> ostrstr(cstr, std::ios::in, a); +#if _GLIBCXX_USE_CXX11_ABI + VERIFY( ostrstr.rdbuf()->get_allocator() == a ); +#endif + VERIFY( string_view{ostrstr.str()} == cstr ); + VERIFY( ostrstr.rdbuf()->sgetc() == cstr.s[0]); + VERIFY( ostrstr.put('X').good() ); + } + { + ostringstream_with_alloc<alloc_type> ostrstr(cstr, std::ios::out, a); +#if _GLIBCXX_USE_CXX11_ABI + VERIFY( ostrstr.rdbuf()->get_allocator() == a ); +#endif + VERIFY( string_view{ostrstr.str()} == cstr ); + VERIFY( ostrstr.rdbuf()->sgetc() == ostringstream::traits_type::eof() ); + VERIFY( ostrstr.put('Y').rdstate() == ostrstr.goodbit ); + } +} + +void test04() +{ + { + ostringstream ostrstr; + ostrstr.str(cstr); + VERIFY( ostrstr.str() == cstr.s ); + } + { + ostringstream ostrstr; + ostrstr.str(ccstr); + VERIFY( ostrstr.str() == ccstr.s ); + } +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); +} diff --git a/libstdc++-v3/testsuite/27_io/basic_ostringstream/cons/wchar_t/string_view.cc b/libstdc++-v3/testsuite/27_io/basic_ostringstream/cons/wchar_t/string_view.cc new file mode 100644 index 0000000..ee6ac8d --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/basic_ostringstream/cons/wchar_t/string_view.cc @@ -0,0 +1,6 @@ +// C++26 [ostringstream.general] +// { dg-do run { target c++26 } } + +#define C wchar_t +#define L(a) L##a +#include "../char/string_view.cc" diff --git a/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/char/string_view.cc b/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/char/string_view.cc new file mode 100644 index 0000000..14278b3 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/char/string_view.cc @@ -0,0 +1,211 @@ +// C++26 31.8.2.1 [stringbuf.general] + +// { dg-do run { target c++26 } } + +#include <sstream> +#include <string> +#include <string_view> +#include <testsuite_allocator.h> +#include <testsuite_hooks.h> + +// Check C++26 P2495 stringbuf ctors and members str(s) that accept a +// string_view, or anything convertible to a string_view, in place of a +// string object. + +#ifndef C +# define C char +# define L(a) a +#endif + +using string = std::basic_string<C>; +using string_view = std::basic_string_view<C>; +using stringbuf = std::basic_stringbuf<C>; + +struct convertible_to_string_view { + string s; + operator string_view() const { return s; } +}; + +const string str(L("This is a test string")); +convertible_to_string_view cstr{str}; // a copy +const convertible_to_string_view ccstr{str}; // another copy + +template <typename stringbuf = std::basic_stringbuf<C>> +void +test01() +{ + // Test C++26 constructor and str(s) taking a generalized string_view + + static_assert(! requires { stringbuf(1); }, + "stringbuf ctor should reject what cannot be converted to a string_view"); + static_assert(! requires { stringbuf().str(1); }, + "stringbuf::str(s) should reject what cannot be converted to a string_view"); + + static_assert(!std::is_convertible_v<string_view, stringbuf>, + "stringbuf(string_view, ios::openmode) is explicit"); + static_assert(!std::is_convertible_v<const string_view, stringbuf>, + "stringbuf(string_view, ios::openmode) is explicit"); + static_assert(!std::is_convertible_v<convertible_to_string_view, stringbuf>, + "stringbuf(convertible_to_string_view, ios::openmode) is explicit"); + static_assert( + !std::is_convertible_v<const convertible_to_string_view, stringbuf>, + "stringbuf(convertible_to_string_view, ios::openmode) is explicit"); + + { + stringbuf sbuf(cstr); + VERIFY( sbuf.str() == cstr.s ); + VERIFY( sbuf.sgetc() == cstr.s[0] ); + } + { + stringbuf sbuf(ccstr); + VERIFY( sbuf.str() == ccstr.s ); + VERIFY( sbuf.sgetc() == ccstr.s[0] ); + } + { + stringbuf sbuf(cstr, std::ios_base::in); + VERIFY( sbuf.str() == cstr.s ); + VERIFY( sbuf.sgetc() == cstr.s[0] ); + VERIFY( sbuf.sputc('X') == stringbuf::traits_type::eof() ); + } + { + stringbuf sbuf(ccstr, std::ios_base::in); + VERIFY( sbuf.str() == ccstr.s ); + VERIFY( sbuf.sgetc() == ccstr.s[0] ); + VERIFY( sbuf.sputc('X') == stringbuf::traits_type::eof() ); + } + { + stringbuf sbuf(cstr, std::ios_base::out); + VERIFY( sbuf.str() == cstr.s ); + VERIFY( sbuf.sputc('Y') == 'Y' ); + VERIFY( sbuf.sgetc() == stringbuf::traits_type::eof() ); + } + { + stringbuf sbuf(ccstr, std::ios_base::out); + VERIFY( sbuf.str() == ccstr.s ); + VERIFY( sbuf.sputc('Y') == 'Y' ); + VERIFY( sbuf.sgetc() == stringbuf::traits_type::eof() ); + } +} + +void +test02() +{ + // Test C++26 constructors taking string views using different allocators + + auto const mode = std::ios_base::in | std::ios_base::out; + + { + // template <typename T> + // basic_stringbuf(const T&, ios_base::openmode, const allocator_type&) + + stringbuf::allocator_type a; + { + stringbuf sbuf(cstr, mode, a); // ={} checks for non-explicit ctor + VERIFY( sbuf.str() == cstr.s ); + } + { + stringbuf sbuf(cstr, std::ios::in, a); + VERIFY( sbuf.str() == cstr.s ); + VERIFY( sbuf.sgetc() == cstr.s[0] ); + VERIFY( sbuf.sputc('X') == stringbuf::traits_type::eof() ); + } + + { + stringbuf sbuf(cstr, std::ios::out, a); + VERIFY( sbuf.str() == cstr.s ); + VERIFY( sbuf.sputc('X') == 'X' ); + VERIFY( sbuf.sgetc() == stringbuf::traits_type::eof() ); + } + } + + { + // template <typename T> + // basic_stringbuf(const T&, ios_base::openmode) + { + stringbuf sbuf(cstr, mode); + VERIFY( sbuf.str() == cstr.s ); + } + { + stringbuf sbuf(cstr, std::ios::in); + VERIFY( sbuf.str() == cstr.s ); + VERIFY( sbuf.sgetc() == cstr.s[0] ); + VERIFY( sbuf.sputc('X') == stringbuf::traits_type::eof() ); + } + { + stringbuf sbuf(cstr, std::ios::out); + VERIFY( sbuf.str() == cstr.s ); + VERIFY( sbuf.sputc('X') == 'X' ); + VERIFY( sbuf.sgetc() == stringbuf::traits_type::eof() ); + } + } + + { + // template <typename T> + // explicit + // basic_stringbuf(const T&, ios_base::openmode = ios_base::in|ios_base::out) + + stringbuf sbuf(cstr); + VERIFY( sbuf.str() == cstr.s ); + VERIFY( sbuf.sgetc() == cstr.s[0] ); + } +} + +using alloc_type = __gnu_test::uneq_allocator<C>; + +template<typename Alloc, typename CC = typename Alloc::value_type> + using stringbuf_with_alloc + = std::basic_stringbuf<CC, std::char_traits<CC>, Alloc>; + +void test03() +{ + alloc_type a{1}; + { + stringbuf_with_alloc<alloc_type> sbuf(cstr, a); +#if _GLIBCXX_USE_CXX11_ABI + VERIFY( sbuf.get_allocator() == a ); +#endif + VERIFY( string_view{sbuf.str()} == cstr ); + VERIFY( sbuf.sgetc() == cstr.s[0] ); + } + { + stringbuf_with_alloc<alloc_type> sbuf(cstr, std::ios::in, a); +#if _GLIBCXX_USE_CXX11_ABI + VERIFY( sbuf.get_allocator() == a ); +#endif + VERIFY( string_view{sbuf.str()} == cstr ); + VERIFY( sbuf.sgetc() == cstr.s[0] ); + VERIFY( sbuf.sputc('X') == stringbuf::traits_type::eof() ); + } + { + stringbuf_with_alloc<alloc_type> sbuf(cstr, std::ios::out, a); +#if _GLIBCXX_USE_CXX11_ABI + VERIFY( sbuf.get_allocator() == a ); +#endif + VERIFY( string_view{sbuf.str()} == cstr ); + VERIFY( sbuf.sputc('X') == 'X' ); + VERIFY( sbuf.sgetc() == stringbuf::traits_type::eof() ); + } +} + +void test04() +{ + { + stringbuf sbuf; + sbuf.str(cstr); + VERIFY( sbuf.str() == cstr.s ); + } + { + stringbuf sbuf; + sbuf.str(ccstr); + VERIFY( sbuf.str() == ccstr.s ); + } +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); +} diff --git a/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/wchar_t/string_view.cc b/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/wchar_t/string_view.cc new file mode 100644 index 0000000..c428491 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/basic_stringbuf/cons/wchar_t/string_view.cc @@ -0,0 +1,6 @@ +// C++26 [stringbuf.general] +// { dg-do run { target c++26 } } + +#define C wchar_t +#define L(a) L##a +#include "../char/string_view.cc" diff --git a/libstdc++-v3/testsuite/27_io/basic_stringstream/cons/char/string_view.cc b/libstdc++-v3/testsuite/27_io/basic_stringstream/cons/char/string_view.cc new file mode 100644 index 0000000..1c9ecea --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/basic_stringstream/cons/char/string_view.cc @@ -0,0 +1,210 @@ +// C++26 31.8.2.1 [stringstream.general] + +// { dg-do run { target c++26 } } + +#include <sstream> +#include <string> +#include <string_view> +#include <testsuite_allocator.h> +#include <testsuite_hooks.h> + +// Check C++26 P2495 stringstream ctors and members str(s) that accept a +// string_view, or anything convertible to a string_view, in place of a +// string object. Mostly just verify plumbing. + +#ifndef C +# define C char +# define L(a) a +#endif + +using string = std::basic_string<C>; +using string_view = std::basic_string_view<C>; +using stringstream = std::basic_stringstream<C>; + +struct convertible_to_string_view { + string s; + operator string_view() const { return s; } +}; + +const string str(L("This is a test string")); +convertible_to_string_view cstr{str}; // a copy +const convertible_to_string_view ccstr{str}; // another copy + +template <typename stringstream = std::basic_stringstream<C>> +void +test01() +{ + // Test C++26 constructor and str(s) taking a generalized string_view + + static_assert(! requires { stringstream(1); }, + "stringstream ctor should reject what cannot be converted to a string_view"); + static_assert(! requires { stringstream().str(1); }, + "stringstream::str(s) should reject what cannot be converted to a string_view"); + + static_assert(!std::is_convertible_v<string_view, stringstream>, + "stringstream(string_view, ios::openmode) is explicit"); + static_assert(!std::is_convertible_v<const string_view, stringstream>, + "stringstream(string_view, ios::openmode) is explicit"); + static_assert(!std::is_convertible_v<convertible_to_string_view, stringstream>, + "stringstream(convertible_to_string_view, ios::openmode) is explicit"); + static_assert(!std::is_convertible_v<const convertible_to_string_view, stringstream>, + "stringstream(convertible_to_string_view, ios::openmode) is explicit"); + + { + stringstream strstr(cstr); + VERIFY( strstr.str() == cstr.s ); + VERIFY( strstr.get() == cstr.s[0] ); + } + { + stringstream strstr(ccstr); + VERIFY( strstr.str() == ccstr.s ); + VERIFY( strstr.get() == ccstr.s[0] ); + } + { + stringstream strstr(cstr, std::ios_base::in); + VERIFY( strstr.str() == cstr.s ); + VERIFY( strstr.get() == cstr.s[0] ); + VERIFY( strstr.put('X').rdstate() == strstr.badbit ); + } + { + stringstream strstr(cstr, std::ios_base::out); + VERIFY( strstr.str() == cstr.s ); + VERIFY( strstr.put('Y').good() ); + VERIFY( strstr.get() == stringstream::traits_type::eof()); + } +} + +void +test02() +{ + // Test C++26 various constructors taking string views + + auto const mode = std::ios_base::in | std::ios_base::out; + + { + // template <typename T> + // basic_stringstream(const T&, ios_base::openmode, const allocator_type&) + + stringstream::allocator_type a; + { + stringstream strstr(cstr, mode, a); // ={} checks for non-explicit ctor + VERIFY( strstr.str() == cstr.s ); + } + { + stringstream strstr(cstr, std::ios::in, a); + VERIFY( strstr.str() == cstr.s ); + VERIFY( strstr.get() == cstr.s[0] ); + VERIFY( strstr.put('X').rdstate() == strstr.badbit ); + } + { + stringstream strstr(cstr, std::ios::out, a); + VERIFY( strstr.str() == cstr.s ); + VERIFY( strstr.put('X').good() ); + VERIFY( strstr.get() == stringstream::traits_type::eof()); + } + } + + { + // template <typename T> + // basic_stringstream(const T&, ios_base::openmode) + + { + stringstream strstr(cstr, mode); + VERIFY( strstr.str() == cstr.s ); + VERIFY( strstr.get() == cstr.s[0] ); + VERIFY( strstr.put('X').good() ); + } + { + stringstream strstr(cstr, std::ios::in); + VERIFY( strstr.str() == cstr.s ); + VERIFY( strstr.get() == cstr.s[0] ); + VERIFY( strstr.put('X').rdstate() == strstr.badbit ); + } + { + stringstream strstr(cstr, std::ios::out); + VERIFY( strstr.str() == cstr.s ); + VERIFY( strstr.put('X').good() ); + VERIFY( strstr.get() == stringstream::traits_type::eof()); + } + } + + { + // template <typename T> + // explicit + // basic_stringstream(const T&, ios_base::openmode = ios_base::in|ios_base::out) + + stringstream strstr(cstr); + VERIFY( strstr.str() == cstr.s ); + VERIFY( strstr.get() == cstr.s[0] ); + VERIFY( strstr.put('X').good() ); + } +} + +// A minimal allocator with no default constructor +template<typename T> + struct NoDefaultCons : __gnu_test::SimpleAllocator<T> + { + using __gnu_test::SimpleAllocator<T>::SimpleAllocator; + NoDefaultCons() = delete; + NoDefaultCons(int) { } + }; + +using alloc_type = __gnu_test::uneq_allocator<C>; + +template<typename Alloc, typename CC = typename Alloc::value_type> + using stringstream_with_alloc + = std::basic_stringstream<CC, std::char_traits<CC>, Alloc>; + +void test03() +{ + alloc_type a{1}; + { + stringstream_with_alloc<alloc_type> strstr(cstr, a); +#if _GLIBCXX_USE_CXX11_ABI + VERIFY( strstr.rdbuf()->get_allocator() == a ); +#endif + VERIFY( string_view{strstr.str()} == cstr ); + VERIFY( strstr.get() == cstr.s[0] ); + } + { + stringstream_with_alloc<alloc_type> strstr(cstr, std::ios::in, a); +#if _GLIBCXX_USE_CXX11_ABI + VERIFY( strstr.rdbuf()->get_allocator() == a ); +#endif + VERIFY( string_view{strstr.str()} == cstr ); + VERIFY( strstr.get() == cstr.s[0] ); + VERIFY( strstr.put('X').rdstate() == strstr.badbit ); + } + { + stringstream_with_alloc<alloc_type> strstr(cstr, std::ios::out, a); +#if _GLIBCXX_USE_CXX11_ABI + VERIFY( strstr.rdbuf()->get_allocator() == a ); +#endif + VERIFY( string_view{strstr.str()} == cstr ); + VERIFY( strstr.put('X').good() ); + VERIFY( strstr.get() == stringstream::traits_type::eof()); + } +} + +void test04() +{ + { + stringstream strstr; + strstr.str( cstr ); + VERIFY( strstr.str() == cstr.s ); + } + { + stringstream strstr; + strstr.str( ccstr ); + VERIFY( strstr.str() == ccstr.s ); + } +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); +} diff --git a/libstdc++-v3/testsuite/27_io/basic_stringstream/cons/wchar_t/string_view.cc b/libstdc++-v3/testsuite/27_io/basic_stringstream/cons/wchar_t/string_view.cc new file mode 100644 index 0000000..921c0fe --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/basic_stringstream/cons/wchar_t/string_view.cc @@ -0,0 +1,6 @@ +// C++26 [stringstream.general] +// { dg-do run { target c++26 } } + +#define C wchar_t +#define L(a) L##a +#include "../char/string_view.cc" diff --git a/libstdc++-v3/testsuite/27_io/filesystem/operations/rename.cc b/libstdc++-v3/testsuite/27_io/filesystem/operations/rename.cc index 00e088a..fef2547 100644 --- a/libstdc++-v3/testsuite/27_io/filesystem/operations/rename.cc +++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/rename.cc @@ -170,10 +170,20 @@ test_directories() fs::remove_all(dir, ec); } +void +test_pr122726() +{ + std::error_code ec; + const auto nonesuch = __gnu_test::nonexistent_path(); + fs::rename(nonesuch, "new-name", ec); + VERIFY( ec == std::make_error_code(std::errc::no_such_file_or_directory) ); +} + int main() { test01(); test_symlinks(); test_directories(); + test_pr122726(); } diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/concat/120029.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/concat/120029.cc new file mode 100644 index 0000000..5153d59 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/filesystem/path/concat/120029.cc @@ -0,0 +1,72 @@ +// { dg-do run { target c++17 } } + +// Bug libstdc++/120029 +// Dangling iterator usage in path::operator+=(const path& p) when this == p + +#include <filesystem> +#include <testsuite_hooks.h> + +namespace fs = std::filesystem; + +void +test_root_dir() +{ + fs::path p = "/"; + p += p; + p += p; + VERIFY( p == "////" ); + p += p.filename(); + VERIFY( p == "////" ); + p += *std::prev(p.end()); + VERIFY( p == "////" ); +} + +void +test_root_name() +{ + fs::path p = "C:/"; + p += p; + p += p; + VERIFY( p == "C:/C:/C:/C:/" ); + p += p.filename(); + VERIFY( p == "C:/C:/C:/C:/" ); + p += *std::prev(p.end()); + VERIFY( p == "C:/C:/C:/C:/" ); +} + +void +test_filename() +{ + fs::path p = "file"; + p += p; + p += p; + VERIFY( p == "filefilefilefile" ); + p += p.filename(); + VERIFY( p == "filefilefilefilefilefilefilefile" ); + p += *std::prev(p.end()); + VERIFY( p == "filefilefilefilefilefilefilefilefilefilefilefilefilefilefilefile" ); +} + +void +test_multi() +{ + fs::path p = "/home/username/Documents/mu"; + p += p; + p += p; + VERIFY( p == "/home/username/Documents/mu/home/username/Documents/mu/home/username/Documents/mu/home/username/Documents/mu" ); + p += p.filename(); + VERIFY( p == "/home/username/Documents/mu/home/username/Documents/mu/home/username/Documents/mu/home/username/Documents/mumu" ); + p += *std::prev(p.end()); + VERIFY( p == "/home/username/Documents/mu/home/username/Documents/mu/home/username/Documents/mu/home/username/Documents/mumumumu" ); + auto n = std::distance(p.begin(), p.end()); + for (int i = 0; i < n; ++i) + p += *std::next(p.begin(), i); +} + +int main() +{ + test_root_dir(); + test_root_name(); + test_filename(); + test_multi(); +} diff --git a/libstdc++-v3/testsuite/27_io/ios_base/types/fmtflags/case_label.cc b/libstdc++-v3/testsuite/27_io/ios_base/types/fmtflags/case_label.cc index c72e00f..6f0390b 100644 --- a/libstdc++-v3/testsuite/27_io/ios_base/types/fmtflags/case_label.cc +++ b/libstdc++-v3/testsuite/27_io/ios_base/types/fmtflags/case_label.cc @@ -68,12 +68,6 @@ case_labels(bitmask_type b) break; case std::ios_base::floatfield: break; - case std::_S_ios_fmtflags_end: - break; - case __INT_MAX__: - break; - case ~__INT_MAX__: - break; } using underlying_type = std::underlying_type<bitmask_type>::type; static_assert( sizeof(underlying_type) == sizeof(int), diff --git a/libstdc++-v3/testsuite/27_io/ios_base/types/iostate/case_label.cc b/libstdc++-v3/testsuite/27_io/ios_base/types/iostate/case_label.cc index 6a1dd90..410f746 100644 --- a/libstdc++-v3/testsuite/27_io/ios_base/types/iostate/case_label.cc +++ b/libstdc++-v3/testsuite/27_io/ios_base/types/iostate/case_label.cc @@ -40,12 +40,6 @@ case_labels(bitmask_type b) break; case std::ios_base::failbit: break; - case std::_S_ios_iostate_end: - break; - case __INT_MAX__: - break; - case ~__INT_MAX__: - break; } using underlying_type = std::underlying_type<bitmask_type>::type; static_assert( sizeof(underlying_type) == sizeof(int), diff --git a/libstdc++-v3/testsuite/27_io/ios_base/types/openmode/case_label.cc b/libstdc++-v3/testsuite/27_io/ios_base/types/openmode/case_label.cc index e132070..44b84a9 100644 --- a/libstdc++-v3/testsuite/27_io/ios_base/types/openmode/case_label.cc +++ b/libstdc++-v3/testsuite/27_io/ios_base/types/openmode/case_label.cc @@ -48,12 +48,6 @@ case_labels(bitmask_type b) case std::ios_base::noreplace: break; #endif - case std::_S_ios_openmode_end: - break; - case __INT_MAX__: - break; - case ~__INT_MAX__: - break; } using underlying_type = std::underlying_type<bitmask_type>::type; static_assert( sizeof(underlying_type) == sizeof(int), diff --git a/libstdc++-v3/testsuite/27_io/ios_base/types/seekdir/case_label.cc b/libstdc++-v3/testsuite/27_io/ios_base/types/seekdir/case_label.cc index c7b9817..2dd1560 100644 --- a/libstdc++-v3/testsuite/27_io/ios_base/types/seekdir/case_label.cc +++ b/libstdc++-v3/testsuite/27_io/ios_base/types/seekdir/case_label.cc @@ -38,7 +38,5 @@ case_labels(test_type b) break; case std::ios_base::end: break; - case std::_S_ios_fmtflags_end: - break; } } diff --git a/libstdc++-v3/testsuite/27_io/manipulators/extended/get_time/wchar_t/1.cc b/libstdc++-v3/testsuite/27_io/manipulators/extended/get_time/wchar_t/1.cc index 0f37282..ee3e84d 100644 --- a/libstdc++-v3/testsuite/27_io/manipulators/extended/get_time/wchar_t/1.cc +++ b/libstdc++-v3/testsuite/27_io/manipulators/extended/get_time/wchar_t/1.cc @@ -36,8 +36,8 @@ void test01() tm time1; iss >> get_time(&time1, L"%H:%M:%S %Y"); VERIFY( static_cast<bool>(iss) ); - VERIFY(time1.tm_hour = 12); - VERIFY(time1.tm_min = 1); + VERIFY(time1.tm_hour == 12); + VERIFY(time1.tm_min == 1); VERIFY(time1.tm_sec == 30); VERIFY(time1.tm_year == 71); } diff --git a/libstdc++-v3/testsuite/27_io/print/1.cc b/libstdc++-v3/testsuite/27_io/print/1.cc index 2a74e50..58f1eb1 100644 --- a/libstdc++-v3/testsuite/27_io/print/1.cc +++ b/libstdc++-v3/testsuite/27_io/print/1.cc @@ -68,15 +68,22 @@ test_print_raw() void test_vprint_nonunicode() { - std::vprint_nonunicode("{0} in \xc0 {0} out\n", + std::vprint_nonunicode_buffered("{0} in \xc0 {0} out\n", std::make_format_args("garbage")); - // { dg-output "garbage in . garbage out" } + // { dg-output "garbage in . garbage out\r?\n" } + std::vprint_nonunicode_buffered(stdout, "{0} in \xc3 {0} out\n", + std::make_format_args("junk")); + // { dg-output "junk in . junk out\r?\n" } + std::vprint_nonunicode(stdout, "{0} in \xc2 {0} out\n", + std::make_format_args("trash")); + // { dg-output "trash in . trash out\r?\n" } + } +#ifdef __cpp_exceptions void test_errors() { -#ifdef __cpp_exceptions try { std::print(stdin, "{}", "nope"); @@ -85,9 +92,47 @@ test_errors() catch (const std::system_error&) { } -#endif } +struct ThrowOnFormat +{}; + +template<typename CharT> +struct std::formatter<ThrowOnFormat, CharT> +{ + constexpr typename std::basic_format_parse_context<CharT>::iterator + parse(const std::basic_format_parse_context<CharT>& pc) const + { return pc.begin(); } + + template<typename Out> + typename std::basic_format_context<Out, CharT>::iterator + format(ThrowOnFormat, const std::basic_format_context<Out, CharT>&) const + { throw ThrowOnFormat{}; } +}; + +void +test_buffered() +{ + __gnu_test::scoped_file f; + FILE* strm = std::fopen(f.path.string().c_str(), "w"); + VERIFY( strm ); + try + { + std::string s = "Test"; + ThrowOnFormat tf; + std::vprint_unicode_buffered(strm, "{} {} {} {}", std::make_format_args(s, s, s, tf)); + VERIFY(false); + } + catch (ThrowOnFormat) + { } + std::fclose(strm); + + std::ifstream in(f.path); + std::string txt(std::istreambuf_iterator<char>(in), {}); + VERIFY( txt.empty() ); +} +#endif + int main() { test_print_default(); @@ -96,5 +141,8 @@ int main() test_println_file(); test_print_raw(); test_vprint_nonunicode(); +#ifdef __cpp_exceptions test_errors(); + test_buffered(); +#endif } diff --git a/libstdc++-v3/testsuite/28_regex/algorithms/regex_match/multiline.cc b/libstdc++-v3/testsuite/28_regex/algorithms/regex_match/multiline.cc index a1982fc..f4b3cf0 100644 --- a/libstdc++-v3/testsuite/28_regex/algorithms/regex_match/multiline.cc +++ b/libstdc++-v3/testsuite/28_regex/algorithms/regex_match/multiline.cc @@ -27,11 +27,11 @@ test01() VERIFY(std::regex_search("x\nab\nx", ml)); ml.assign("a$\n^b$\n^c", ECMAScript|__multiline); - VERIFY( ml.flags() == ECMAScript|__multiline ); + VERIFY( ml.flags() == (ECMAScript|__multiline) ); VERIFY( regex_search("a\nb\nc", ml) ); ml.assign("a$\n^b$\n^c", ECMAScript|__multiline|icase); - VERIFY( ml.flags() == ECMAScript|__multiline|icase ); + VERIFY( ml.flags() == (ECMAScript|__multiline|icase) ); VERIFY( regex_search("A\nB\nC", ml) ); } diff --git a/libstdc++-v3/testsuite/28_regex/basic_regex/assign/wchar_t/pstring.cc b/libstdc++-v3/testsuite/28_regex/basic_regex/assign/wchar_t/pstring.cc index 2e9dea1..91e5512 100644 --- a/libstdc++-v3/testsuite/28_regex/basic_regex/assign/wchar_t/pstring.cc +++ b/libstdc++-v3/testsuite/28_regex/basic_regex/assign/wchar_t/pstring.cc @@ -23,6 +23,7 @@ // [28.8.3] class template basic_regex assign() #include <regex> +#include <cwchar> // Tests assign operation from a Pascal-style counted-string. void test01() @@ -31,7 +32,7 @@ void test01() const wchar_t cs[] = L"aab"; test_type re; - re.assign(cs, sizeof(cs)-1, std::regex_constants::basic); + re.assign(cs, std::wcslen(cs), std::regex_constants::basic); } int diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/lwg3220.cc b/libstdc++-v3/testsuite/29_atomics/atomic/lwg3220.cc index 217db7c..a8b7851 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic/lwg3220.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic/lwg3220.cc @@ -1,4 +1,4 @@ -// { dg-options "-Wno-deprecated" } +// { dg-options "-Wno-deprecated-declarations" } // { dg-do compile { target c++11 } } // std::shared_ptr is not freestanding. diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/operators/pointer_partial_void.cc b/libstdc++-v3/testsuite/29_atomics/atomic/operators/pointer_partial_void.cc index e959418a1..1e2f71e 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic/operators/pointer_partial_void.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic/operators/pointer_partial_void.cc @@ -1,6 +1,7 @@ // { dg-do run { target { c++11_only || c++14_only } } } // { dg-require-atomic-builtins "" } // { dg-require-effective-target hosted } +// { dg-additional-options "-Wsystem-headers" } // Copyright (C) 2012-2025 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/requirements/types_neg.cc b/libstdc++-v3/testsuite/29_atomics/atomic/requirements/types_neg.cc index 5aa3243..b910548 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic/requirements/types_neg.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic/requirements/types_neg.cc @@ -19,7 +19,10 @@ #include <atomic> -std::atomic<const int> a; // { dg-error "here" } +std::atomic<const int> ca; // { dg-error "here" } +std::atomic<volatile int> va; // { dg-error "here" } +std::atomic<const volatile int> cva; // { dg-error "here" } +// { dg-error "assignment to read-only type" "" { target *-*-* } 0 } struct MoveOnly { @@ -40,3 +43,4 @@ struct NoMove std::atomic<NoMove> c; // { dg-error "here" } // { dg-error "static assertion failed" "" { target *-*-* } 0 } +// { dg-error "use of deleted function" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/100334.cc b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/100334.cc index 018c0c9..21ff570c 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/100334.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic/wait_notify/100334.cc @@ -47,16 +47,17 @@ int main() { // all atomic share the same waiter -// atomics_sharing_same_waiter<char> atomics; atomics_sharing_same_waiter<char> atomics; for (auto& atom : atomics.a) { atom->store(0); } - auto a = &std::__detail::__waiter_pool_base::_S_for(reinterpret_cast<char *>(atomics.a[0])); - auto b = &std::__detail::__waiter_pool_base::_S_for(reinterpret_cast<char *>(atomics.a[1])); +#if 0 + auto a = &std::__detail::__waitable_state::_S_state_for((void*)(atomics.a[0])); + auto b = &std::__detail::__waitable_state::_S_state_for((void*)(atomics.a[1])); VERIFY( a == b ); +#endif auto fut0 = std::async(std::launch::async, [&] { atomics.a[0]->wait(0); }); auto fut1 = std::async(std::launch::async, [&] { atomics.a[1]->wait(0); }); diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc b/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc index c7f8779..6e74f2c 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_integral/wait_notify.cc @@ -33,12 +33,16 @@ template<typename Tp> std::atomic<Tp> a{ Tp(1) }; VERIFY( a.load() == Tp(1) ); a.wait( Tp(0) ); + std::atomic<bool> b{false}; std::thread t([&] { - a.store(Tp(0)); - a.notify_one(); + b.store(true, std::memory_order_relaxed); + a.store(Tp(0)); + a.notify_one(); }); a.wait(Tp(1)); + // Ensure we actually waited until a.store(0) happened: + VERIFY( b.load(std::memory_order_relaxed) ); t.join(); } diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/address.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/address.cc new file mode 100644 index 0000000..e2eda50 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/address.cc @@ -0,0 +1,42 @@ +// { dg-do run { target c++26 } } +// { dg-require-atomic-cmpxchg-word "" } +// { dg-add-options libatomic } + +#include <atomic> +#include <memory> +#include <type_traits> + +#include <testsuite_hooks.h> + +template <typename T> +void testAtomicRefAddress() +{ + T x(T(42)); + const std::atomic_ref<T> a(x); + + static_assert( noexcept(a.address()) ); + static_assert( std::is_same_v<decltype(a.address()), T*> ); + VERIFY( std::addressof(x) == a.address() ); +} + +template <typename T> +void testAtomicRefAddressForCV() +{ + testAtomicRefAddress<T>(); + testAtomicRefAddress<const T>(); + if constexpr (std::atomic_ref<T>::is_always_lock_free) + { + testAtomicRefAddress<volatile T>(); + testAtomicRefAddress<const volatile T>(); + } +} + +int +main () +{ + struct X { int c; }; + testAtomicRefAddressForCV<X>(); + testAtomicRefAddressForCV<int>(); + testAtomicRefAddressForCV<float>(); + testAtomicRefAddressForCV<char*>(); +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/bool.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/bool.cc index 4702932..76ba9fd 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/bool.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/bool.cc @@ -1,15 +1,89 @@ -// { dg-do compile { target c++20 } } +// Copyright (C) 2019-2025 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-do run { target c++20 } } +// { dg-require-atomic-cmpxchg-word "" } +// { dg-add-options libatomic } #include <atomic> +#include <testsuite_hooks.h> +#include <type_traits> + +template<typename T> +using volatile_ + = std::conditional_t<std::atomic_ref<T>::is_always_lock_free, volatile T, T>; + +void +test01() +{ + bool value; + { + const auto mo = std::memory_order_relaxed; + std::atomic_ref<bool> a(value); + bool ok = a.is_lock_free(); + if constexpr (std::atomic_ref<bool>::is_always_lock_free) + VERIFY( ok ); + a = false; + VERIFY( !a.load() ); + VERIFY( !a.load(mo) ); + a.store(true); + VERIFY( a.load() ); + auto v = a.exchange(false); + VERIFY( !a.load() ); + VERIFY( v ); + v = a.exchange(true, mo); + VERIFY( a.load() ); + VERIFY( !v ); + + auto expected = a.load(); + while (!a.compare_exchange_weak(expected, false, mo, mo)) + { /* weak form can fail spuriously */ } + VERIFY( !a.load() ); + VERIFY( expected ); + + ok = a.compare_exchange_strong(expected, true); + VERIFY( !ok && !a.load() && !expected ); + + ok = a.compare_exchange_strong(expected, true); + VERIFY( ok && a.load() && !expected ); + } +} + +void +test02() +{ + bool b = false; + std::atomic_ref<bool> a0(b); + std::atomic_ref<bool> a1(b); + std::atomic_ref<const bool> a1c(b); + std::atomic_ref<volatile_<bool>> a1v(b); + std::atomic_ref<volatile_<const bool>> a1cv(b); + std::atomic_ref<bool> a2(a0); + b = true; + VERIFY( a1.load() ); + VERIFY( a1c.load() ); + VERIFY( a1v.load() ); + VERIFY( a1cv.load() ); + VERIFY( a2.load() ); +} -template<class T> concept has_and = requires (T& a) { a &= false; }; -template<class T> concept has_or = requires (T& a) { a |= false; }; -template<class T> concept has_xor = requires (T& a) { a ^= false; }; -template<class T> concept has_fetch_add = requires (T& a) { a.fetch_add(true); }; -template<class T> concept has_fetch_sub = requires (T& a) { a.fetch_sub(true); }; - -static_assert( not has_and<std::atomic_ref<bool>> ); -static_assert( not has_or<std::atomic_ref<bool>> ); -static_assert( not has_xor<std::atomic_ref<bool>> ); -static_assert( not has_fetch_add<std::atomic_ref<bool>> ); -static_assert( not has_fetch_sub<std::atomic_ref<bool>> ); +int +main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/cv_qual.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/cv_qual.cc new file mode 100644 index 0000000..dfc6a55 --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/cv_qual.cc @@ -0,0 +1,94 @@ +// Copyright (C) 2019-2025 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// <http://www.gnu.org/licenses/>. + +// { dg-do run { target c++20 } } +// { dg-require-atomic-cmpxchg-word "" } +// { dg-add-options libatomic } + +#include <atomic> +#include <testsuite_hooks.h> + +struct X +{ + X() = default; + X(int i) : i(i) { } + int i; + + friend bool + operator==(X, X) = default; +}; + +template<typename V> +void +test01(V v0, V v1) +{ + V value; + + if constexpr (std::atomic_ref<V>::is_always_lock_free) + { + std::atomic_ref<volatile V> a(value); + VERIFY( a.is_lock_free() ); + + a = v0; + VERIFY( V(a) == v0 ); + VERIFY( a.load() == v0 ); + + a.store(v1); + VERIFY( a.load() == v1 ); + + V last = a.exchange(v0); + VERIFY( a.load() == v0 ); + VERIFY( last == v1 ); + + V expected = a.load(); + while (!a.compare_exchange_weak(expected, v1)) + { /* weak form can fail spuriously */ } + VERIFY( a.load() == v1 ); + VERIFY( expected == v0 ); + + bool ok; + ok = a.compare_exchange_strong(expected, v0); + VERIFY( !ok && a.load() == v1 && expected == v1 ); + + ok = a.compare_exchange_strong(expected, v0); + VERIFY( ok && a.load() == v0 && expected == v1 ); + + std::atomic_ref<const volatile V> cva(value); + VERIFY( cva.is_lock_free() ); + VERIFY( V(cva) == v0 ); + VERIFY( cva.load() == v0 ); + } + + value = v0; + std::atomic_ref<const V> ca(value); + bool lf = ca.is_lock_free(); + if constexpr (std::atomic_ref<V>::is_always_lock_free) + VERIFY( lf ); + VERIFY( V(ca) == v0 ); + VERIFY( ca.load() == v0 ); +} + +int +main() +{ + int x; + test01<bool>(false, true); + test01<int>(1, 2); + test01<float>(1.2, 3.4); + test01<int*>(&x, &x+1); + test01<X>(12, 13); +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/deduction.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/deduction.cc index f67190e..bfc2080 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/deduction.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/deduction.cc @@ -19,22 +19,32 @@ #include <atomic> +template <typename T> void -test01() +test_impl(T v) { - int i = 0; - std::atomic_ref a0(i); - static_assert(std::is_same_v<decltype(a0), std::atomic_ref<int>>); - - float f = 1.0f; - std::atomic_ref a1(f); - static_assert(std::is_same_v<decltype(a1), std::atomic_ref<float>>); + std::atomic_ref a(v); + static_assert(std::is_same_v<decltype(a), std::atomic_ref<T>>); +} - int* p = &i; - std::atomic_ref a2(p); - static_assert(std::is_same_v<decltype(a2), std::atomic_ref<int*>>); +template <typename T> +void +test(T v) +{ + test_impl<T>(v); + test_impl<const T>(v); + if constexpr (std::atomic_ref<T>::is_always_lock_free) + { + test_impl<volatile T>(v); + test_impl<const volatile T>(v); + } +} +int main() +{ + test<int>(0); + test<float>(1.0f); + test<int*>(nullptr); struct X { } x; - std::atomic_ref a3(x); - static_assert(std::is_same_v<decltype(a3), std::atomic_ref<X>>); + test<X>(x); } diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/float.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/float.cc index 5773d14..5736668 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/float.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/float.cc @@ -19,6 +19,11 @@ #include <atomic> #include <testsuite_hooks.h> +#include <type_traits> + +template<typename T> +using volatile_ + = std::conditional_t<std::atomic_ref<T>::is_always_lock_free, volatile T, T>; void test01() @@ -297,17 +302,19 @@ test03() void test04() { - if constexpr (std::atomic_ref<float>::is_always_lock_free) - { - float i = 0; - float* ptr = 0; - std::atomic_ref<float*> a0(ptr); - std::atomic_ref<float*> a1(ptr); - std::atomic_ref<float*> a2(a0); - a0 = &i; - VERIFY( a1 == &i ); - VERIFY( a2 == &i ); - } + float i = 0.0f; + std::atomic_ref<float> a0(i); + std::atomic_ref<float> a1(i); + std::atomic_ref<const float> a1c(i); + std::atomic_ref<volatile_<float>> a1v(i); + std::atomic_ref<volatile_<const float>> a1cv(i); + std::atomic_ref<float> a2(a0); + a0 = 1.0f; + VERIFY( a1 == 1.0f ); + VERIFY( a1c == 1.0f ); + VERIFY( a1v == 1.0f ); + VERIFY( a1cv == 1.0f ); + VERIFY( a2 == 1.0f ); } int diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/generic.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/generic.cc index 2e6fa0f..7bdecdd 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/generic.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/generic.cc @@ -22,6 +22,11 @@ #include <atomic> #include <limits.h> #include <testsuite_hooks.h> +#include <type_traits> + +template<typename T> +using volatile_ + = std::conditional_t<std::atomic_ref<T>::is_always_lock_free, volatile T, T>; struct X { @@ -108,9 +113,15 @@ test02() X i; std::atomic_ref<X> a0(i); std::atomic_ref<X> a1(i); + std::atomic_ref<const X> a1c(i); + std::atomic_ref<volatile_<X>> a1v(i); + std::atomic_ref<volatile_<const X>> a1cv(i); std::atomic_ref<X> a2(a0); a0 = 42; VERIFY( a1.load() == 42 ); + VERIFY( a1c.load() == 42 ); + VERIFY( a1v.load() == 42 ); + VERIFY( a1cv.load() == 42 ); VERIFY( a2.load() == 42 ); } diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/integral.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/integral.cc index f6b68eb..010b40b 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/integral.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/integral.cc @@ -22,6 +22,11 @@ #include <atomic> #include <limits.h> #include <testsuite_hooks.h> +#include <type_traits> + +template<typename T> +using volatile_ + = std::conditional_t<std::atomic_ref<T>::is_always_lock_free, volatile T, T>; void test01() @@ -302,9 +307,15 @@ test03() int i = 0; std::atomic_ref<int> a0(i); std::atomic_ref<int> a1(i); + std::atomic_ref<const int> a1c(i); + std::atomic_ref<volatile_<int>> a1v(i); + std::atomic_ref<volatile_<const int>> a1cv(i); std::atomic_ref<int> a2(a0); a0 = 42; VERIFY( a1 == 42 ); + VERIFY( a1c == 42 ); + VERIFY( a1v == 42 ); + VERIFY( a1cv == 42 ); VERIFY( a2 == 42 ); } diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/op_support.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/op_support.cc new file mode 100644 index 0000000..3afa0bb --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/op_support.cc @@ -0,0 +1,127 @@ +// { dg-do compile { target c++20 } } + +#include <atomic> + +template<typename T> +concept is_supported + = !std::is_volatile_v<T> + || std::atomic_ref<std::remove_cv_t<T>>::is_always_lock_free; + +template<class T> concept has_and = requires (T& a) { a &= false; }; +template<class T> concept has_or = requires (T& a) { a |= false; }; +template<class T> concept has_xor = requires (T& a) { a ^= false; }; +template<class T> concept has_fetch_add = requires (T& a) { a.fetch_add(true); }; +template<class T> concept has_fetch_sub = requires (T& a) { a.fetch_sub(true); }; + +static constexpr std::memory_order mo = std::memory_order_seq_cst; + +#define HAS(op) (requires (std::atomic_ref<T> a, T t) { op; }) + +template<typename T> +void +no_stores() +{ + if constexpr (is_supported<T>) + { + static_assert( !HAS(a = t) ); + static_assert( !HAS(a.store(t)) ); + static_assert( !HAS(a.store(t, mo)) ); + static_assert( !HAS(a.exchange(t)) ); + static_assert( !HAS(a.exchange(t, mo)) ); + + static_assert( !HAS(a.compare_exchange_weak(t, t)) ); + static_assert( !HAS(a.compare_exchange_weak(t, t, mo)) ); + static_assert( !HAS(a.compare_exchange_weak(t, t, mo, mo)) ); + + static_assert( !HAS(a.compare_exchange_strong(t, t)) ); + static_assert( !HAS(a.compare_exchange_strong(t, t, mo)) ); + static_assert( !HAS(a.compare_exchange_strong(t, t, mo, mo)) ); + } +} + +template<typename T> +void +no_additions() +{ + if constexpr (is_supported<T>) + { + static_assert( !HAS(a++) ); + static_assert( !HAS(++a) ); + static_assert( !HAS(a += t) ); + static_assert( !HAS(a.fetch_add(t)) ); + static_assert( !HAS(a.fetch_add(t, mo)) ); + + static_assert( !HAS(a--) ); + static_assert( !HAS(--a) ); + static_assert( !HAS(a -= t) ); + static_assert( !HAS(a.fetch_sub(t)) ); + static_assert( !HAS(a.fetch_sub(t, mo)) ); + } +} + +template<typename T> +void +no_bitops() +{ + if constexpr (is_supported<T>) + { + static_assert( !HAS(a &= t) ); + static_assert( !HAS(a.fetch_and(t)) ); + static_assert( !HAS(a.fetch_and(t, mo)) ); + + static_assert( !HAS(a |= t) ); + static_assert( !HAS(a.fetch_or(t)) ); + static_assert( !HAS(a.fetch_or(t, mo)) ); + + static_assert( !HAS(a ^= t) ); + static_assert( !HAS(a.fetch_xor(t)) ); + static_assert( !HAS(a.fetch_xor(t, mo)) ); + } +} + +template<typename T> +void +no_math() +{ + no_additions<T>(); + no_bitops<T>(); +} + +template<typename T> +void +no_mutations() +{ + no_stores<T>(); + no_math<T>(); +} + +struct S +{ + int x; + int y; +}; + +int main() +{ + no_mutations<const int>(); + no_mutations<const volatile int>(); + + no_bitops<float>(); + no_bitops<volatile float>(); + no_mutations<const float>(); + + no_bitops<int*>(); + no_bitops<int* volatile>(); + no_mutations<int* const>(); + no_mutations<int* const volatile>(); + + no_math<bool>(); + no_math<volatile bool>(); + no_mutations<const bool>(); + no_mutations<const volatile bool>(); + + no_math<S>(); + no_math<volatile S>(); + no_mutations<const S>(); + no_mutations<const volatile S>(); +} diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer.cc index d1789af..c503b33 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/pointer.cc @@ -21,6 +21,11 @@ #include <atomic> #include <testsuite_hooks.h> +#include <type_traits> + +template<typename T> +using volatile_ + = std::conditional_t<std::atomic_ref<T>::is_always_lock_free, volatile T, T>; void test01() @@ -210,9 +215,15 @@ test03() int* ptr = 0; std::atomic_ref<int*> a0(ptr); std::atomic_ref<int*> a1(ptr); + std::atomic_ref<int* const> a1c(ptr); + std::atomic_ref<volatile_<int*>> a1v(ptr); + std::atomic_ref<volatile_<int* const>> a1cv(ptr); std::atomic_ref<int*> a2(a0); a0 = &i; VERIFY( a1 == &i ); + VERIFY( a1c == &i ); + VERIFY( a1v == &i ); + VERIFY( a1cv == &i ); VERIFY( a2 == &i ); } diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements.cc index 3b92956..5d67c77 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements.cc @@ -18,56 +18,109 @@ // { dg-do compile { target c++20 } } #include <atomic> +#include <type_traits> +template<typename T> +concept is_supported + = !std::is_volatile_v<T> + || std::atomic_ref<std::remove_cv_t<T>>::is_always_lock_free; + +template <class T> void -test01() +test_generic() { - struct X { int c; }; - using A = std::atomic_ref<X>; - static_assert( std::is_standard_layout_v<A> ); - static_assert( std::is_nothrow_copy_constructible_v<A> ); - static_assert( std::is_trivially_destructible_v<A> ); - static_assert( std::is_same_v<A::value_type, X> ); - static_assert( !std::is_copy_assignable_v<A> ); - static_assert( !std::is_move_assignable_v<A> ); + if constexpr (is_supported<T>) + { + using A = std::atomic_ref<T>; + static_assert( std::is_standard_layout_v<A> ); + static_assert( std::is_nothrow_copy_constructible_v<A> ); + static_assert( std::is_trivially_destructible_v<A> ); + static_assert( std::is_same_v<typename A::value_type, std::remove_cv_t<T>> ); + static_assert( !requires { typename A::difference_type; } ); + static_assert( !std::is_copy_assignable_v<A> ); + static_assert( !std::is_move_assignable_v<A> ); + } } +template <class T> void -test02() +test_integral() { - using A = std::atomic_ref<int>; - static_assert( std::is_standard_layout_v<A> ); - static_assert( std::is_nothrow_copy_constructible_v<A> ); - static_assert( std::is_trivially_destructible_v<A> ); - static_assert( std::is_same_v<A::value_type, int> ); - static_assert( std::is_same_v<A::difference_type, A::value_type> ); - static_assert( !std::is_copy_assignable_v<A> ); - static_assert( !std::is_move_assignable_v<A> ); + if constexpr (is_supported<T>) + { + using A = std::atomic_ref<T>; + static_assert( std::is_standard_layout_v<A> ); + static_assert( std::is_nothrow_copy_constructible_v<A> ); + static_assert( std::is_trivially_destructible_v<A> ); + static_assert( std::is_same_v<typename A::value_type, std::remove_cv_t<T>> ); + static_assert( std::is_same_v<typename A::difference_type, typename A::value_type> ); + static_assert( !std::is_copy_assignable_v<A> ); + static_assert( !std::is_move_assignable_v<A> ); + } } +template <class T> void -test03() +test_floating_point() { - using A = std::atomic_ref<double>; - static_assert( std::is_standard_layout_v<A> ); - static_assert( std::is_nothrow_copy_constructible_v<A> ); - static_assert( std::is_trivially_destructible_v<A> ); - static_assert( std::is_same_v<A::value_type, double> ); - static_assert( std::is_same_v<A::difference_type, A::value_type> ); - static_assert( !std::is_copy_assignable_v<A> ); - static_assert( !std::is_move_assignable_v<A> ); + if constexpr (is_supported<T>) + { + using A = std::atomic_ref<T>; + static_assert( std::is_standard_layout_v<A> ); + static_assert( std::is_nothrow_copy_constructible_v<A> ); + static_assert( std::is_trivially_destructible_v<A> ); + static_assert( std::is_same_v<typename A::value_type, std::remove_cv_t<T>> ); + static_assert( std::is_same_v<typename A::difference_type, typename A::value_type> ); + static_assert( !std::is_copy_assignable_v<A> ); + static_assert( !std::is_move_assignable_v<A> ); + } } +template <class T> void -test04() +test_pointer() +{ + if constexpr (is_supported<T>) + { + using A = std::atomic_ref<T>; + static_assert( std::is_standard_layout_v<A> ); + static_assert( std::is_nothrow_copy_constructible_v<A> ); + static_assert( std::is_trivially_destructible_v<A> ); + static_assert( std::is_same_v<typename A::value_type, std::remove_cv_t<T>> ); + static_assert( std::is_same_v<typename A::difference_type, std::ptrdiff_t> ); + static_assert( std::is_nothrow_copy_constructible_v<A> ); + static_assert( !std::is_copy_assignable_v<A> ); + static_assert( !std::is_move_assignable_v<A> ); + } +} + +int +main() { - using A = std::atomic_ref<int*>; - static_assert( std::is_standard_layout_v<A> ); - static_assert( std::is_nothrow_copy_constructible_v<A> ); - static_assert( std::is_trivially_destructible_v<A> ); - static_assert( std::is_same_v<A::value_type, int*> ); - static_assert( std::is_same_v<A::difference_type, std::ptrdiff_t> ); - static_assert( std::is_nothrow_copy_constructible_v<A> ); - static_assert( !std::is_copy_assignable_v<A> ); - static_assert( !std::is_move_assignable_v<A> ); + struct X { int c; }; + test_generic<X>(); + test_generic<const X>(); + test_generic<volatile X>(); + test_generic<const volatile X>(); + + // atomic_ref excludes (cv) `bool` from the set of integral types + test_generic<bool>(); + test_generic<const bool>(); + test_generic<volatile bool>(); + test_generic<const volatile bool>(); + + test_integral<int>(); + test_integral<const int>(); + test_integral<volatile int>(); + test_integral<const volatile int>(); + + test_floating_point<double>(); + test_floating_point<const double>(); + test_floating_point<volatile double>(); + test_floating_point<const volatile double>(); + + test_pointer<int*>(); + test_pointer<int* const>(); + test_pointer<int* volatile>(); + test_pointer<int* const volatile>(); } diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements_neg.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements_neg.cc new file mode 100644 index 0000000..8b0abbd --- /dev/null +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/requirements_neg.cc @@ -0,0 +1,34 @@ +// { dg-do compile { target c++20 } } + +#include <atomic> + +template<size_t N> +struct NonTrivial +{ + NonTrivial() = default; + NonTrivial(NonTrivial const&) { }; +}; + +template<size_t N> +NonTrivial<N> ntv; + +std::atomic_ref<NonTrivial<0>> nt(ntv<0>); // { dg-error "here" } +std::atomic_ref<const NonTrivial<1>> cnt(ntv<1>); // { dg-error "here" } +std::atomic_ref<volatile NonTrivial<2>> vnt(ntv<2>); // { dg-error "here" } +std::atomic_ref<const volatile NonTrivial<3>> cvnt(ntv<3>); // { dg-error "here" } + +template<size_t N> +struct NonLockFree +{ + char c[1024 + N]; +}; + +template<size_t N> +NonLockFree<N> nlfv; + +std::atomic_ref<NonLockFree<0>> nlf(nlfv<0>); +std::atomic_ref<const NonLockFree<1>> cnlf(nlfv<1>); +std::atomic_ref<volatile NonLockFree<2>> vnlf(nlfv<2>); // { dg-error "here" } +std::atomic_ref<const volatile NonLockFree<3>> cvnlf(nlfv<3>); // { dg-error "here" } + +// { dg-error "static assertion failed" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc b/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc index ecabeec..db20a19 100644 --- a/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc +++ b/libstdc++-v3/testsuite/29_atomics/atomic_ref/wait_notify.cc @@ -41,6 +41,16 @@ template<typename S> }); a.wait(va); t.join(); + + std::atomic_ref<const S> b{ aa }; + b.wait(va); + std::thread t2([&] + { + a.store(va); + a.notify_one(); + }); + b.wait(vb); + t2.join(); } } diff --git a/libstdc++-v3/testsuite/30_threads/barrier/1.cc b/libstdc++-v3/testsuite/30_threads/barrier/1.cc index eff8ef3..01c55d9 100644 --- a/libstdc++-v3/testsuite/30_threads/barrier/1.cc +++ b/libstdc++-v3/testsuite/30_threads/barrier/1.cc @@ -16,7 +16,8 @@ // <http://www.gnu.org/licenses/>. // { dg-do compile { target c++20 } } -// { dg-require-effective-target gthreads } +// { dg-require-effective-target gthreads { target { ! *-*-linux* } } } +// { dg-require-effective-target hosted } // { dg-add-options no_pch } #include <barrier> diff --git a/libstdc++-v3/testsuite/30_threads/barrier/2.cc b/libstdc++-v3/testsuite/30_threads/barrier/2.cc index e0188b3..728030b 100644 --- a/libstdc++-v3/testsuite/30_threads/barrier/2.cc +++ b/libstdc++-v3/testsuite/30_threads/barrier/2.cc @@ -16,7 +16,7 @@ // <http://www.gnu.org/licenses/>. // { dg-do compile { target c++20 } } -// { dg-require-effective-target gthreads } +// { dg-require-effective-target gthreads { target { ! *-*-linux* } } } // { dg-require-effective-target hosted } // { dg-add-options no_pch } diff --git a/libstdc++-v3/testsuite/30_threads/barrier/cons.cc b/libstdc++-v3/testsuite/30_threads/barrier/cons.cc new file mode 100644 index 0000000..0b80514 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/barrier/cons.cc @@ -0,0 +1,6 @@ +// { dg-do compile { target c++20 } } + +#include <barrier> + +// PR 118395 Constructor of std::barrier is not constexpr +constinit std::barrier<> b(std::barrier<>::max()); diff --git a/libstdc++-v3/testsuite/30_threads/barrier/lwg3898.cc b/libstdc++-v3/testsuite/30_threads/barrier/lwg3898.cc new file mode 100644 index 0000000..e3160dc --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/barrier/lwg3898.cc @@ -0,0 +1,45 @@ +// { dg-do run { target c++20 } } +// { dg-require-effective-target gthreads } + +#include <barrier> +#include <exception> +#include <cstdlib> +#if !_GLIBCXX_USE_C99_STDLIB && defined _GLIBCXX_HAVE_UNISTD_H +# include <unistd.h> +#endif + +void handle_terminate() +{ +#if _GLIBCXX_USE_C99_STDLIB + std::_Exit(0); +#elif defined _GLIBCXX_HAVE_UNISTD_H + _exit(0); +#else + std::exit(0); +#endif +} + +struct F +{ + void operator()() + { + std::set_terminate(handle_terminate); + throw 1; + } +}; + +void +test_lwg3898() +{ + std::barrier<F> b(1, F{}); + // This should call the terminate handler and exit with zero status: + b.arrive_and_wait(); + // Should not reach here: + std::abort(); +} + +int +main() +{ + test_lwg3898(); +} diff --git a/libstdc++-v3/testsuite/30_threads/condition_variable/members/116586.cc b/libstdc++-v3/testsuite/30_threads/condition_variable/members/116586.cc new file mode 100644 index 0000000..e8c3e16 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/condition_variable/members/116586.cc @@ -0,0 +1,63 @@ +// { dg-do run { target c++11 } } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-require-gthreads "" } +// { dg-require-effective-target hosted } + +#include <condition_variable> +#include <chrono> +#include <mutex> +#include <initializer_list> +#include <testsuite_hooks.h> + +namespace chrono = std::chrono; + +// thread.timedmutex.requirements.general: +// If abs_time has already passed, the function attempts to obtain +// ownership without blocking (as if by calling try_lock()). + +template <typename Clock> +void +test_absolute(chrono::nanoseconds offset) +{ + std::mutex mtx; + std::condition_variable cv; + chrono::time_point<Clock> tp(offset); + std::unique_lock<std::mutex> lock(mtx); + // Doesn't cope with spurious wakeup + VERIFY(cv.wait_until(lock, tp) == std::cv_status::timeout); +} + +// The type of clock used for the actual wait depends on whether +// _GLIBCXX_USE_PTHREAD_MUTEX_CLOCKLOCK is defined. We might as well just test +// both steady_clock and system_clock. +template <typename Clock> +void +test_relative(chrono::nanoseconds offset) +{ + std::mutex mtx; + std::condition_variable cv; + const auto d = -Clock::now().time_since_epoch() + offset; + std::unique_lock<std::mutex> lock(mtx); + // Doesn't cope with spurious wakeup + VERIFY(cv.wait_for(lock, d) == std::cv_status::timeout); +} + +int main() +{ + // It's not really possible to arrange for the relative calls to have + // tv_nsec == 0 due to time advancing. + for (const chrono::nanoseconds offset : { + // tv_sec == 0, tv_nsec == 0 + chrono::nanoseconds{0}, + // tv_sec == 0, tv_nsec < 0 + chrono::duration_cast<chrono::nanoseconds>(chrono::milliseconds{-10}), + // tv_sec < 0 + chrono::duration_cast<chrono::nanoseconds>(chrono::seconds{-10}) + }) { + test_absolute<chrono::system_clock>(offset); + test_relative<chrono::system_clock>(offset); + + test_absolute<chrono::steady_clock>(offset); + test_relative<chrono::steady_clock>(offset); + } +} diff --git a/libstdc++-v3/testsuite/30_threads/future/members/116586.cc b/libstdc++-v3/testsuite/30_threads/future/members/116586.cc new file mode 100644 index 0000000..82f1e5c --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/future/members/116586.cc @@ -0,0 +1,58 @@ +// { dg-do run { target c++11 } } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-require-gthreads "" } +// { dg-require-effective-target hosted } + +#include <future> +#include <chrono> +#include <initializer_list> +#include <testsuite_hooks.h> + +namespace chrono = std::chrono; + +// thread.timedmutex.requirements.general: +// If abs_time has already passed, the function attempts to obtain +// ownership without blocking (as if by calling try_lock()). + +template <typename Clock> +void +test_absolute(chrono::nanoseconds offset) +{ + std::promise<int> p; + std::future<int> f = p.get_future(); + const chrono::time_point<Clock> tp(offset); + VERIFY(f.wait_until(tp) == std::future_status::timeout); +} + +// The type of clock used for the actual wait depends on whether +// _GLIBCXX_HAVE_LINUX_FUTEX is defined. We might as well just test both +// steady_clock and system_clock. +template <typename Clock> +void +test_relative(chrono::nanoseconds offset) +{ + std::promise<int> p; + std::future<int> f = p.get_future(); + const auto d = -Clock::now().time_since_epoch() + offset; + VERIFY(f.wait_for(d) == std::future_status::timeout); +} + +int main() +{ + // It's not really possible to arrange for the relative calls to have tv_nsec + // == 0 due to time advancing. + for (const chrono::nanoseconds offset : { + // tv_sec == 0, tv_nsec == 0 + chrono::nanoseconds{0}, + // tv_sec == 0, tv_nsec < 0 + chrono::duration_cast<chrono::nanoseconds>(chrono::milliseconds{-10}), + // tv_sec < 0 + chrono::duration_cast<chrono::nanoseconds>(chrono::seconds{-10}) + }) { + test_absolute<chrono::system_clock>(offset); + test_relative<chrono::system_clock>(offset); + + test_absolute<chrono::steady_clock>(offset); + test_relative<chrono::steady_clock>(offset); + } +} diff --git a/libstdc++-v3/testsuite/30_threads/packaged_task/cons/dangling_ref.cc b/libstdc++-v3/testsuite/30_threads/packaged_task/cons/dangling_ref.cc index 51c6ade..30e05b0 100644 --- a/libstdc++-v3/testsuite/30_threads/packaged_task/cons/dangling_ref.cc +++ b/libstdc++-v3/testsuite/30_threads/packaged_task/cons/dangling_ref.cc @@ -1,4 +1,5 @@ // { dg-do compile { target c++11 } } +// { dg-additional-options "-Wsystem-headers" } #include <future> // C++20 [futures.task.members] @@ -8,6 +9,5 @@ int f(); std::packaged_task<const int&()> task(f); // { dg-error "dangling reference" "" { target { c++14_down } } 0 } // { dg-error "reference to temporary" "" { target { c++14_down } } 0 } -// { dg-error "no matching function" "" { target c++17 } 0 } -// { dg-error "enable_if" "" { target c++17 } 0 } // { dg-error "static assertion failed" "" { target c++17 } 0 } +// { dg-error "note: .*std::is_invocable_r" "" { target c++17 } 0 } diff --git a/libstdc++-v3/testsuite/30_threads/packaged_task/cons/lwg4154_neg.cc b/libstdc++-v3/testsuite/30_threads/packaged_task/cons/lwg4154_neg.cc index 6ba1bb1..b3413c2 100644 --- a/libstdc++-v3/testsuite/30_threads/packaged_task/cons/lwg4154_neg.cc +++ b/libstdc++-v3/testsuite/30_threads/packaged_task/cons/lwg4154_neg.cc @@ -12,16 +12,16 @@ struct F { // Mandates: is_invocable_r_v<R, decay_t<F>&, ArgTypes...> is true. const F f; -std::packaged_task<void()> p(f); // { dg-error "here" "" { target c++17 } } -// { dg-error "static assertion failed" "" { target c++17 } 0 } -// { dg-error "invoke_r" "" { target *-*-* } 0 } -// { dg-prune-output "enable_if<false" } +std::packaged_task<void()> p(f); // { dg-error "here" } +// { dg-error "static assertion failed" "" { target *-*-* } 0 } +// { dg-error "note: .*std::is_invocable_r_v<void, " "" { target c++17 } 0 } // Only callable as rvalue struct Frv { int* operator()() && { return 0; } }; -std::packaged_task<int*()> p2(Frv{}); // { dg-error "here" "" { target c++17 } } +std::packaged_task<int*()> p2(Frv{}); // { dg-error "here" } +// { dg-error "note: .*std::is_invocable_r_v<int., " "" { target c++17 } 0 } // Only callable as non-const lvalue struct Fnc { diff --git a/libstdc++-v3/testsuite/30_threads/recursive_timed_mutex/try_lock_until/116586.cc b/libstdc++-v3/testsuite/30_threads/recursive_timed_mutex/try_lock_until/116586.cc new file mode 100644 index 0000000..25a78e7 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/recursive_timed_mutex/try_lock_until/116586.cc @@ -0,0 +1,75 @@ +// { dg-do run { target c++11 } } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-require-gthreads "" } +// { dg-require-effective-target hosted } + +#include <mutex> +#include <chrono> +#include <future> +#include <initializer_list> +#include <testsuite_hooks.h> + +namespace chrono = std::chrono; + +// thread.timedmutex.requirements.general: +// If abs_time has already passed, the function attempts to obtain +// ownership without blocking (as if by calling try_lock()). + +template <typename Clock> +void +test_absolute(chrono::nanoseconds offset) +{ + std::recursive_timed_mutex mtx; + chrono::time_point<Clock> tp(offset); + VERIFY(mtx.try_lock_until(tp)); + + { + // To test failing to lock a recursive mutex we need to try to lock on a + // different thread. + auto t = std::async(std::launch::async, [&mtx, tp]() { + VERIFY(!mtx.try_lock_until(tp)); + }); + } +} + +// The type of clock used for the actual wait depends on whether +// _GLIBCXX_USE_PTHREAD_MUTEX_CLOCKLOCK is defined. We might as well just test +// both steady_clock and system_clock. +template <typename Clock> +void +test_relative(chrono::nanoseconds offset) +{ + std::recursive_timed_mutex mtx; + const auto d = -Clock::now().time_since_epoch() + offset; + VERIFY(mtx.try_lock_for(d)); + + { + // To test failing to lock a recursive mutex we need to try to lock on a + // different thread. + auto t = std::async(std::launch::async, [&mtx, d]() { + VERIFY(!mtx.try_lock_for(d)); + }); + } +} + +int main() +{ + // Try once with an offset that ought to result in tv_sec == 0, tv_nsec < 0 + // and one with an offset that ought to result in tv_sec < 0, tv_nsec == 0 + // for the absolute calls at least. It's not really possible to arrange for + // the relative calls to have tv_nsec == 0 due to time advancing. + for (const chrono::nanoseconds offset : { + // tv_sec == 0, tv_nsec == 0 + chrono::nanoseconds{0}, + // tv_sec == 0, tv_nsec < 0 + chrono::duration_cast<chrono::nanoseconds>(chrono::milliseconds{-10}), + // tv_sec < 0 + chrono::duration_cast<chrono::nanoseconds>(chrono::seconds{-10}) + }) { + test_absolute<chrono::system_clock>(offset); + test_relative<chrono::system_clock>(offset); + + test_absolute<chrono::steady_clock>(offset); + test_relative<chrono::steady_clock>(offset); + } +} diff --git a/libstdc++-v3/testsuite/30_threads/scoped_lock/requirements/typedefs.cc b/libstdc++-v3/testsuite/30_threads/scoped_lock/requirements/typedefs.cc index 4cd07da..ba52b36 100644 --- a/libstdc++-v3/testsuite/30_threads/scoped_lock/requirements/typedefs.cc +++ b/libstdc++-v3/testsuite/30_threads/scoped_lock/requirements/typedefs.cc @@ -1,5 +1,4 @@ // { dg-do compile { target c++17 } } -// { dg-require-gthreads "" } // { dg-add-options no_pch } // Copyright (C) 2017-2025 Free Software Foundation, Inc. @@ -29,9 +28,30 @@ # error "Feature-test macro for scoped_lock has wrong value" #endif +struct BasicLockable +{ + BasicLockable() = default; + ~BasicLockable() = default; + void lock() { } + void unlock() { } +}; + void test01() { - // Check for required typedefs - typedef std::scoped_lock<std::mutex> test_type; - typedef test_type::mutex_type mutex_type; + // Check for required typedef. + using test_type = std::scoped_lock<BasicLockable>; + static_assert(std::is_same_v<test_type::mutex_type, BasicLockable>); +} + +template<typename T, typename = void> +constexpr bool has_mutex_type = false; + +template<typename T> +constexpr bool has_mutex_type<T, std::void_t<typename T::mutex_type>> = true; + +void test02() +{ + // Check that typedef is absent as required. + using test_type = std::scoped_lock<BasicLockable, BasicLockable>; + static_assert(!has_mutex_type<test_type>); } diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc index ac9f97b..9472def 100644 --- a/libstdc++-v3/testsuite/30_threads/semaphore/1.cc +++ b/libstdc++-v3/testsuite/30_threads/semaphore/1.cc @@ -16,6 +16,8 @@ // <http://www.gnu.org/licenses/>. // { dg-do compile { target c++20 } } +// { dg-require-effective-target gthreads { target { ! *-*-linux* } } } +// { dg-require-effective-target hosted } // { dg-add-options no_pch } #include <semaphore> @@ -25,3 +27,25 @@ #elif __cpp_lib_semaphore != 201907L # error "Feature-test macro for semaphore has wrong value in <semaphore>" #endif + +static_assert(std::is_same_v<std::counting_semaphore<1>, + std::binary_semaphore>); + +static_assert(! std::is_same_v<std::counting_semaphore<2>, + std::binary_semaphore>); + +static_assert(! std::is_same_v<std::counting_semaphore<>, + std::binary_semaphore>); + +// The standard permits max() to be greater than the template argument, +// but for the current libstdc++ implementation it's always equal to it. +static_assert(std::binary_semaphore::max() == 1); +static_assert(std::counting_semaphore<0>::max() == 0); +static_assert(std::counting_semaphore<2>::max() == 2); + +#include <limits.h> + +static_assert(std::counting_semaphore<INT_MAX>::max() == INT_MAX); +static_assert(std::counting_semaphore<INT_MAX-1>::max() == INT_MAX-1); +static_assert(std::counting_semaphore<PTRDIFF_MAX>::max() == PTRDIFF_MAX); +static_assert(std::counting_semaphore<PTRDIFF_MAX-3>::max() == PTRDIFF_MAX-3); diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/100806.cc b/libstdc++-v3/testsuite/30_threads/semaphore/100806.cc index c770f05..4b761ce 100644 --- a/libstdc++-v3/testsuite/30_threads/semaphore/100806.cc +++ b/libstdc++-v3/testsuite/30_threads/semaphore/100806.cc @@ -12,7 +12,7 @@ #include <chrono> #include <vector> -std::counting_semaphore<4> semaphore{6}; +std::counting_semaphore<6> semaphore{6}; std::mutex mtx; std::vector<std::string> results; diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/104928-2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/104928-2.cc new file mode 100644 index 0000000..7b90da8 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/104928-2.cc @@ -0,0 +1,101 @@ +// { dg-do run { target c++20 } } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-require-gthreads "" } +// { dg-add-options libatomic } + +// Bug libstdc++/104928 - std::counting_semaphore on Linux can sleep forever + +#include <semaphore> +#include <thread> +#include <chrono> +#include <atomic> + +std::binary_semaphore t1(1); +std::binary_semaphore sem2(0); +std::atomic<int> room1 = 0; +int room2 = 0; + +std::atomic<bool> run{true}; + +enum class AcquireKind { Acquire, Try, TryFor }; + +template<std::ptrdiff_t N, AcquireKind Kind> +struct Morris +{ + using Semaphore = std::counting_semaphore<N>; + + Semaphore sem1{1}; + Semaphore sem2{0}; + unsigned counter = 0; + + void operator()() + { + while (run) + { + room1 += 1; + + acquire(sem1); + room2 += 1; + room1 -= 1; + if (room1 == 0) + sem2.release(); + else + sem1.release(); + + acquire(sem2); + room2 -= 1; + + // critical region + ++counter; + // end critical region + + if (room2 == 0) + sem1.release(); + else + sem2.release(); + } + } + + void acquire(Semaphore& sem) + { + using enum AcquireKind; + using namespace std::chrono; + if constexpr (Kind == Acquire) + sem.acquire(); + else if constexpr (Kind == Try) + while (!sem.try_acquire()) { } + else if constexpr (Kind == TryFor) + while (!sem.try_acquire_for(1h)) { } + } +}; + +template<std::ptrdiff_t N, AcquireKind Kind> +void +test_morris_kind() +{ + Morris<N, Kind> algo; + std::thread t1(std::ref(algo)); + std::thread t2(std::ref(algo)); + std::this_thread::sleep_for(std::chrono::seconds(2)); + run = false; + t1.join(); + t2.join(); +} + +template<std::ptrdiff_t N> +void +test_morris() +{ + test_morris_kind<N, AcquireKind::Acquire>(); + test_morris_kind<N, AcquireKind::Try>(); + test_morris_kind<N, AcquireKind::TryFor>(); +} + +int main() +{ + test_morris<1>(); // std::binary_semaphore + test_morris<1000>(); // std::counting_semaphore that can use futex +#if PTRDIFF_MAX > INT_MAX + // test_morris<PTRDIFF_MAX>(); // std::counting_semaphore that cannot use futex +#endif +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/104928.cc b/libstdc++-v3/testsuite/30_threads/semaphore/104928.cc new file mode 100644 index 0000000..f360da9 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/104928.cc @@ -0,0 +1,70 @@ +// { dg-do run { target c++20 } } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-require-gthreads "" } +// { dg-add-options libatomic } +// { dg-options "-DSIMULATOR_TEST" { target simulator } } + +// Bug libstdc++/104928 - std::counting_semaphore on Linux can sleep forever + +#include <semaphore> +#include <thread> +#include <chrono> +#include <climits> + +#ifdef SIMULATOR_TEST +const int loop_count = 100; +const int thread_count = 6; +#else +const int loop_count = 1000000; +const int thread_count = 20; +#endif + +template<std::ptrdiff_t N, typename Acquire> +void +test_acquire(Acquire acq_func) +{ + std::counting_semaphore<N * loop_count> s{0}; + std::thread threads[thread_count]; + for (int i = 0; i < thread_count; i += 2) { + threads[i] = std::thread([&s, &acq_func]() { + for (int i = 0; i < loop_count; ++i) + acq_func(s); + }); + threads[i+1] = std::thread([&s]() { + for (int i = 0; i < loop_count; ++i) + s.release(); + }); + } + for (auto& t : threads) + t.join(); +} + +template<typename Acquire> +void +test_all(Acquire f) +{ + const int max = INT_MAX / loop_count; + test_acquire<max>(f); // can use futex +#if PTRDIFF_MAX > INT_MAX + test_acquire<max * 10>(f); // cannot use futex +#endif +} + +int main() +{ + test_all([](auto& sem) { sem.acquire(); }); + + test_all([](auto& sem) { while (!sem.try_acquire()) { } }); + + using namespace std::chrono; + + test_all([](auto& sem) { while (!sem.try_acquire_for(1h)) { } }); + + auto try_acquire_until = [](auto& sem, auto time) { + while (!sem.try_acquire_until(time + 1h)) + { } + }; + test_all([&](auto& sem) { try_acquire_until(sem, system_clock::now()); }); + test_all([&](auto& sem) { try_acquire_until(sem, steady_clock::now()); }); + test_all([&](auto& sem) { try_acquire_until(sem, utc_clock::now()); }); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc index 251e96a..bff747c 100644 --- a/libstdc++-v3/testsuite/30_threads/semaphore/2.cc +++ b/libstdc++-v3/testsuite/30_threads/semaphore/2.cc @@ -16,6 +16,7 @@ // <http://www.gnu.org/licenses/>. // { dg-do compile { target c++20 } } +// { dg-require-effective-target gthreads { target { ! *-*-linux* } } } // { dg-require-effective-target hosted } // { dg-add-options no_pch } diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/3.cc b/libstdc++-v3/testsuite/30_threads/semaphore/3.cc new file mode 100644 index 0000000..51b9fbb --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/3.cc @@ -0,0 +1,19 @@ +// { dg-do compile { target *-*-*linux* } } +// { dg-require-effective-target c++20 } +// { dg-require-effective-target hosted } + +#include <semaphore> +#include <limits.h> + +// on Linux these specializations all use a futex: +static_assert(sizeof(std::counting_semaphore<0>) == sizeof(int)); +static_assert(sizeof(std::counting_semaphore<1>) == sizeof(int)); +static_assert(sizeof(std::counting_semaphore<INT_MAX>) == sizeof(int)); +static_assert(sizeof(std::counting_semaphore<>) == sizeof(int)); + +// This will use a futex iff ptrdiff_t has 32 bits: +static_assert(sizeof(std::counting_semaphore<PTRDIFF_MAX>) == sizeof(std::ptrdiff_t)); + +#if PTRDIFF_MAX > INT_MAX +static_assert(sizeof(std::counting_semaphore<INT_MAX+1LL>) == sizeof(std::ptrdiff_t)); +#endif diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/cons.cc b/libstdc++-v3/testsuite/30_threads/semaphore/cons.cc new file mode 100644 index 0000000..790ff2e --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/cons.cc @@ -0,0 +1,9 @@ +// { dg-do compile { target c++20 } } +// { dg-require-effective-target gthreads { target { ! *-*-linux* } } } +// { dg-require-effective-target hosted } + +#include <semaphore> + +// PR 110854 Constructor of std::counting_semaphore is not constexpr +constinit std::binary_semaphore b(0); +constinit std::counting_semaphore<5> c(2); diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/cons_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/cons_neg.cc new file mode 100644 index 0000000..56e27d7 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/cons_neg.cc @@ -0,0 +1,12 @@ +// { dg-options "-D_GLIBCXX_ASSERTIONS" } +// { dg-do run { target c++20 xfail *-*-* } } +// { dg-require-effective-target hosted } +// { dg-add-options libatomic } + +#include <semaphore> + +int main() +{ + // Preconditions: desired >= 0 is true, and desired <= max() is true. + std::binary_semaphore b(2); +} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value.cc new file mode 100644 index 0000000..67fa125 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value.cc @@ -0,0 +1,9 @@ +// { dg-do compile { target c++20 } } +// { dg-require-effective-target gthreads { target { ! *-*-linux* } } } +// { dg-require-effective-target hosted } + +#include <semaphore> + +std::counting_semaphore<> sem(0); +std::counting_semaphore<> sem2(2); +std::counting_semaphore sem3(3); diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc index 1498d38..e2680b1 100644 --- a/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc +++ b/libstdc++-v3/testsuite/30_threads/semaphore/least_max_value_neg.cc @@ -16,8 +16,8 @@ // <http://www.gnu.org/licenses/>. // { dg-do compile { target c++20 } } -// { dg-require-effective-target pthread } -// { dg-require-gthreads "" } +// { dg-require-effective-target gthreads { target { ! *-*-linux* } } } +// { dg-require-effective-target hosted } #include <semaphore> diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/platform_try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/platform_try_acquire_for.cc deleted file mode 100644 index 6d90564..0000000 --- a/libstdc++-v3/testsuite/30_threads/semaphore/platform_try_acquire_for.cc +++ /dev/null @@ -1,9 +0,0 @@ -// { dg-options "-D_GLIBCXX_USE_POSIX_SEMAPHORE" } -// { dg-do run { target c++20 } } -// { dg-additional-options "-pthread" { target pthread } } -// { dg-require-gthreads "" } -// { dg-add-options libatomic } - -#include "try_acquire_for.cc" - -// { dg-prune-output "ignoring _GLIBCXX_USE_POSIX_SEMAPHORE" } diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc index d709126..ea7859b 100644 --- a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire.cc @@ -17,7 +17,8 @@ // { dg-do run { target c++20 } } // { dg-additional-options "-pthread" { target pthread } } -// { dg-require-gthreads "" } +// { dg-require-effective-target gthreads { target { ! *-*-linux* } } } +// { dg-require-effective-target hosted } // { dg-add-options libatomic } #include <semaphore> diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc index 39681c7..94acb25 100644 --- a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_for.cc @@ -24,6 +24,7 @@ #include <chrono> #include <thread> #include <atomic> +#include <initializer_list> #include <testsuite_hooks.h> void test01() @@ -90,9 +91,30 @@ test03() s.try_acquire_for(timeout); } +// Prove semaphore doesn't suffer from PR116586 +template <typename Clock> +void +test_relative(std::chrono::nanoseconds offset) +{ + std::binary_semaphore sem(1); + VERIFY(sem.try_acquire_for(offset)); + VERIFY(!sem.try_acquire_for(offset)); +} + int main() { test01(); test02(); test03(); + using namespace std::chrono; + for (const nanoseconds offset : { + nanoseconds{0}, + nanoseconds{-10ms}, + nanoseconds{-10s} + }) { + test_relative<std::chrono::system_clock>(offset); + test_relative<std::chrono::system_clock>(offset - std::chrono::system_clock::now().time_since_epoch()); + test_relative<std::chrono::steady_clock>(offset); + test_relative<std::chrono::steady_clock>(offset - std::chrono::steady_clock::now().time_since_epoch()); + } } diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc deleted file mode 100644 index cf57455..0000000 --- a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_posix.cc +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright (C) 2020-2025 Free Software Foundation, Inc. -// -// This file is part of the GNU ISO C++ Library. This library is free -// software; you can redistribute it and/or modify it under the -// terms of the GNU General Public License as published by the -// Free Software Foundation; either version 3, or (at your option) -// any later version. - -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License along -// with this library; see the file COPYING3. If not see -// <http://www.gnu.org/licenses/>. - -// { dg-do run { target c++20 } } -// { dg-additional-options "-pthread" { target pthread } } -// { dg-require-gthreads "" } -// { dg-add-options libatomic } - -#include <semaphore> -#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE -#include <chrono> -#include <thread> -#include <atomic> -#include <testsuite_hooks.h> - -void test01() -{ - using namespace std::chrono_literals; - std::__platform_semaphore s(2); - s._M_acquire(); - - auto const dur = 250ms; - { - auto const t0 = std::chrono::steady_clock::now(); - VERIFY( s._M_try_acquire_for(dur) ); - auto const diff = std::chrono::steady_clock::now() - t0; - VERIFY( diff < dur ); - } - - { - auto const t0 = std::chrono::steady_clock::now(); - VERIFY( !s._M_try_acquire_for(dur) ); - auto const diff = std::chrono::steady_clock::now() - t0; - VERIFY( diff >= dur ); - } -} - -void test02() -{ - using namespace std::chrono_literals; - std::__platform_semaphore s(1); - std::atomic<int> a(0), b(0); - std::thread t([&] { - a.wait(0); - auto const dur = 250ms; - VERIFY( !s._M_try_acquire_for(dur) ); - b++; - b.notify_one(); - - a.wait(1); - VERIFY( s._M_try_acquire_for(dur) ); - b++; - b.notify_one(); - }); - t.detach(); - - s._M_acquire(); - a++; - a.notify_one(); - b.wait(0); - s._M_release(1); - a++; - a.notify_one(); - - b.wait(1); -} - -void test03() -{ - using namespace std::chrono_literals; - std::__platform_semaphore s(2); - s._M_acquire(); - - auto const dur = 250ms; - { - auto const at = std::chrono::system_clock::now() + dur; - auto const t0 = std::chrono::steady_clock::now(); - VERIFY( s._M_try_acquire_until(at) ); - auto const diff = std::chrono::steady_clock::now() - t0; - VERIFY( diff < dur ); - } - - { - auto const at = std::chrono::system_clock::now() + dur; - auto const t0 = std::chrono::steady_clock::now(); - VERIFY( !s._M_try_acquire_until(at) ); - auto const diff = std::chrono::steady_clock::now() - t0; - VERIFY( diff >= dur ); - } -} - -void test04() -{ - using namespace std::chrono_literals; - std::__platform_semaphore s(1); - std::atomic<int> a(0), b(0); - std::thread t([&] { - a.wait(0); - auto const dur = 250ms; - { - auto const at = std::chrono::system_clock::now() + dur; - VERIFY( !s._M_try_acquire_until(at) ); - - b++; - b.notify_one(); - } - - a.wait(1); - { - auto const at = std::chrono::system_clock::now() + dur; - VERIFY( s._M_try_acquire_until(at) ); - } - b++; - b.notify_one(); - }); - t.detach(); - - s._M_acquire(); - a++; - a.notify_one(); - b.wait(0); - s._M_release(1); - a++; - a.notify_one(); - - b.wait(1); -} -#endif - -int main() -{ -#ifdef _GLIBCXX_HAVE_POSIX_SEMAPHORE - test01(); - test02(); - test03(); - test04(); -#endif - return 0; -} diff --git a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc index de0068d..ed6bd11 100644 --- a/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc +++ b/libstdc++-v3/testsuite/30_threads/semaphore/try_acquire_until.cc @@ -24,6 +24,7 @@ #include <chrono> #include <thread> #include <atomic> +#include <initializer_list> #include <testsuite_hooks.h> void test01() @@ -87,8 +88,31 @@ void test02() b.wait(1); } +// Prove semaphore doesn't suffer from PR116586 +template <typename Clock> +void +test_absolute(std::chrono::nanoseconds offset) +{ + std::binary_semaphore sem(1); + std::chrono::time_point<Clock> tp(offset); + VERIFY(sem.try_acquire_until(tp)); + VERIFY(!sem.try_acquire_until(tp)); +} + int main() { test01(); test02(); + using namespace std::chrono; + for (const nanoseconds offset : { + // tv_sec == 0, tv_nsec == 0 + nanoseconds{0}, + // tv_sec == 0, tv_nsec < 0 + nanoseconds{-10ms}, + // tv_sec < 0 + nanoseconds{-10s} + }) { + test_absolute<std::chrono::system_clock>(offset); + test_absolute<std::chrono::steady_clock>(offset); + } } diff --git a/libstdc++-v3/testsuite/30_threads/shared_timed_mutex/try_lock_until/116586.cc b/libstdc++-v3/testsuite/30_threads/shared_timed_mutex/try_lock_until/116586.cc new file mode 100644 index 0000000..da9b653 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/shared_timed_mutex/try_lock_until/116586.cc @@ -0,0 +1,125 @@ +// { dg-do run { target c++14 } } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-require-gthreads "" } +// { dg-require-effective-target hosted } + +#include <shared_mutex> +#include <chrono> +#include <future> +#include <initializer_list> +#include <testsuite_hooks.h> + +namespace chrono = std::chrono; + +// [thread.timedmutex.requirements.general]: +// If abs_time has already passed, the function attempts to obtain +// ownership without blocking (as if by calling try_lock()). + +// C++14 [thread.sharedtimedmutex.class] 3.2 says it's undefined for a thread +// to attempt to recursively gain any ownership of a shared_timed_mutex. +// This isn't just theoretical, as Glibc's pthread_rwlock_timedrdlock will +// return EDEADLK if called on the same thread that already holds the +// exclusive (write) lock. +#define VERIFY_IN_NEW_THREAD(X) \ + (void) std::async(std::launch::async, [&] { VERIFY(X); }) + +// Unfortunately POSIX says that pthread_rwlock_clockwrlock, +// pthread_rwlock_clockrdlock, pthread_rwlock_timedwrlock and +// pthread_rwlock_timedrdlock are allowed to deadlock rather than return +// EDEADLK or just time out if the thread already has the associated lock. This +// means that we can't enable the deadlock tests by default. The glibc +// implementation does return EDEADLK and __shared_mutex_cv times out so we can +// test both of them. +#if (defined(__GLIBC__) || ! (_GLIBCXX_USE_PTHREAD_RWLOCK_T && _GTHREAD_USE_MUTEX_TIMEDLOCK)) \ + && ! defined(_GLIBCXX_ASSERTIONS) +#define DEADLOCK_VERIFY(X) \ + VERIFY(X) +#else +#define DEADLOCK_VERIFY(X) \ + do {} while(0) +#endif + +template <typename Clock> +void +test_exclusive_absolute(chrono::nanoseconds offset) +{ + std::shared_timed_mutex stm; + chrono::time_point<Clock> tp(offset); + VERIFY(stm.try_lock_until(tp)); + + // Trying to acquire the same long on the same thread + DEADLOCK_VERIFY(!stm.try_lock_until(tp)); + + // Check that false is returned on timeouts even if the implementation + // returned EDEADLK above by trying to lock on a different thread. + VERIFY_IN_NEW_THREAD(!stm.try_lock_until(tp)); +} + +template <typename Clock> +void +test_shared_absolute(chrono::nanoseconds offset) +{ + std::shared_timed_mutex stm; + chrono::time_point<Clock> tp(offset); + VERIFY(stm.try_lock_shared_until(tp)); + stm.unlock_shared(); + + VERIFY(stm.try_lock_for(chrono::seconds{10})); + DEADLOCK_VERIFY(!stm.try_lock_shared_until(tp)); + VERIFY_IN_NEW_THREAD(!stm.try_lock_shared_until(tp)); +} + +// The type of clock used for the actual wait depends on whether +// _GLIBCXX_USE_PTHREAD_RWLOCK_CLOCKLOCK is defined. We might as well just test +// both steady_clock and system_clock. +template <typename Clock> +void +test_exclusive_relative(chrono::nanoseconds offset) +{ + std::shared_timed_mutex stm; + const auto d = -Clock::now().time_since_epoch() + offset; + VERIFY(stm.try_lock_for(d)); + DEADLOCK_VERIFY(!stm.try_lock_for(d)); + VERIFY_IN_NEW_THREAD(!stm.try_lock_for(d)); +} + +template <typename Clock> +void +test_shared_relative(chrono::nanoseconds offset) +{ + std::shared_timed_mutex stm; + const auto d = -Clock::now().time_since_epoch() + offset; + VERIFY(stm.try_lock_shared_for(d)); + stm.unlock_shared(); + // Should complete immediately + VERIFY(stm.try_lock_for(chrono::seconds{10})); + DEADLOCK_VERIFY(!stm.try_lock_shared_for(d)); + VERIFY_IN_NEW_THREAD(!stm.try_lock_shared_for(d)); +} + +int main() +{ + // Try once with an offset that ought to result in tv_sec == 0, tv_nsec < 0 + // and one with an offset that ought to result in tv_sec < 0, tv_nsec == 0 + // for the absolute calls at least. It's not really possible to arrange for + // the relative calls to have tv_nsec == 0 due to time advancing. + using namespace std::chrono_literals; + for (const chrono::nanoseconds offset : { + // tv_sec == 0, tv_nsec == 0 + chrono::nanoseconds{0}, + // tv_sec == 0, tv_nsec < 0 + chrono::nanoseconds{-10ms}, + // tv_sec < 0 + chrono::nanoseconds{-10s} + }) { + test_exclusive_absolute<chrono::system_clock>(offset); + test_shared_absolute<chrono::system_clock>(offset); + test_exclusive_relative<chrono::system_clock>(offset); + test_shared_relative<chrono::system_clock>(offset); + + test_exclusive_absolute<chrono::steady_clock>(offset); + test_shared_absolute<chrono::steady_clock>(offset); + test_exclusive_relative<chrono::steady_clock>(offset); + test_shared_relative<chrono::steady_clock>(offset); + } +} diff --git a/libstdc++-v3/testsuite/30_threads/stop_token/stop_callback/destructible_neg.cc b/libstdc++-v3/testsuite/30_threads/stop_token/stop_callback/destructible_neg.cc index 2c5125b..cdbb2f4 100644 --- a/libstdc++-v3/testsuite/30_threads/stop_token/stop_callback/destructible_neg.cc +++ b/libstdc++-v3/testsuite/30_threads/stop_token/stop_callback/destructible_neg.cc @@ -54,3 +54,4 @@ test02(std::stop_token& tok, G& g) } // { dg-error "static assertion failed" "" { target *-*-* } 0 } +// { dg-error "private within this context" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/30_threads/stop_token/stop_callback/invocable_neg.cc b/libstdc++-v3/testsuite/30_threads/stop_token/stop_callback/invocable_neg.cc index 93b3186..2b2fce4 100644 --- a/libstdc++-v3/testsuite/30_threads/stop_token/stop_callback/invocable_neg.cc +++ b/libstdc++-v3/testsuite/30_threads/stop_token/stop_callback/invocable_neg.cc @@ -32,3 +32,4 @@ test01(std::stop_token& tok, F& f) } // { dg-error "static assertion failed" "" { target *-*-* } 0 } +// { dg-error "no match for call" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/30_threads/this_thread/113327.cc b/libstdc++-v3/testsuite/30_threads/this_thread/113327.cc new file mode 100644 index 0000000..2daa2b0 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/this_thread/113327.cc @@ -0,0 +1,29 @@ +// { dg-do run { target c++11 } } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-require-gthreads "" } + +// PR libstdc++/113327 +// std::sleep_for(std::chrono::hours::max()) returns immediately + +#include <thread> +#include <chrono> +#include <cstdlib> +#include <csignal> + +int main() +{ + std::thread sleepy([] { + // Rather than overflowing to a negative value, the timeout should be + // truncated to seconds::max() and so sleep for 292 billion years. + std::this_thread::sleep_for(std::chrono::minutes::max()); + // This should not happen: + throw 1; + }); + // Give the new thread a chance to start sleeping: + std::this_thread::yield(); + std::this_thread::sleep_for(std::chrono::seconds(2)); + // If we get here without the other thread throwing an exception + // then it should be sleeping peacefully, so the test passed. + // pthread_kill(sleepy.native_handle(), SIGINT); + std::_Exit(0); +} diff --git a/libstdc++-v3/testsuite/30_threads/this_thread/sleep_for.cc b/libstdc++-v3/testsuite/30_threads/this_thread/sleep_for.cc index 3f55ccc..5b0518d 100644 --- a/libstdc++-v3/testsuite/30_threads/this_thread/sleep_for.cc +++ b/libstdc++-v3/testsuite/30_threads/this_thread/sleep_for.cc @@ -37,7 +37,20 @@ test01() VERIFY( (chr::system_clock::now() - begin) >= ms ); } +void +test_negative() +{ + chr::system_clock::time_point begin = chr::system_clock::now(); + + std::this_thread::sleep_for(-chr::hours(8)); + + // That should have completed immediately, but be generous because we don't + // want spurious failures on busy machines. + VERIFY( (chr::system_clock::now() - begin) < chr::seconds(10) ); +} + int main() { test01(); + test_negative(); } diff --git a/libstdc++-v3/testsuite/30_threads/this_thread/sleep_until.cc b/libstdc++-v3/testsuite/30_threads/this_thread/sleep_until.cc index 1fb82b6..8c70c2e 100644 --- a/libstdc++-v3/testsuite/30_threads/this_thread/sleep_until.cc +++ b/libstdc++-v3/testsuite/30_threads/this_thread/sleep_until.cc @@ -26,18 +26,36 @@ namespace chr = std::chrono; +template <typename Clock> void test01() { - chr::system_clock::time_point begin = chr::system_clock::now(); + typename Clock::time_point begin = Clock::now(); chr::microseconds ms(500); - std::this_thread::sleep_until(chr::system_clock::now() + ms); + std::this_thread::sleep_until(Clock::now() + ms); - VERIFY( (chr::system_clock::now() - begin) >= ms ); + VERIFY( (Clock::now() - begin) >= ms ); +} + +template <typename Clock> +void +test_negative() +{ + typename Clock::time_point begin = Clock::now(); + + typename Clock::time_point tp(-chr::hours(8)); + std::this_thread::sleep_until(tp); + + // That should have completed immediately, but be generous because we don't + // want spurious failures on busy machines. + VERIFY( (Clock::now() - begin) < chr::seconds(10) ); } int main() { - test01(); + test01<chr::steady_clock>(); + test01<chr::system_clock>(); + test_negative<chr::steady_clock>(); + test_negative<chr::system_clock>(); } diff --git a/libstdc++-v3/testsuite/30_threads/thread/id/output.cc b/libstdc++-v3/testsuite/30_threads/thread/id/output.cc index 94a6ff0..c3e0d42 100644 --- a/libstdc++-v3/testsuite/30_threads/thread/id/output.cc +++ b/libstdc++-v3/testsuite/30_threads/thread/id/output.cc @@ -81,7 +81,6 @@ void test02() { #if __cpp_lib_formatters >= 202302 - static_assert( std::is_default_constructible_v<std::formatter<std::thread::id, char>> ); std::thread t1([]{}); @@ -118,13 +117,47 @@ test02() VERIFY( ws1.length() == len ); #endif + out.str(""); + out << i; + s1 = out.str(); + len = s1.size(); + out.str(""); + + // with width + s2 = std::format("{0:{1}}", i, len + 2); + VERIFY( s2 == (" " + s1) ); + // with align + width + s2 = std::format("{0:>{1}}", i, len + 2); + VERIFY( s2 == (" " + s1) ); + s2 = std::format("{0:<{1}}", i, len + 2); + VERIFY( s2 == (s1 + " ") ); + // with fill-and-align + width + s2 = std::format("{0:x^{1}}", i, len + 5); + VERIFY( s2 == ("xx" + s1 + "xxx") ); + +#ifdef _GLIBCXX_USE_WCHAR_T + static_assert( std::is_default_constructible_v<std::formatter<std::thread::id, wchar_t>> ); + ws1 = std::format(L"{}", i); + VERIFY( ws1.length() == len ); +#endif + t1.join(); t2.join(); + + static_assert( std::formattable<std::thread::id, char> ); + static_assert( std::formattable<std::thread::id, wchar_t> ); + static_assert( !std::formattable<std::thread::id, char16_t> ); + static_assert( !std::formattable<std::thread::id, int> ); + #elif __cplusplus >= 202302L # error "Feature-test macro for formatters has wrong value in <thread>" #endif } +#if __cplusplus >= 202302L +static_assert(std::enable_nonlocking_formatter_optimization<std::thread::id>); +#endif + int main() { test01(); diff --git a/libstdc++-v3/testsuite/30_threads/thread/swap/1.cc b/libstdc++-v3/testsuite/30_threads/thread/swap/1.cc index 9616b15..b1fde09 100644 --- a/libstdc++-v3/testsuite/30_threads/thread/swap/1.cc +++ b/libstdc++-v3/testsuite/30_threads/thread/swap/1.cc @@ -23,7 +23,7 @@ #include <thread> #include <system_error> -#include <bits/move.h> // std::move +#include <utility> // std::move #include <testsuite_hooks.h> void f() { } diff --git a/libstdc++-v3/testsuite/30_threads/timed_mutex/121496.cc b/libstdc++-v3/testsuite/30_threads/timed_mutex/121496.cc new file mode 100644 index 0000000..d919704 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/timed_mutex/121496.cc @@ -0,0 +1,14 @@ +// { dg-do compile { target { { i?86-*-linux* x86_64-*-linux* } && lp64 } } } +// { dg-require-effective-target c++11 } +// { dg-options "-fsanitize=thread" } + +// PR libstdc++/121496 no member named '_M_clocklock' with -fsanitize=thread + +#include <mutex> +#include <chrono> + +void +test_pr121496(std::timed_mutex& m) +{ + (void) m.try_lock_until(std::chrono::steady_clock::time_point{}); +} diff --git a/libstdc++-v3/testsuite/30_threads/timed_mutex/try_lock_until/116586.cc b/libstdc++-v3/testsuite/30_threads/timed_mutex/try_lock_until/116586.cc new file mode 100644 index 0000000..1566228 --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/timed_mutex/try_lock_until/116586.cc @@ -0,0 +1,60 @@ +// { dg-do run { target c++11 } } +// { dg-additional-options "-pthread" { target pthread } } +// { dg-require-gthreads "" } +// { dg-require-effective-target hosted } + +#include <chrono> +#include <mutex> +#include <initializer_list> +#include <testsuite_hooks.h> + +namespace chrono = std::chrono; + +// thread.timedmutex.requirements.general: +// If abs_time has already passed, the function attempts to obtain +// ownership without blocking (as if by calling try_lock()). + +template <typename Clock> +void +test_absolute(chrono::nanoseconds offset) +{ + std::timed_mutex mtx; + chrono::time_point<Clock> tp(offset); + VERIFY(mtx.try_lock_until(tp)); + VERIFY(!mtx.try_lock_until(tp)); +} + +// The type of clock used for the actual wait depends on whether +// _GLIBCXX_USE_PTHREAD_MUTEX_CLOCKLOCK is defined. We might as well just test +// both steady_clock and system_clock. +template <typename Clock> +void +test_relative(chrono::nanoseconds offset) +{ + std::timed_mutex mtx; + const auto d = -Clock::now().time_since_epoch() + offset; + VERIFY(mtx.try_lock_for(d)); + VERIFY(!mtx.try_lock_for(d)); +} + +int main() +{ + // Try once with an offset that ought to result in tv_sec == 0, tv_nsec < 0 + // and one with an offset that ought to result in tv_sec < 0, tv_nsec == 0 + // for the absolute calls at least. It's not really possible to arrange for + // the relative calls to have tv_nsec == 0 due to time advancing. + for (const chrono::nanoseconds offset : { + // tv_sec == 0, tv_nsec == 0 + chrono::nanoseconds{0}, + // tv_sec == 0, tv_nsec < 0 + chrono::duration_cast<chrono::nanoseconds>(chrono::milliseconds{-10}), + // tv_sec < 0 + chrono::duration_cast<chrono::nanoseconds>(chrono::seconds{-10}) + }) { + test_absolute<chrono::system_clock>(offset); + test_relative<chrono::system_clock>(offset); + + test_absolute<chrono::steady_clock>(offset); + test_relative<chrono::steady_clock>(offset); + } +} diff --git a/libstdc++-v3/testsuite/Makefile.am b/libstdc++-v3/testsuite/Makefile.am index bbdb72e..7754ab6 100644 --- a/libstdc++-v3/testsuite/Makefile.am +++ b/libstdc++-v3/testsuite/Makefile.am @@ -178,7 +178,7 @@ check-compile: testsuite_files ${compile_script} # Runs the testsuite/performance tests. # Some of these tests create large (~75MB) files, allocate huge -# ammounts of memory, or otherwise tie up machine resources. Thus, +# amounts of memory, or otherwise tie up machine resources. Thus, # running this is off by default. # XXX Need to add dependency on libtestc++.a check_performance_script=${glibcxx_srcdir}/scripts/check_performance diff --git a/libstdc++-v3/testsuite/Makefile.in b/libstdc++-v3/testsuite/Makefile.in index 2e42d47..c3693b6 100644 --- a/libstdc++-v3/testsuite/Makefile.in +++ b/libstdc++-v3/testsuite/Makefile.in @@ -91,8 +91,10 @@ target_triplet = @target@ subdir = testsuite ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/../config/acx.m4 \ + $(top_srcdir)/../config/clang-plugin.m4 \ $(top_srcdir)/../config/enable.m4 \ $(top_srcdir)/../config/futex.m4 \ + $(top_srcdir)/../config/gcc-plugin.m4 \ $(top_srcdir)/../config/hwcaps.m4 \ $(top_srcdir)/../config/iconv.m4 \ $(top_srcdir)/../config/lead-dot.m4 \ @@ -222,6 +224,7 @@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ +LLVM_CONFIG = @LLVM_CONFIG@ LN_S = @LN_S@ LONG_DOUBLE_128_FLAGS = @LONG_DOUBLE_128_FLAGS@ LONG_DOUBLE_ALT128_COMPAT_FLAGS = @LONG_DOUBLE_ALT128_COMPAT_FLAGS@ @@ -412,7 +415,7 @@ compile_script = ${glibcxx_srcdir}/scripts/check_compile # Runs the testsuite/performance tests. # Some of these tests create large (~75MB) files, allocate huge -# ammounts of memory, or otherwise tie up machine resources. Thus, +# amounts of memory, or otherwise tie up machine resources. Thus, # running this is off by default. # XXX Need to add dependency on libtestc++.a check_performance_script = ${glibcxx_srcdir}/scripts/check_performance diff --git a/libstdc++-v3/testsuite/backward/hash_set/check_construct_destroy.cc b/libstdc++-v3/testsuite/backward/hash_set/check_construct_destroy.cc index 042de4e..aca296d 100644 --- a/libstdc++-v3/testsuite/backward/hash_set/check_construct_destroy.cc +++ b/libstdc++-v3/testsuite/backward/hash_set/check_construct_destroy.cc @@ -39,50 +39,45 @@ int main() int buckets; - // For C++11 and later add 1 to all counts, because the std::vector used - // internally by the hashtable creates and destroys a temporary object - // using its allocator. - const int extra = __cplusplus >= 201102L ? 1 : 0; - tracker_allocator_counter::reset(); { Container c; buckets = c.bucket_count(); - ok = check_construct_destroy("empty container", buckets+extra, extra) && ok; + ok = check_construct_destroy("empty container", buckets, 0) && ok; } - ok = check_construct_destroy("empty container", buckets+extra, buckets+extra) && ok; + ok = check_construct_destroy("empty container", buckets, buckets) && ok; tracker_allocator_counter::reset(); { Container c(arr10, arr10 + 10); - ok = check_construct_destroy("Construct from range", buckets+10+extra, extra) && ok; + ok = check_construct_destroy("Construct from range", buckets+10, 0) && ok; } - ok = check_construct_destroy("Construct from range", buckets+10+extra, buckets+10+extra) && ok; + ok = check_construct_destroy("Construct from range", buckets+10, buckets+10) && ok; tracker_allocator_counter::reset(); { Container c(arr10, arr10 + 10); c.insert(arr10a[0]); - ok = check_construct_destroy("Insert element", buckets+11+extra, extra) && ok; + ok = check_construct_destroy("Insert element", buckets+11, 0) && ok; } - ok = check_construct_destroy("Insert element", buckets+11+extra, buckets+11+extra) && ok; + ok = check_construct_destroy("Insert element", buckets+11, buckets+11) && ok; tracker_allocator_counter::reset(); { Container c(arr10, arr10 + 10); c.insert(arr10a, arr10a+3); - ok = check_construct_destroy("Insert short range", buckets+13+extra, extra) && ok; + ok = check_construct_destroy("Insert short range", buckets+13, 0) && ok; } - ok = check_construct_destroy("Insert short range", buckets+13+extra, buckets+13+extra) && ok; + ok = check_construct_destroy("Insert short range", buckets+13, buckets+13) && ok; tracker_allocator_counter::reset(); { Container c(arr10, arr10 + 10); c.insert(arr10a, arr10a+10); - ok = check_construct_destroy("Insert long range", buckets+20+extra, extra) && ok; + ok = check_construct_destroy("Insert long range", buckets+20, 0) && ok; } - ok = check_construct_destroy("Insert long range", buckets+20+extra, buckets+20+extra) && ok; + ok = check_construct_destroy("Insert long range", buckets+20, buckets+20) && ok; return ok ? 0 : 1; } diff --git a/libstdc++-v3/testsuite/experimental/filesystem/path/concat/120029.cc b/libstdc++-v3/testsuite/experimental/filesystem/path/concat/120029.cc new file mode 100644 index 0000000..209d968 --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/path/concat/120029.cc @@ -0,0 +1,74 @@ +// { dg-options "-DUSE_FILESYSTEM_TS -lstdc++fs" } +// { dg-do run { target c++11 } } +// { dg-require-filesystem-ts "" } + +// Bug libstdc++/120029 +// Dangling iterator usage in path::operator+=(const path& p) when this == p + +#include <experimental/filesystem> +#include <testsuite_hooks.h> + +namespace fs = std::experimental::filesystem; + +void +test_root_dir() +{ + fs::path p = "/"; + p += p; + p += p; + VERIFY( p == "////" ); + p += p.filename(); + VERIFY( p == "////////" ); + p += *std::prev(p.end()); + VERIFY( p == "////////////////" ); +} + +void +test_root_name() +{ + fs::path p = "C:/"; + p += p; + p += p; + VERIFY( p == "C:/C:/C:/C:/" ); + p += p.filename(); // For Filesystem TS the filename is "." + VERIFY( p == "C:/C:/C:/C:/." ); + p += *std::prev(p.end()); + VERIFY( p == "C:/C:/C:/C:/.." ); +} + +void +test_filename() +{ + fs::path p = "file"; + p += p; + p += p; + VERIFY( p == "filefilefilefile" ); + p += p.filename(); + VERIFY( p == "filefilefilefilefilefilefilefile" ); + p += *std::prev(p.end()); + VERIFY( p == "filefilefilefilefilefilefilefilefilefilefilefilefilefilefilefile" ); +} + +void +test_multi() +{ + fs::path p = "/home/username/Documents/mu"; + p += p; + p += p; + VERIFY( p == "/home/username/Documents/mu/home/username/Documents/mu/home/username/Documents/mu/home/username/Documents/mu" ); + p += p.filename(); + VERIFY( p == "/home/username/Documents/mu/home/username/Documents/mu/home/username/Documents/mu/home/username/Documents/mumu" ); + p += *std::prev(p.end()); + VERIFY( p == "/home/username/Documents/mu/home/username/Documents/mu/home/username/Documents/mu/home/username/Documents/mumumumu" ); + auto n = std::distance(p.begin(), p.end()); + for (int i = 0; i < n; ++i) + p += *std::next(p.begin(), i); +} + +int main() +{ + test_root_dir(); + test_root_name(); + test_filename(); + test_multi(); +} diff --git a/libstdc++-v3/testsuite/experimental/memory/observer_ptr/make_observer.cc b/libstdc++-v3/testsuite/experimental/memory/observer_ptr/make_observer.cc index 048735f..1de9cf0 100644 --- a/libstdc++-v3/testsuite/experimental/memory/observer_ptr/make_observer.cc +++ b/libstdc++-v3/testsuite/experimental/memory/observer_ptr/make_observer.cc @@ -20,12 +20,19 @@ #include <experimental/memory> #include <testsuite_hooks.h> -int main() +constexpr bool test() { const int i = 42; auto o = std::experimental::make_observer(&i); static_assert( std::is_same<decltype(o), - std::experimental::observer_ptr<const int>>(), "" ); + std::experimental::observer_ptr<const int>>(), "" ); VERIFY( o && *o == 42 ); VERIFY( o.get() == &i ); + return true; +} + +int main() +{ + test(); + static_assert( test(), "LWG 4295 - make_observer should be constexpr" ); } diff --git a/libstdc++-v3/testsuite/experimental/memory/observer_ptr/relops/relops.cc b/libstdc++-v3/testsuite/experimental/memory/observer_ptr/relops/relops.cc index 3e23e0b..d03dd5d 100644 --- a/libstdc++-v3/testsuite/experimental/memory/observer_ptr/relops/relops.cc +++ b/libstdc++-v3/testsuite/experimental/memory/observer_ptr/relops/relops.cc @@ -22,13 +22,13 @@ using std::experimental::observer_ptr; -void test01() +constexpr void test01() { observer_ptr<int> a, b; VERIFY(a == b); } -void test02() +constexpr void test02() { int x[2]{}; observer_ptr<int> a{&x[0]}; @@ -40,7 +40,7 @@ void test02() VERIFY(b > a); } -void test03() +constexpr void test03() { int x{}; observer_ptr<int> a{&x}; @@ -48,9 +48,10 @@ void test03() VERIFY(a == b); } -void test04() +int x[2]{}; + +constexpr void test04() { - static constexpr int x[2]{}; constexpr observer_ptr<const int> a{&x[0]}; constexpr observer_ptr<const int> b{&x[1]}; VERIFY(a != b); @@ -60,20 +61,25 @@ void test04() VERIFY(b > a); } -void test05() +constexpr void test05() { - static constexpr int x{}; - constexpr observer_ptr<const int> a{&x}; - constexpr observer_ptr<const int> b{&x}; + constexpr observer_ptr<const int> a{&x[0]}; + constexpr observer_ptr<const int> b{&x[0]}; VERIFY(a == b); } - -int main() +constexpr bool all_tests() { test01(); test02(); test03(); test04(); test05(); + return true; +} + +int main() +{ + all_tests(); + static_assert( all_tests(), "LWG 4295 - relops should be constexpr" ); } diff --git a/libstdc++-v3/testsuite/experimental/memory/observer_ptr/swap/swap.cc b/libstdc++-v3/testsuite/experimental/memory/observer_ptr/swap/swap.cc index 9e76788..84b8844 100644 --- a/libstdc++-v3/testsuite/experimental/memory/observer_ptr/swap/swap.cc +++ b/libstdc++-v3/testsuite/experimental/memory/observer_ptr/swap/swap.cc @@ -25,7 +25,7 @@ using std::experimental::observer_ptr; struct B {}; struct D : B {}; -void test01() +constexpr void test01() { observer_ptr<int> a, b; VERIFY(a == b); @@ -33,7 +33,7 @@ void test01() VERIFY(a == b); } -void test02() +constexpr void test02() { int x{}; observer_ptr<int> a; @@ -45,7 +45,7 @@ void test02() VERIFY(!b); } -void test03() +constexpr void test03() { int x[2]{1,2}; observer_ptr<int> a{&x[0]}; @@ -57,10 +57,16 @@ void test03() VERIFY(*b == 1); } - int main() { - test01(); - test02(); - test03(); + auto tests = [] { + test01(); + test02(); + test03(); + return true; + }; + tests(); +#if __cpp_lib_constexpr_algorithms >= 201806L // >= C++20 + static_assert( tests(), "LWG 4295 - swap should be constexpr" ); +#endif } diff --git a/libstdc++-v3/testsuite/experimental/memory/shared_ptr/cons/move_ctor.cc b/libstdc++-v3/testsuite/experimental/memory/shared_ptr/cons/move_ctor.cc index f8ce58e..e414b1f 100644 --- a/libstdc++-v3/testsuite/experimental/memory/shared_ptr/cons/move_ctor.cc +++ b/libstdc++-v3/testsuite/experimental/memory/shared_ptr/cons/move_ctor.cc @@ -106,6 +106,8 @@ test04() { reset_count_struct __attribute__((unused)) reset; + // The std::move here prevents copy elision, so we construct from a prvalue. + // { dg-prune-output "-Wpessimizing-move" } std::experimental::shared_ptr<A[5]> a(std::move(std::experimental ::shared_ptr<A[5]> (new A[5]))); diff --git a/libstdc++-v3/testsuite/experimental/names.cc b/libstdc++-v3/testsuite/experimental/names.cc index e0a7d4f..94ae76f 100644 --- a/libstdc++-v3/testsuite/experimental/names.cc +++ b/libstdc++-v3/testsuite/experimental/names.cc @@ -22,6 +22,12 @@ // naming variables, parameters etc. in the library. #include "../17_intro/names.cc" + +#ifdef _AIX +// <netdb.h> declares endnetgrent_r with ptr parameter. +# undef ptr +#endif + // Filesystem #if __has_include(<experimental/filesystem>) # include <experimental/filesystem> diff --git a/libstdc++-v3/testsuite/experimental/net/buffer/arithmetic.cc b/libstdc++-v3/testsuite/experimental/net/buffer/arithmetic.cc index 04b3c93..483d01f 100644 --- a/libstdc++-v3/testsuite/experimental/net/buffer/arithmetic.cc +++ b/libstdc++-v3/testsuite/experimental/net/buffer/arithmetic.cc @@ -26,7 +26,6 @@ using std::experimental::net::const_buffer; void test01() { - bool test __attribute__((unused)) = false; char c[4]; mutable_buffer mb; @@ -64,7 +63,6 @@ test01() void test02() { - bool test __attribute__((unused)) = false; char c[4]; const_buffer cb; diff --git a/libstdc++-v3/testsuite/experimental/net/buffer/const.cc b/libstdc++-v3/testsuite/experimental/net/buffer/const.cc index c6adae6..4310c28 100644 --- a/libstdc++-v3/testsuite/experimental/net/buffer/const.cc +++ b/libstdc++-v3/testsuite/experimental/net/buffer/const.cc @@ -50,7 +50,6 @@ test01() void test02() { - bool test __attribute__((unused)) = false; char c[4]; const_buffer b; diff --git a/libstdc++-v3/testsuite/experimental/net/buffer/mutable.cc b/libstdc++-v3/testsuite/experimental/net/buffer/mutable.cc index 9f0d7d4..06a848c 100644 --- a/libstdc++-v3/testsuite/experimental/net/buffer/mutable.cc +++ b/libstdc++-v3/testsuite/experimental/net/buffer/mutable.cc @@ -47,7 +47,6 @@ test01() void test02() { - bool test __attribute__((unused)) = false; char c[4]; mutable_buffer b; diff --git a/libstdc++-v3/testsuite/experimental/net/buffer/size.cc b/libstdc++-v3/testsuite/experimental/net/buffer/size.cc index 4592a19..7d5f339 100644 --- a/libstdc++-v3/testsuite/experimental/net/buffer/size.cc +++ b/libstdc++-v3/testsuite/experimental/net/buffer/size.cc @@ -26,7 +26,6 @@ using std::experimental::net::mutable_buffer; void test01() { - bool test __attribute__((unused)) = false; char c[4]; mutable_buffer mb; @@ -44,7 +43,6 @@ test01() void test02() { - bool test __attribute__((unused)) = false; char c[32]; std::vector<mutable_buffer> mv{ {c, 0}, {c, 32}, {c, 16}, {c, 3}, {c, 0} }; diff --git a/libstdc++-v3/testsuite/experimental/net/timer/waitable/cons.cc b/libstdc++-v3/testsuite/experimental/net/timer/waitable/cons.cc index 6da7528..e394ec8 100644 --- a/libstdc++-v3/testsuite/experimental/net/timer/waitable/cons.cc +++ b/libstdc++-v3/testsuite/experimental/net/timer/waitable/cons.cc @@ -27,8 +27,6 @@ using std::experimental::net::io_context; void test01() { - bool test __attribute__((unused)) = false; - io_context ctx1, ctx2; system_timer timer1(ctx1); @@ -54,8 +52,6 @@ test01() void test02() { - bool test __attribute__((unused)) = false; - io_context ctx1, ctx2; auto t1 = system_timer::clock_type::now(); auto t2 = t1 + system_timer::duration(10); @@ -83,8 +79,6 @@ test02() void test03() { - bool test __attribute__((unused)) = false; - io_context ctx1, ctx2; auto now = system_timer::clock_type::now(); auto d1 = system_timer::duration(10); diff --git a/libstdc++-v3/testsuite/experimental/net/timer/waitable/dest.cc b/libstdc++-v3/testsuite/experimental/net/timer/waitable/dest.cc index d384a67..ef458bd 100644 --- a/libstdc++-v3/testsuite/experimental/net/timer/waitable/dest.cc +++ b/libstdc++-v3/testsuite/experimental/net/timer/waitable/dest.cc @@ -28,8 +28,6 @@ using std::experimental::net::io_context; void test01() { - bool test __attribute__((unused)) = false; - std::error_code ec; io_context ctx; diff --git a/libstdc++-v3/testsuite/experimental/net/timer/waitable/ops.cc b/libstdc++-v3/testsuite/experimental/net/timer/waitable/ops.cc index 82c05c0..ff0b800 100644 --- a/libstdc++-v3/testsuite/experimental/net/timer/waitable/ops.cc +++ b/libstdc++-v3/testsuite/experimental/net/timer/waitable/ops.cc @@ -29,8 +29,6 @@ using std::error_code; void test01() { - bool test __attribute__((unused)) = false; - io_context ctx; error_code ec; bool complete = false; @@ -57,8 +55,6 @@ test01() void test02() { - bool test __attribute__((unused)) = false; - io_context ctx; error_code ec1, ec2; diff --git a/libstdc++-v3/testsuite/experimental/type_traits/value.cc b/libstdc++-v3/testsuite/experimental/type_traits/value.cc index 60207b5..7ad72c6 100644 --- a/libstdc++-v3/testsuite/experimental/type_traits/value.cc +++ b/libstdc++-v3/testsuite/experimental/type_traits/value.cc @@ -1,5 +1,5 @@ // { dg-do compile { target c++14 } } -// { dg-additional-options "-Wno-deprecated" { target { c++2a } } } +// { dg-additional-options "-Wno-deprecated-declarations" { target { c++2a } } } // Copyright (C) 2014-2025 Free Software Foundation, Inc. // diff --git a/libstdc++-v3/testsuite/ext/iotaarray.cc b/libstdc++-v3/testsuite/ext/iotaarray.cc new file mode 100644 index 0000000..b259602 --- /dev/null +++ b/libstdc++-v3/testsuite/ext/iotaarray.cc @@ -0,0 +1,20 @@ +// { dg-do compile { target c++26 } } + +#include <utility> +#include <type_traits> + +template<auto N> +void test() +{ + constexpr auto [id0, ...ids] = std::_IotaArray<N>; + static_assert( std::is_same_v<decltype(id0), const decltype(N)> ); + static_assert( sizeof...(ids) == N - 1 ); + static_assert( (id0 + ... + ids) == N*(N-1)/2 ); +} + +int main() +{ + test<1>(); + test<4u>(); + test<8ull>(); +} diff --git a/libstdc++-v3/testsuite/ext/special_functions/airy_ai/check_value.cc b/libstdc++-v3/testsuite/ext/special_functions/airy_ai/check_value.cc index 7ff005b..6e8e36b 100644 --- a/libstdc++-v3/testsuite/ext/special_functions/airy_ai/check_value.cc +++ b/libstdc++-v3/testsuite/ext/special_functions/airy_ai/check_value.cc @@ -96,7 +96,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_airy_ai<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/ext/special_functions/airy_bi/check_value.cc b/libstdc++-v3/testsuite/ext/special_functions/airy_bi/check_value.cc index 0afefdd..6fcad83 100644 --- a/libstdc++-v3/testsuite/ext/special_functions/airy_bi/check_value.cc +++ b/libstdc++-v3/testsuite/ext/special_functions/airy_bi/check_value.cc @@ -96,7 +96,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_airy_bi<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/ext/special_functions/conf_hyperg/check_value.cc b/libstdc++-v3/testsuite/ext/special_functions/conf_hyperg/check_value.cc index 1227361..a050f62 100644 --- a/libstdc++-v3/testsuite/ext/special_functions/conf_hyperg/check_value.cc +++ b/libstdc++-v3/testsuite/ext/special_functions/conf_hyperg/check_value.cc @@ -3820,7 +3820,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_conf_hyperg<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/ext/special_functions/hyperg/check_value.cc b/libstdc++-v3/testsuite/ext/special_functions/hyperg/check_value.cc index 8e0c57f..b658684 100644 --- a/libstdc++-v3/testsuite/ext/special_functions/hyperg/check_value.cc +++ b/libstdc++-v3/testsuite/ext/special_functions/hyperg/check_value.cc @@ -12293,7 +12293,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_hyperg<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/ext/unicode/properties.cc b/libstdc++-v3/testsuite/ext/unicode/properties.cc index 50b215e..120192d 100644 --- a/libstdc++-v3/testsuite/ext/unicode/properties.cc +++ b/libstdc++-v3/testsuite/ext/unicode/properties.cc @@ -122,7 +122,7 @@ static_assert( uc::__is_extended_pictographic(U'\N{SOUTH WEST ARROW}') ); static_assert( ! uc::__is_extended_pictographic(U'\N{SOUTH WEST ARROW}' + 1) ); static_assert( uc::__is_extended_pictographic(U'\N{POSTBOX}') ); static_assert( ! uc::__is_extended_pictographic(U'\U0001EFFF') ); -static_assert( uc::__is_extended_pictographic(U'\U0001F000') ); +static_assert( uc::__is_extended_pictographic(U'\U0001F004') ); static_assert( uc::__is_extended_pictographic(U'\U0001FFFD') ); static_assert( ! uc::__is_extended_pictographic(U'\U0001FFFE') ); static_assert( ! uc::__is_extended_pictographic(U'\U0001FFFF') ); diff --git a/libstdc++-v3/testsuite/ext/unicode/view.cc b/libstdc++-v3/testsuite/ext/unicode/view.cc index 6f3c099..677a21d 100644 --- a/libstdc++-v3/testsuite/ext/unicode/view.cc +++ b/libstdc++-v3/testsuite/ext/unicode/view.cc @@ -7,13 +7,28 @@ namespace uc = std::__unicode; using namespace std::string_view_literals; +static_assert( std::ranges::view<uc::_Utf8_view<std::string_view>> ); +static_assert( std::ranges::view<uc::_Utf16_view<std::string_view>> ); +static_assert( std::ranges::view<uc::_Utf32_view<std::string_view>> ); + +template<std::ranges::range View> +constexpr void +compare(View v, std::basic_string_view<std::ranges::range_value_t<View>> s) +{ + long size = s.size(); + VERIFY( std::ranges::distance(v) == size ); + VERIFY( std::ranges::equal(v, s) ); + auto rev = std::views::reverse(v); + VERIFY( std::ranges::distance(rev) == size ); + VERIFY( std::ranges::equal(rev, s | std::views::reverse) ); +} + constexpr void test_utf8_to_utf8() { const auto s8 = u8"£🇬🇧 €🇪🇺 æбçδé ♠♥♦♣ 🤡"sv; uc::_Utf8_view v(s8); - VERIFY( std::ranges::distance(v) == s8.size() ); - VERIFY( std::ranges::equal(v, s8) ); + compare(v, s8); } constexpr void @@ -22,8 +37,7 @@ test_utf8_to_utf16() const auto s8 = u8"£🇬🇧 €🇪🇺 æбçδé ♠♥♦♣ 🤡"sv; const std::u16string_view s16 = u"£🇬🇧 €🇪🇺 æбçδé ♠♥♦♣ 🤡"; uc::_Utf16_view v(s8); - VERIFY( std::ranges::distance(v) == s16.size() ); - VERIFY( std::ranges::equal(v, s16) ); + compare(v, s16); } constexpr void @@ -32,36 +46,41 @@ test_utf8_to_utf32() const auto s8 = u8"£🇬🇧 €🇪🇺 æбçδé ♠♥♦♣ 🤡"sv; const auto s32 = U"£🇬🇧 €🇪🇺 æбçδé ♠♥♦♣ 🤡"sv; uc::_Utf32_view v(s8); - VERIFY( std::ranges::distance(v) == s32.size() ); - VERIFY( std::ranges::equal(v, s32) ); + compare(v, s32); } constexpr void test_illformed_utf8() { uc::_Utf32_view v("\xa3 10.99 \xee \xdd"sv); - VERIFY( std::ranges::equal(v, U"\uFFFD 10.99 \uFFFD \uFFFD"sv) ); + compare(v, U"\uFFFD 10.99 \uFFFD \uFFFD"sv); uc::_Utf16_view v2(" \xf8\x80\x80\x80 "sv); - VERIFY( std::ranges::distance(v2) == 6 ); - VERIFY( std::ranges::equal(v2, U" \uFFFD\uFFFD\uFFFD\uFFFD "sv) ); + compare(v2, u" \uFFFD\uFFFD\uFFFD\uFFFD "sv); // Examples of U+FFFD substitution from Unicode standard. uc::_Utf8_view v3("\xc0\xaf\xe0\x80\xbf\xf0\x81\x82\x41"sv); // Table 3-8 - VERIFY( std::ranges::equal(v3, u8"\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\x41"sv) ); + compare(v3, u8"\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\x41"sv); uc::_Utf8_view v4("\xed\xa0\x80\xed\xbf\xbf\xed\xaf\x41"sv); // Table 3-9 - VERIFY( std::ranges::equal(v4, u8"\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\x41"sv) ); + compare(v4, u8"\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\x41"sv); uc::_Utf8_view v5("\xf4\x91\x92\x93\xff\x41\x80\xbf\x42"sv); // Table 3-10 - VERIFY( std::ranges::equal(v5, u8"\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\x41\uFFFD\uFFFD\x42"sv) ); + compare(v5, u8"\uFFFD\uFFFD\uFFFD\uFFFD\uFFFD\x41\uFFFD\uFFFD\x42"sv); uc::_Utf8_view v6("\xe1\x80\xe2\xf0\x91\x92\xf1\xbf\x41"sv); // Table 3-11 - VERIFY( std::ranges::equal(v6, u8"\uFFFD\uFFFD\uFFFD\uFFFD\x41"sv) ); + compare(v6, u8"\uFFFD\uFFFD\uFFFD\uFFFD\x41"sv); uc::_Utf32_view v7("\xe1\x80"sv); - VERIFY( std::ranges::equal(v7, U"\uFFFD"sv) ); + compare(v7, U"\uFFFD"sv); uc::_Utf32_view v8("\xf1\x80"sv); - VERIFY( std::ranges::equal(v8, U"\uFFFD"sv) ); + compare(v8, U"\uFFFD"sv); uc::_Utf32_view v9("\xf1\x80\x80"sv); - VERIFY( std::ranges::equal(v9, U"\uFFFD"sv) ); + compare(v9, U"\uFFFD"sv); + + uc::_Utf32_view v10("\xcf\x80\x80\x81\x82\x83 \x84\x85\x86\x87\x88 "sv); + compare(v10, U"\u03C0\uFFFD\uFFFD\uFFFD\uFFFD \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD "sv); + uc::_Utf16_view v11("\xcf\x80\x80\x81\x82\x83 \x84\x85\x86\x87\x88 "sv); + compare(v11, u"\u03C0\uFFFD\uFFFD\uFFFD\uFFFD \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD "sv); + uc::_Utf8_view v12("\xcf\x80\x80\x81\x82\x83 \x84\x85\x86\x87\x88 "sv); + compare(v12, u8"\u03C0\uFFFD\uFFFD\uFFFD\uFFFD \uFFFD\uFFFD\uFFFD\uFFFD\uFFFD "sv); } constexpr void @@ -69,27 +88,32 @@ test_illformed_utf16() { std::u16string_view s = u"\N{CLOWN FACE}"; std::u16string_view r = u"\uFFFD"; - VERIFY( std::ranges::equal(uc::_Utf16_view(s.substr(0, 1)), r) ); - VERIFY( std::ranges::equal(uc::_Utf16_view(s.substr(1, 1)), r) ); + compare(uc::_Utf16_view(s.substr(0, 1)), r); + compare(uc::_Utf16_view(s.substr(1, 1)), r); std::array s2{ s[0], s[0] }; - VERIFY( std::ranges::equal(uc::_Utf16_view(s2), u"\uFFFD\uFFFD"sv) ); + compare(uc::_Utf16_view(std::span(s2)), u"\uFFFD\uFFFD"sv); std::array s3{ s[0], s[0], s[1] }; - VERIFY( std::ranges::equal(uc::_Utf16_view(s3), u"\uFFFD\N{CLOWN FACE}"sv) ); + compare(uc::_Utf16_view(std::span(s3)), u"\uFFFD\N{CLOWN FACE}"sv); std::array s4{ s[1], s[0] }; - VERIFY( std::ranges::equal(uc::_Utf16_view(s4), u"\uFFFD\uFFFD"sv) ); + compare(uc::_Utf16_view(std::span(s4)), u"\uFFFD\uFFFD"sv); std::array s5{ s[1], s[0], s[1] }; - VERIFY( std::ranges::equal(uc::_Utf16_view(s5), u"\uFFFD\N{CLOWN FACE}"sv) ); + compare(uc::_Utf16_view(std::span(s5)), u"\uFFFD\N{CLOWN FACE}"sv); + + std::array<char16_t, 2> s6{ 0xDC00, 0xDC01 }; + compare(uc::_Utf16_view(std::span(s6)), u"\uFFFD\uFFFD"sv); + std::array<char16_t, 2> s7{ 0xD7FF, 0xDC00 }; + compare(uc::_Utf16_view(std::span(s7)), u"\uD7FF\uFFFD"sv); } constexpr void test_illformed_utf32() { std::u32string_view s = U"\x110000"; - VERIFY( std::ranges::equal(uc::_Utf32_view(s), U"\uFFFD"sv) ); + compare(uc::_Utf32_view(s), U"\uFFFD"sv); s = U"\xFFFFFF"; - VERIFY( std::ranges::equal(uc::_Utf32_view(s), U"\uFFFD"sv) ); + compare(uc::_Utf32_view(s), U"\uFFFD"sv); s = U"\xFFFFFFF0"; - VERIFY( std::ranges::equal(uc::_Utf32_view(s), U"\uFFFD"sv) ); + compare(uc::_Utf32_view(s), U"\uFFFD"sv); } constexpr void @@ -110,6 +134,13 @@ test_past_the_end() iter++; VERIFY( iter == v.end() ); VERIFY( *iter == U'4' ); + std::ranges::advance(iter, -4); + VERIFY( *iter == U'1' ); + // Incrementing before begin has well-defined behaviour. + iter--; + VERIFY( *iter == U'1' ); + iter--; + VERIFY( *iter == U'1' ); std::string_view empty; uc::_Utf32_view v2(empty); @@ -119,6 +150,9 @@ test_past_the_end() iter++; VERIFY( iter2 == v2.end() ); VERIFY( *iter2 == U'\0' ); + iter--; + VERIFY( iter2 == v2.end() ); + VERIFY( *iter2 == U'\0' ); } int main() diff --git a/libstdc++-v3/testsuite/ext/verify_neg.cc b/libstdc++-v3/testsuite/ext/verify_neg.cc new file mode 100644 index 0000000..ce03374 --- /dev/null +++ b/libstdc++-v3/testsuite/ext/verify_neg.cc @@ -0,0 +1,28 @@ +// { dg-do compile { target c++11 } } + +#include <testsuite_hooks.h> + +struct X { explicit operator void*() const { return nullptr; } }; + +void +test_VERIFY(int i) +{ + // This should not be parsed as a function type bool(bool(i)): + VERIFY( bool(i) ); + + // This should not produce warnings about lambda in unevaluated context: + VERIFY( []{ return 1; }() ); + + // Only one expression allowed: + VERIFY(1, 2); // { dg-error "in expansion of macro" } + // { dg-error "compound expression in functional cast" "" { target *-*-* } 0 } + + // A scoped enum is not contextually convertible to bool: + enum class E { E0 }; + VERIFY( E::E0 ); // { dg-error "could not convert" } + + // explicit conversion to void* is not contextually convertible to bool: + X x; + VERIFY( x ); // { dg-error "in expansion of macro" } + // { dg-error "invalid cast .* to type 'bool'" "" { target *-*-* } 0 } +} diff --git a/libstdc++-v3/testsuite/lib/libstdc++.exp b/libstdc++-v3/testsuite/lib/libstdc++.exp index 3c34d18..9f2dd8a 100644 --- a/libstdc++-v3/testsuite/lib/libstdc++.exp +++ b/libstdc++-v3/testsuite/lib/libstdc++.exp @@ -586,6 +586,7 @@ proc v3_target_compile { source dest type options } { global tool lappend options "additional_flags=-fdiagnostics-plain-output" + lappend options "additional_flags=-Wabi=20"; if { [target_info needs_status_wrapper] != "" && [info exists gluefile] } { lappend options "libs=${gluefile}" @@ -648,6 +649,11 @@ proc v3_target_compile { source dest type options } { set v3_additional_files {} set comp_output [target_compile $source $dest $type $options] + + # Strip versioned namespace from the compiler output, + # so that dg-error and dg-warning patterns can ignore it: + regsub -all "std::__8::" $comp_output "std::" comp_output + if { $type == "executable" && $file_to_delete != "" } { file delete $file_to_delete if { [istarget *-*-darwin*] && [file exists $file_to_delete.dSYM] } { @@ -1013,8 +1019,8 @@ proc check_v3_target_time { } { }] } -proc check_v3_target_namedlocale { args } { - set key "et_namedlocale $args" +proc check_v3_target_namedlocale { locale } { + set key "et_namedlocale $locale" return [check_v3_target_prop_cached $key { global tool # Set up, compile, and execute a C++ test program that tries to use @@ -1029,7 +1035,7 @@ proc check_v3_target_namedlocale { args } { puts $f "using namespace std;" puts $f "char *transform_locale(const char *name)" puts $f "{" - puts $f " char *result = new char\[50\];" + puts $f " char *result = new char\[strlen(name)+6\];" puts $f " strcpy(result, name);" puts $f "#if defined __FreeBSD__ || defined __DragonFly__ || defined __NetBSD__" puts $f " /* fall-through */" @@ -1040,14 +1046,9 @@ proc check_v3_target_namedlocale { args } { puts $f "#endif" puts $f " return result;" puts $f "}" - puts $f "int main (int argc, char** argv)" + puts $f "int main ()" puts $f "{" - puts $f " if (argc < 2)" - puts $f " {" - puts $f " printf(\"locale support test not supported\\n\");" - puts $f " return 1;" - puts $f " }" - puts $f " const char *namedloc = transform_locale(*(argv + 1));" + puts $f " const char *namedloc = transform_locale(\"$locale\");" puts $f " try" puts $f " {" puts $f " locale((const char*)namedloc);" @@ -1071,10 +1072,10 @@ proc check_v3_target_namedlocale { args } { return 0 } - set result [${tool}_load "./$exe" "$args" ""] + set result [${tool}_load "./$exe" "" ""] set status [lindex $result 0] - verbose "check_v3_target_namedlocale <$args>: status is <$status>" 2 + verbose "check_v3_target_namedlocale <$locale>: status is <$status>" 2 if { $status == "pass" } { return 1 diff --git a/libstdc++-v3/testsuite/lib/prune.exp b/libstdc++-v3/testsuite/lib/prune.exp index a9a2993..93d3d52 100644 --- a/libstdc++-v3/testsuite/lib/prune.exp +++ b/libstdc++-v3/testsuite/lib/prune.exp @@ -53,12 +53,13 @@ proc libstdc++-dg-prune { system text } { regsub -all "(^|\n)\[^\n\]*: (recursively )?required \[^\n\]*" $text "" text regsub -all "(^|\n)\[^\n\]*: . skipping \[0-9\]* instantiation contexts \[^\n\]*" $text "" text regsub -all "(^|\n)\[^\n\]*: in .constexpr. expansion \[^\n\]*" $text "" text - regsub -all "(^|\n)\[^\n\]*: in requirements .with\[^\n\]*" $text "" text - regsub -all "(^|\n)\[^\n\]*: in requirements with\[^\n\]*" $text "" text + regsub -all "(^|\n)\[^\n\]*: in requirements \[^\n\]*" $text "" text regsub -all "(^|\n) inlined from \[^\n\]*" $text "" text - # Why doesn't GCC need these to strip header context? - regsub -all "(^|\n)In file included from \[^\n\]*" $text "" text - regsub -all "(^|\n)\[ \t\]*from \[^\n\]*" $text "" text + + # Diagnostic inclusion stack + regsub -all "(^|\n)(In file)?\[ \]+included from \[^\n\]*" $text "" text + regsub -all "(^|\n)\[ \]+from \[^\n\]*" $text "" text + regsub -all "(^|\n)(In|of) module( \[^\n \]*,)? imported at \[^\n\]*" $text "" text # Ignore informational notes. #TODO As this isn't even using 'gcc-dg.exp', cannot consider its diff --git a/libstdc++-v3/testsuite/performance/23_containers/sort_search/list.cc b/libstdc++-v3/testsuite/performance/23_containers/sort_search/list.cc index 525d37aa..efb6e6a 100644 --- a/libstdc++-v3/testsuite/performance/23_containers/sort_search/list.cc +++ b/libstdc++-v3/testsuite/performance/23_containers/sort_search/list.cc @@ -34,7 +34,7 @@ template<typename Container, int Iter> //Search for random values that may or may not belong to the list. for (int i = 0; i < 50; ++i) - std::find(obj.begin(), obj.end(), rand() % 100001); + (void) std::find(obj.begin(), obj.end(), rand() % 100001); obj.sort(); diff --git a/libstdc++-v3/testsuite/performance/25_algorithms/equal_deque_iterators.cc b/libstdc++-v3/testsuite/performance/25_algorithms/equal_deque_iterators.cc index 1f97adb..5816667 100644 --- a/libstdc++-v3/testsuite/performance/25_algorithms/equal_deque_iterators.cc +++ b/libstdc++-v3/testsuite/performance/25_algorithms/equal_deque_iterators.cc @@ -34,7 +34,7 @@ int main() start_counters(time, resource); for (int i = 0; i < 1000; ++i) for (int j = 0; j < 3000; ++j) - std::equal(data.begin(), data.begin() + j, d.begin()); + (void) std::equal(data.begin(), data.begin() + j, d.begin()); stop_counters(time, resource); report_performance(__FILE__, "deque vs deque", time, resource); clear_counters(time, resource); @@ -44,7 +44,7 @@ int main() start_counters(time, resource); for (int i = 0; i < 1000; ++i) for (int j = 0; j < 3000; ++j) - std::equal(data.begin(), data.begin() + j, v.begin()); + (void) std::equal(data.begin(), data.begin() + j, v.begin()); stop_counters(time, resource); report_performance(__FILE__, "deque vs vector", time, resource); clear_counters(time, resource); @@ -54,7 +54,7 @@ int main() start_counters(time, resource); for (int i = 0; i < 1000; ++i) for (int j = 0; j < 3000; ++j) - std::equal(v.begin(), v.begin() + j, d.begin()); + (void) std::equal(v.begin(), v.begin() + j, d.begin()); stop_counters(time, resource); report_performance(__FILE__, "vector vs deque", time, resource); clear_counters(time, resource); @@ -64,7 +64,7 @@ int main() start_counters(time, resource); for (int i = 0; i < 1000; ++i) for (int j = 0; j < 3000; ++j) - std::equal(data.begin(), data.begin() + j, cv.begin()); + (void) std::equal(data.begin(), data.begin() + j, cv.begin()); stop_counters(time, resource); report_performance(__FILE__, "int deque vs char vector", time, resource); clear_counters(time, resource); @@ -74,7 +74,7 @@ int main() start_counters(time, resource); for (int i = 0; i < 1000; ++i) for (int j = 0; j < 3000; ++j) - std::equal(cv.begin(), cv.begin() + j, d.begin()); + (void) std::equal(cv.begin(), cv.begin() + j, d.begin()); stop_counters(time, resource); report_performance(__FILE__, "char vector vs int deque", time, resource); diff --git a/libstdc++-v3/testsuite/performance/25_algorithms/search_n.cc b/libstdc++-v3/testsuite/performance/25_algorithms/search_n.cc index 3f3585a..6218c1e 100644 --- a/libstdc++-v3/testsuite/performance/25_algorithms/search_n.cc +++ b/libstdc++-v3/testsuite/performance/25_algorithms/search_n.cc @@ -47,7 +47,7 @@ main(void) __gnu_test::test_container<int, forward_iterator_wrapper> fcon(ary, ary + length); start_counters(time, resource); for(int i = 0; i < 100; i++) - search_n(fcon.begin(), fcon.end(), 10, 1); + (void) search_n(fcon.begin(), fcon.end(), 10, 1); stop_counters(time, resource); report_performance(__FILE__, "forward iterator", time, resource); clear_counters(time, resource); @@ -55,7 +55,7 @@ main(void) __gnu_test::test_container<int, random_access_iterator_wrapper> rcon(ary, ary + length); start_counters(time, resource); for(int i = 0; i < 100; i++) - search_n(rcon.begin(), rcon.end(), 10, 1); + (void) search_n(rcon.begin(), rcon.end(), 10, 1); stop_counters(time, resource); report_performance(__FILE__, "random access iterator", time, resource); clear_counters(time, resource); diff --git a/libstdc++-v3/testsuite/special_functions/01_assoc_laguerre/check_value.cc b/libstdc++-v3/testsuite/special_functions/01_assoc_laguerre/check_value.cc index 4e40f0f..90d7d36 100644 --- a/libstdc++-v3/testsuite/special_functions/01_assoc_laguerre/check_value.cc +++ b/libstdc++-v3/testsuite/special_functions/01_assoc_laguerre/check_value.cc @@ -2217,7 +2217,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_assoc_laguerre<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/special_functions/02_assoc_legendre/check_value.cc b/libstdc++-v3/testsuite/special_functions/02_assoc_legendre/check_value.cc index b54e59e..54ae51d 100644 --- a/libstdc++-v3/testsuite/special_functions/02_assoc_legendre/check_value.cc +++ b/libstdc++-v3/testsuite/special_functions/02_assoc_legendre/check_value.cc @@ -1985,7 +1985,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_assoc_legendre<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/special_functions/02_assoc_legendre/pr86655.cc b/libstdc++-v3/testsuite/special_functions/02_assoc_legendre/pr86655.cc index 9d6507a..cbd7c51 100644 --- a/libstdc++-v3/testsuite/special_functions/02_assoc_legendre/pr86655.cc +++ b/libstdc++-v3/testsuite/special_functions/02_assoc_legendre/pr86655.cc @@ -37,7 +37,6 @@ template<typename _Tp> void test_m_gt_l() { - bool test __attribute__((unused)) = true; for (auto l : {0u, 1u, 2u, 5u}) for (auto m : {l + 1u, l + 2u}) for (auto i : {-2, -1, 0, 1, 2}) diff --git a/libstdc++-v3/testsuite/special_functions/03_beta/check_value.cc b/libstdc++-v3/testsuite/special_functions/03_beta/check_value.cc index 24f5033..e5e85a5 100644 --- a/libstdc++-v3/testsuite/special_functions/03_beta/check_value.cc +++ b/libstdc++-v3/testsuite/special_functions/03_beta/check_value.cc @@ -261,7 +261,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_beta<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/special_functions/04_comp_ellint_1/check_value.cc b/libstdc++-v3/testsuite/special_functions/04_comp_ellint_1/check_value.cc index efa7d5a..c109410 100644 --- a/libstdc++-v3/testsuite/special_functions/04_comp_ellint_1/check_value.cc +++ b/libstdc++-v3/testsuite/special_functions/04_comp_ellint_1/check_value.cc @@ -73,7 +73,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_comp_ellint_1<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/special_functions/05_comp_ellint_2/check_value.cc b/libstdc++-v3/testsuite/special_functions/05_comp_ellint_2/check_value.cc index 8c77daf..6a5d14d 100644 --- a/libstdc++-v3/testsuite/special_functions/05_comp_ellint_2/check_value.cc +++ b/libstdc++-v3/testsuite/special_functions/05_comp_ellint_2/check_value.cc @@ -73,7 +73,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_comp_ellint_2<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/special_functions/06_comp_ellint_3/check_value.cc b/libstdc++-v3/testsuite/special_functions/06_comp_ellint_3/check_value.cc index 8a74415..df544b5 100644 --- a/libstdc++-v3/testsuite/special_functions/06_comp_ellint_3/check_value.cc +++ b/libstdc++-v3/testsuite/special_functions/06_comp_ellint_3/check_value.cc @@ -459,7 +459,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_comp_ellint_3<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/special_functions/07_cyl_bessel_i/check_value.cc b/libstdc++-v3/testsuite/special_functions/07_cyl_bessel_i/check_value.cc index ab182c1..a379bed 100644 --- a/libstdc++-v3/testsuite/special_functions/07_cyl_bessel_i/check_value.cc +++ b/libstdc++-v3/testsuite/special_functions/07_cyl_bessel_i/check_value.cc @@ -702,7 +702,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_cyl_bessel_i<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/special_functions/08_cyl_bessel_j/check_value.cc b/libstdc++-v3/testsuite/special_functions/08_cyl_bessel_j/check_value.cc index a99e1e7..e09ae3d 100644 --- a/libstdc++-v3/testsuite/special_functions/08_cyl_bessel_j/check_value.cc +++ b/libstdc++-v3/testsuite/special_functions/08_cyl_bessel_j/check_value.cc @@ -735,7 +735,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_cyl_bessel_j<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/special_functions/09_cyl_bessel_k/check_value.cc b/libstdc++-v3/testsuite/special_functions/09_cyl_bessel_k/check_value.cc index 5e6804e..ce5abcc 100644 --- a/libstdc++-v3/testsuite/special_functions/09_cyl_bessel_k/check_value.cc +++ b/libstdc++-v3/testsuite/special_functions/09_cyl_bessel_k/check_value.cc @@ -746,7 +746,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_cyl_bessel_k<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/special_functions/10_cyl_neumann/check_value.cc b/libstdc++-v3/testsuite/special_functions/10_cyl_neumann/check_value.cc index 30c8a34..d934377 100644 --- a/libstdc++-v3/testsuite/special_functions/10_cyl_neumann/check_value.cc +++ b/libstdc++-v3/testsuite/special_functions/10_cyl_neumann/check_value.cc @@ -779,7 +779,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_cyl_neumann<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/special_functions/11_ellint_1/check_value.cc b/libstdc++-v3/testsuite/special_functions/11_ellint_1/check_value.cc index c8bad36..2d6a3c5 100644 --- a/libstdc++-v3/testsuite/special_functions/11_ellint_1/check_value.cc +++ b/libstdc++-v3/testsuite/special_functions/11_ellint_1/check_value.cc @@ -459,7 +459,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_ellint_1<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/special_functions/12_ellint_2/check_value.cc b/libstdc++-v3/testsuite/special_functions/12_ellint_2/check_value.cc index fc971e6..0bbd7bb 100644 --- a/libstdc++-v3/testsuite/special_functions/12_ellint_2/check_value.cc +++ b/libstdc++-v3/testsuite/special_functions/12_ellint_2/check_value.cc @@ -459,7 +459,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_ellint_2<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/special_functions/13_ellint_3/check_value.cc b/libstdc++-v3/testsuite/special_functions/13_ellint_3/check_value.cc index f46d8a5..bb1b4c1 100644 --- a/libstdc++-v3/testsuite/special_functions/13_ellint_3/check_value.cc +++ b/libstdc++-v3/testsuite/special_functions/13_ellint_3/check_value.cc @@ -6121,7 +6121,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_ellint_3<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/special_functions/14_expint/check_value.cc b/libstdc++-v3/testsuite/special_functions/14_expint/check_value.cc index 6bf060a..92e9714 100644 --- a/libstdc++-v3/testsuite/special_functions/14_expint/check_value.cc +++ b/libstdc++-v3/testsuite/special_functions/14_expint/check_value.cc @@ -168,7 +168,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_expint<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/special_functions/15_hermite/check_value.cc b/libstdc++-v3/testsuite/special_functions/15_hermite/check_value.cc index a16073a..419fcb0 100644 --- a/libstdc++-v3/testsuite/special_functions/15_hermite/check_value.cc +++ b/libstdc++-v3/testsuite/special_functions/15_hermite/check_value.cc @@ -1904,7 +1904,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_hermite<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/special_functions/16_laguerre/check_value.cc b/libstdc++-v3/testsuite/special_functions/16_laguerre/check_value.cc index 89b9c94..03c6496 100644 --- a/libstdc++-v3/testsuite/special_functions/16_laguerre/check_value.cc +++ b/libstdc++-v3/testsuite/special_functions/16_laguerre/check_value.cc @@ -305,7 +305,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_laguerre<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/special_functions/17_legendre/check_value.cc b/libstdc++-v3/testsuite/special_functions/17_legendre/check_value.cc index b2c0077..7bfb98c 100644 --- a/libstdc++-v3/testsuite/special_functions/17_legendre/check_value.cc +++ b/libstdc++-v3/testsuite/special_functions/17_legendre/check_value.cc @@ -305,7 +305,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_legendre<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/special_functions/18_riemann_zeta/check_value.cc b/libstdc++-v3/testsuite/special_functions/18_riemann_zeta/check_value.cc index 1967bf7..c8589ff 100644 --- a/libstdc++-v3/testsuite/special_functions/18_riemann_zeta/check_value.cc +++ b/libstdc++-v3/testsuite/special_functions/18_riemann_zeta/check_value.cc @@ -273,7 +273,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_riemann_zeta<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/special_functions/19_sph_bessel/check_value.cc b/libstdc++-v3/testsuite/special_functions/19_sph_bessel/check_value.cc index 8116ac4..658a13b 100644 --- a/libstdc++-v3/testsuite/special_functions/19_sph_bessel/check_value.cc +++ b/libstdc++-v3/testsuite/special_functions/19_sph_bessel/check_value.cc @@ -504,7 +504,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_sph_bessel<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/special_functions/20_sph_legendre/check_value.cc b/libstdc++-v3/testsuite/special_functions/20_sph_legendre/check_value.cc index df417e6..5783ca9 100644 --- a/libstdc++-v3/testsuite/special_functions/20_sph_legendre/check_value.cc +++ b/libstdc++-v3/testsuite/special_functions/20_sph_legendre/check_value.cc @@ -1985,7 +1985,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_sph_legendre<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/special_functions/20_sph_legendre/pr86655.cc b/libstdc++-v3/testsuite/special_functions/20_sph_legendre/pr86655.cc index e397371..80809ee 100644 --- a/libstdc++-v3/testsuite/special_functions/20_sph_legendre/pr86655.cc +++ b/libstdc++-v3/testsuite/special_functions/20_sph_legendre/pr86655.cc @@ -37,7 +37,6 @@ template<typename _Tp> void test_m_gt_l() { - bool test __attribute__((unused)) = true; for (auto l : {0u, 1u, 2u, 5u}) for (auto m : {l + 1u, l + 2u}) for (auto i : {-2, -1, 0, 1, 2}) diff --git a/libstdc++-v3/testsuite/special_functions/21_sph_neumann/check_value.cc b/libstdc++-v3/testsuite/special_functions/21_sph_neumann/check_value.cc index 372ca39..c9873b6 100644 --- a/libstdc++-v3/testsuite/special_functions/21_sph_neumann/check_value.cc +++ b/libstdc++-v3/testsuite/special_functions/21_sph_neumann/check_value.cc @@ -554,7 +554,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_sph_neumann<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/std/concepts/concepts.compare/move_only.cc b/libstdc++-v3/testsuite/std/concepts/concepts.compare/move_only.cc new file mode 100644 index 0000000..01870d8 --- /dev/null +++ b/libstdc++-v3/testsuite/std/concepts/concepts.compare/move_only.cc @@ -0,0 +1,28 @@ +// { dg-do compile { target c++20 } } + +#include <concepts> +#include <compare> + +// P2404R3 Move-only types for equality_comparable_with, +// totally_ordered_with, and three_way_comparable_with + +// This was approved for C++23 but we treat it as a DR for C++20. + +#ifndef __cpp_lib_concepts +# error "Feature-test macro __cpp_lib_concepts is missing in <compare>" +#elif __cpp_lib_concepts < 202207L +# error "Feature-test macro __cpp_lib_concepts has wrong value in <compare>" +#endif + +struct MoveOnly +{ + MoveOnly(int); + MoveOnly(MoveOnly&&) = default; + auto operator<=>(const MoveOnly&) const = default; + std::strong_ordering operator<=>(int) const; + bool operator==(const MoveOnly&) const; +}; + +static_assert(std::equality_comparable_with<MoveOnly, int>); +static_assert(std::totally_ordered_with<MoveOnly, int>); +static_assert(std::three_way_comparable_with<MoveOnly, int>); diff --git a/libstdc++-v3/testsuite/std/format/arguments/args.cc b/libstdc++-v3/testsuite/std/format/arguments/args.cc index 4c50bc7..6029675 100644 --- a/libstdc++-v3/testsuite/std/format/arguments/args.cc +++ b/libstdc++-v3/testsuite/std/format/arguments/args.cc @@ -164,24 +164,6 @@ void test_visited_as_handle() #endif } -template<typename E, typename S> -void test_visited_as() -{ - auto v = static_cast<S>(1.0); - auto store = std::make_format_args(v); - std::format_args args = store; - - auto is_expected_val = [v](auto arg) { - if constexpr (std::is_same_v<decltype(arg), E>) - return arg == static_cast<E>(v); - return false; - }; - VERIFY( std::visit_format_arg(is_expected_val, args.get(0)) ); -#if __cpp_lib_format >= 202306L // C++26 adds std::basic_format_arg::visit - VERIFY( args.get(0).visit(is_expected_val) ); -#endif -} - template<typename T> concept can_format = std::is_default_constructible_v<std::formatter<T, char>>; @@ -195,30 +177,31 @@ int main() test_visited_as_handle<__int128>(); test_visited_as_handle<unsigned __int128>(); #endif -// TODO: This should be visited as handle. -#ifdef __STDCPP_FLOAT16_T__ - if constexpr (can_format<_Float16>) - test_visited_as<float, _Float16>(); -#endif -#ifdef __STDCPP_BFLOAT16_T__ +#ifdef __BFLT16_DIG__ if constexpr (can_format<__gnu_cxx::__bfloat16_t>) - test_visited_as<float, __gnu_cxx::__bfloat16_t>(); + test_visited_as_handle<__gnu_cxx::__bfloat16_t>(); +#endif +#ifdef __FLT16_DIG__ + if constexpr (can_format<_Float16>) + test_visited_as_handle<_Float16>(); #endif #ifdef __FLT32_DIG__ if constexpr (can_format<_Float32>) - test_visited_as<float, _Float32>(); + test_visited_as_handle<_Float32>(); #endif #ifdef __FLT64_DIG__ if constexpr (can_format<_Float64>) - test_visited_as<double, _Float64>(); + test_visited_as_handle<_Float64>(); #endif #ifdef __FLT128_DIG__ if constexpr (can_format<_Float128>) -# ifdef _GLIBCXX_LDOUBLE_IS_IEEE_BINARY128 - test_visited_as<long double, _Float128>(); -# else test_visited_as_handle<_Float128>(); -# endif +#endif +#ifdef __SIZEOF_FLOAT128__ + // __ieee128 is same type as __float128, and may be long double + if constexpr (!std::is_same_v<__float128, long double>) + if constexpr (can_format<__float128>) + test_visited_as_handle<__float128>(); #endif #ifdef _GLIBCXX_LONG_DOUBLE_ALT128_COMPAT if constexpr (!std::is_same_v<__ieee128, long double>) diff --git a/libstdc++-v3/testsuite/std/format/arguments/args_neg.cc b/libstdc++-v3/testsuite/std/format/arguments/args_neg.cc index ded56fe..83c7b22 100644 --- a/libstdc++-v3/testsuite/std/format/arguments/args_neg.cc +++ b/libstdc++-v3/testsuite/std/format/arguments/args_neg.cc @@ -42,3 +42,4 @@ void test_const_arg() } // { dg-prune-output "no matching function for call to .*::basic_format_arg<" } +// { dg-prune-output "use of deleted function" } diff --git a/libstdc++-v3/testsuite/std/format/debug.cc b/libstdc++-v3/testsuite/std/format/debug.cc index 71bb7f4..43e930c 100644 --- a/libstdc++-v3/testsuite/std/format/debug.cc +++ b/libstdc++-v3/testsuite/std/format/debug.cc @@ -1,7 +1,9 @@ // { dg-options "-fexec-charset=UTF-8 -fwide-exec-charset=UTF-32LE -DUNICODE_ENC" { target le } } // { dg-options "-fexec-charset=UTF-8 -fwide-exec-charset=UTF-32BE -DUNICODE_ENC" { target be } } // { dg-do run { target c++23 } } +// { dg-require-effective-target 4byte_wchar_t } // { dg-add-options no_pch } +// { dg-timeout-factor 2 } #include <format> #include <testsuite_hooks.h> @@ -24,13 +26,13 @@ fdebug(std::wstring_view t) #define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S) -#define WIDEN(S) WIDEN_(_CharT, S) +#define WIDEN(S) WIDEN_(CharT, S) -template<typename _CharT> +template<typename CharT> void test_basic_escapes() { - std::basic_string<_CharT> res; + std::basic_string<CharT> res; const auto tab = WIDEN("\t"); res = fdebug(tab); @@ -69,11 +71,11 @@ test_basic_escapes() VERIFY( res == WIDEN(R"('\'')") ); } -template<typename _CharT> +template<typename CharT> void test_ascii_escapes() { - std::basic_string<_CharT> res; + std::basic_string<CharT> res; const auto in = WIDEN("\x10 abcde\x7f\t0123"); res = fdebug(in); @@ -86,19 +88,19 @@ test_ascii_escapes() VERIFY( res == WIDEN(R"('a')") ); } -template<typename _CharT> +template<typename CharT> void test_extended_ascii() { - std::basic_string<_CharT> res; + std::basic_string<CharT> res; const auto in = WIDEN("Åëÿ"); res = fdebug(in); VERIFY( res == WIDEN(R"("Åëÿ")") ); - static constexpr bool __test_characters + static constexpr bool __test_characters #if UNICODE_ENC - = sizeof(_CharT) >= 2; + = sizeof(CharT) >= 2; #else // ISO8859-1 = true; #endif // UNICODE_ENC @@ -114,12 +116,12 @@ test_extended_ascii() } } -#if UNICODE_ENC -template<typename _CharT> +template<typename CharT> void test_unicode_escapes() { - std::basic_string<_CharT> res; +#if UNICODE_ENC + std::basic_string<CharT> res; const auto in = WIDEN( "\u008a" // Cc, Control, Line Tabulation Set, @@ -141,7 +143,7 @@ test_unicode_escapes() res = fdebug(in); VERIFY( res == out ); - if constexpr (sizeof(_CharT) >= 2) + if constexpr (sizeof(CharT) >= 2) { res = fdebug(in[0]); VERIFY( res == WIDEN(R"('\u{8a}')") ); @@ -155,53 +157,59 @@ test_unicode_escapes() VERIFY( res == WIDEN(R"('\u{2029}')") ); } - if constexpr (sizeof(_CharT) >= 4) + if constexpr (sizeof(CharT) >= 4) { res = fdebug(in[5]); VERIFY( res == WIDEN("'\U0001f984'") ); } +#endif // UNICODE_ENC } -template<typename _CharT> +template<typename CharT> void test_grapheme_extend() { - std::basic_string<_CharT> res; +#if UNICODE_ENC + std::basic_string<CharT> res; const auto vin = WIDEN("o\u0302\u0323"); res = fdebug(vin); VERIFY( res == WIDEN("\"o\u0302\u0323\"") ); - std::basic_string_view<_CharT> in = WIDEN("\t\u0302\u0323"); + std::basic_string_view<CharT> in = WIDEN("\t\u0302\u0323"); res = fdebug(in); VERIFY( res == WIDEN(R"("\t\u{302}\u{323}")") ); res = fdebug(in.substr(1)); VERIFY( res == WIDEN(R"("\u{302}\u{323}")") ); - if constexpr (sizeof(_CharT) >= 2) + if constexpr (sizeof(CharT) >= 2) { res = fdebug(in[1]); VERIFY( res == WIDEN(R"('\u{302}')") ); } +#endif // UNICODE_ENC } -template<typename _CharT> +template<typename CharT> void test_replacement_char() { - std::basic_string<_CharT> repl = WIDEN("\uFFFD"); - std::basic_string<_CharT> res = fdebug(repl); +#if UNICODE_ENC + std::basic_string<CharT> repl = WIDEN("\uFFFD"); + std::basic_string<CharT> res = fdebug(repl); VERIFY( res == WIDEN("\"\uFFFD\"") ); repl = WIDEN("\uFFFD\uFFFD"); res = fdebug(repl); VERIFY( res == WIDEN("\"\uFFFD\uFFFD\"") ); +#endif // UNICODE_ENC } void test_ill_formed_utf8_seq() { +#if UNICODE_ENC std::string_view seq = "\xf0\x9f\xa6\x84"; // \U0001F984 std::string res; @@ -233,11 +241,13 @@ test_ill_formed_utf8_seq() VERIFY( res == R"('\x{84}')" ); res = fdebug(seq.substr(3, 1)); VERIFY( res == R"("\x{84}")" ); +#endif // UNICODE_ENC } void test_ill_formed_utf32() { +#if UNICODE_ENC std::wstring res; wchar_t ic1 = static_cast<wchar_t>(0xff'ffff); @@ -255,16 +265,16 @@ test_ill_formed_utf32() std::wstring is2(1, ic2); res = fdebug(is2); VERIFY( res == LR"("\x{ffffffff}")" ); -} #endif // UNICODE_ENC +} -template<typename _CharT> +template<typename CharT> void test_fill() { - std::basic_string<_CharT> res; + std::basic_string<CharT> res; - std::basic_string_view<_CharT> in = WIDEN("a\t\x10\u00ad"); + std::basic_string_view<CharT> in = WIDEN("a\t\x10\u00ad"); res = std::format(WIDEN("{:10?}"), in.substr(0, 1)); VERIFY( res == WIDEN(R"("a" )") ); @@ -289,11 +299,11 @@ test_fill() VERIFY( res == WIDEN(R"(="\u{ad}"=)") ); // width is 2 - std::basic_string_view<_CharT> in2 = WIDEN("\u1100"); + std::basic_string_view<CharT> in2 = WIDEN("\u1100"); res = std::format(WIDEN("{:*^10?}"), in2); VERIFY( res == WIDEN("***\"\u1100\"***") ); - if constexpr (sizeof(_CharT) >= 2) + if constexpr (sizeof(CharT) >= 2) { res = std::format(WIDEN("{:=^10?}"), in[3]); VERIFY( res == WIDEN(R"(='\u{ad}'=)") ); @@ -304,14 +314,14 @@ test_fill() #endif // UNICODE_ENC } -template<typename _CharT> +template<typename CharT> void test_prec() { - std::basic_string<_CharT> res; + std::basic_string<CharT> res; // with ? escpaed presentation is copied to ouput, same as source - std::basic_string_view<_CharT> in = WIDEN("a\t\x10\u00ad"); + std::basic_string_view<CharT> in = WIDEN("a\t\x10\u00ad"); res = std::format(WIDEN("{:.2?}"), in.substr(0, 1)); VERIFY( res == WIDEN(R"("a)") ); @@ -325,12 +335,390 @@ test_prec() res = std::format(WIDEN("{:.10?}"), in.substr(3)); VERIFY( res == WIDEN(R"("\u{ad}")") ); - std::basic_string_view<_CharT> in2 = WIDEN("\u1100"); + std::basic_string_view<CharT> in2 = WIDEN("\u1100"); res = std::format(WIDEN("{:.3?}"), in2); VERIFY( res == WIDEN("\"\u1100") ); #endif // UNICODE_ENC } +bool strip_quote(std::string_view& v) +{ + if (!v.starts_with('"')) + return false; + v.remove_prefix(1); + return true; +} + +bool strip_quotes(std::string_view& v) +{ + if (!v.starts_with('"') || !v.ends_with('"')) + return false; + v.remove_prefix(1); + v.remove_suffix(1); + return true; +} + +bool strip_prefix(std::string_view& v, size_t n, char c) +{ + size_t pos = v.find_first_not_of(c); + if (pos == std::string_view::npos) + pos = v.size(); + if (pos != n) + return false; + v.remove_prefix(n); + return true; +} + +void test_padding() +{ + std::string res; + std::string_view resv; + + // width and size are 26 + std::string in = "abcdefghijklmnopqrstuvwxyz"; + in += in; // width and size are 52 + in += in; // width and size are 104 + in += in; // width and size are 208 + in += in; // width and size are 416 + std::string_view inv = in; + + resv = res = std::format("{}", in); + VERIFY( resv == inv ); + + resv = res = std::format("{:.500}", in); + VERIFY( resv == inv ); + + resv = res = std::format("{:.400}", in); + VERIFY( resv == inv.substr(0, 400) ); + + resv = res = std::format("{:.200}", in); + VERIFY( resv == inv.substr(0, 200) ); + + resv = res = std::format("{:.10}", in); + VERIFY( resv == inv.substr(0, 10) ); + + resv = res = std::format("{:.0}", in); + VERIFY( resv == "" ); + + resv = res = std::format("{:*>20}", in); + VERIFY( resv == inv ); + + resv = res = std::format("{:*>20.500}", in); + VERIFY( resv == inv ); + + resv = res = std::format("{:*>20.400}", in); + VERIFY( resv == inv.substr(0, 400) ); + + resv = res = std::format("{:*>20.200}", in); + VERIFY( resv == inv.substr(0, 200) ); + + resv = res = std::format("{:*>20.10}", in); + VERIFY( strip_prefix(resv, 10, '*') ); + VERIFY( resv == inv.substr(0, 10) ); + + resv = res = std::format("{:*>20.0}", in); + VERIFY( strip_prefix(resv, 20, '*') ); + VERIFY( resv == "" ); + + resv = res = std::format("{:*>450}", in); + VERIFY( strip_prefix(resv, 34, '*') ); + VERIFY( resv == inv ); + + resv = res = std::format("{:*>450.500}", in); + VERIFY( strip_prefix(resv, 34, '*') ); + VERIFY( resv == inv ); + + resv = res = std::format("{:*>450.420}", in); + VERIFY( strip_prefix(resv, 34, '*') ); + VERIFY( resv == inv ); + + resv = res = std::format("{:*>450.400}", in); + VERIFY( strip_prefix(resv, 50, '*') ); + VERIFY( resv == inv.substr(0, 400) ); + + resv = res = std::format("{:*>450.200}", in); + VERIFY( strip_prefix(resv, 250, '*') ); + VERIFY( resv == inv.substr(0, 200) ); + + resv = res = std::format("{:*>450.10}", in); + VERIFY( strip_prefix(resv, 440, '*') ); + VERIFY( resv == inv.substr(0, 10) ); + + resv = res = std::format("{:*>450.0}", in); + VERIFY( strip_prefix(resv, 450, '*') ); + VERIFY( resv == "" ); + + resv = res = std::format("{:?}", in); + VERIFY( strip_quotes(resv) ); + VERIFY( resv == inv ); + + resv = res = std::format("{:.500?}", in); + VERIFY( strip_quotes(resv) ); + VERIFY( resv == inv ); + + resv = res = std::format("{:.400?}", in); + VERIFY( strip_quote(resv) ); + VERIFY( resv == inv.substr(0, 399) ); + + resv = res = std::format("{:.200?}", in); + VERIFY( strip_quote(resv) ); + VERIFY( resv == inv.substr(0, 199) ); + + resv = res = std::format("{:.10?}", in); + VERIFY( strip_quote(resv) ); + VERIFY( resv == inv.substr(0, 9) ); + + resv = res = std::format("{:.1?}", in); + VERIFY( strip_quote(resv) ); + VERIFY( resv == "" ); + + resv = res = std::format("{:.0?}", in); + VERIFY( resv == "" ); + + resv = res = std::format("{:*>20?}", in); + VERIFY( strip_quotes(resv) ); + VERIFY( resv == inv ); + + resv = res = std::format("{:*>20.500?}", in); + VERIFY( strip_quotes(resv) ); + VERIFY( resv == inv ); + + resv = res = std::format("{:*>20.400?}", in); + VERIFY( strip_quote(resv) ); + VERIFY( resv == inv.substr(0, 399) ); + + resv = res = std::format("{:*>20.200?}", in); + VERIFY( strip_quote(resv) ); + VERIFY( resv == inv.substr(0, 199) ); + + resv = res = std::format("{:*>20.10?}", in); + VERIFY( strip_prefix(resv, 10, '*') ); + VERIFY( strip_quote(resv) ); + VERIFY( resv == inv.substr(0, 9) ); + + resv = res = std::format("{:*>20.1?}", in); + VERIFY( strip_prefix(resv, 19, '*') ); + VERIFY( strip_quote(resv) ); + VERIFY( resv == "" ); + + resv = res = std::format("{:*>20.0?}", in); + VERIFY( strip_prefix(resv, 20, '*') ); + VERIFY( resv == "" ); + + resv = res = std::format("{:*>450?}", in); + VERIFY( strip_prefix(resv, 32, '*') ); + VERIFY( strip_quotes(resv) ); + VERIFY( resv == inv ); + + resv = res = std::format("{:*>450.500?}", in); + VERIFY( strip_prefix(resv, 32, '*') ); + VERIFY( strip_quotes(resv) ); + VERIFY( resv == inv ); + + resv = res = std::format("{:*>450.420?}", in); + VERIFY( strip_prefix(resv, 32, '*') ); + VERIFY( strip_quotes(resv) ); + VERIFY( resv == inv ); + + resv = res = std::format("{:*>450.400?}", in); + VERIFY( strip_prefix(resv, 50, '*') ); + VERIFY( strip_quote(resv) ); + VERIFY( resv == inv.substr(0, 399) ); + + resv = res = std::format("{:*>450.200?}", in); + VERIFY( strip_prefix(resv, 250, '*') ); + VERIFY( strip_quote(resv) ); + VERIFY( resv == inv.substr(0, 199) ); + + resv = res = std::format("{:*>450.10?}", in); + VERIFY( strip_prefix(resv, 440, '*') ); + VERIFY( strip_quote(resv) ); + VERIFY( resv == inv.substr(0, 9) ); + + resv = res = std::format("{:*>450.1?}", in); + VERIFY( strip_prefix(resv, 449, '*') ); + VERIFY( strip_quote(resv) ); + VERIFY( resv == "" ); + + resv = res = std::format("{:*>450.0?}", in); + VERIFY( strip_prefix(resv, 450, '*') ); + VERIFY( resv == "" ); + +#if UNICODE_ENC + // width is 3, size is 15 + in = "o\u0302\u0323i\u0302\u0323u\u0302\u0323"; + in += in; // width is 6, size is 30 + in += in; // width is 12, size is 60 + in += in; // width is 24, size is 120 + in += in; // width is 48, size is 240 + in += in; // width is 96, size is 480 + in += in; // width is 192, size is 960 + inv = in; + + resv = res = std::format("{:}", in); + VERIFY( resv == inv ); + + resv = res = std::format("{:.200}", in); + VERIFY( resv == inv ); + + resv = res = std::format("{:.96}", in); + VERIFY( resv == inv.substr(0, 480) ); + + resv = res = std::format("{:.12}", in); + VERIFY( resv == inv.substr(0, 60) ); + + resv = res = std::format("{:.3}", in); + VERIFY( resv == inv.substr(0, 15) ); + + resv = res = std::format("{:.0}", in); + VERIFY( resv == "" ); + + resv = res = std::format("{:*>10}", in); + VERIFY( resv == inv ); + + resv = res = std::format("{:*>10.200}", in); + VERIFY( resv == inv ); + + resv = res = std::format("{:*>10.96}", in); + VERIFY( resv == inv.substr(0, 480) ); + + resv = res = std::format("{:*>10.12}", in); + VERIFY( resv == inv.substr(0, 60) ); + + resv = res = std::format("{:*>10.3}", in); + VERIFY( strip_prefix(resv, 7, '*') ); + VERIFY( resv == inv.substr(0, 15) ); + + resv = res = std::format("{:*>10.0}", in); + VERIFY( strip_prefix(resv, 10, '*') ); + VERIFY( resv == "" ); + + resv = res = std::format("{:*>240s}", in); + VERIFY( strip_prefix(resv, 48, '*') ); + VERIFY( resv == inv ); + + resv = res = std::format("{:*>300.200s}", in); + VERIFY( strip_prefix(resv, 108, '*') ); + VERIFY( resv == inv ); + + resv = res = std::format("{:*>240.200s}", in); + VERIFY( strip_prefix(resv, 48, '*') ); + VERIFY( resv == inv ); + + resv = res = std::format("{:*>240.96s}", in); + VERIFY( strip_prefix(resv, 144, '*') ); + VERIFY( resv == inv.substr(0, 480) ); + + resv = res = std::format("{:*>240.12}", in); + VERIFY( strip_prefix(resv, 228, '*') ); + VERIFY( resv == inv.substr(0, 60) ); + + resv = res = std::format("{:*>240.3s}", in); + VERIFY( strip_prefix(resv, 237, '*') ); + VERIFY( resv == inv.substr(0, 15) ); + + resv = res = std::format("{:*>240.0s}", in); + VERIFY( strip_prefix(resv, 240, '*') ); + VERIFY( resv == "" ); + + resv = res = std::format("{:?}", in); + VERIFY( strip_quotes(resv) ); + VERIFY( resv == inv ); + + resv = res = std::format("{:.200?}", in); + VERIFY( strip_quotes(resv) ); + VERIFY( resv == inv ); + + resv = res = std::format("{:.97?}", in); + VERIFY( strip_quote(resv) ); + VERIFY( resv == inv.substr(0, 480) ); + + resv = res = std::format("{:.13?}", in); + VERIFY( strip_quote(resv) ); + VERIFY( resv == inv.substr(0, 60) ); + + resv = res = std::format("{:.4?}", in); + VERIFY( strip_quote(resv) ); + VERIFY( resv == inv.substr(0, 15) ); + + resv = res = std::format("{:.1?}", in); + VERIFY( strip_quote(resv) ); + VERIFY( resv == "" ); + + resv = res = std::format("{:.0?}", in); + VERIFY( resv == "" ); + + resv = res = std::format("{:*>10?}", in); + VERIFY( strip_quotes(resv) ); + VERIFY( resv == inv ); + + resv = res = std::format("{:*>10.200?}", in); + VERIFY( strip_quotes(resv) ); + VERIFY( resv == inv ); + + resv = res = std::format("{:*>10.97?}", in); + VERIFY( strip_quote(resv) ); + VERIFY( resv == inv.substr(0, 480) ); + + resv = res = std::format("{:*>10.13?}", in); + VERIFY( strip_quote(resv) ); + VERIFY( resv == inv.substr(0, 60) ); + + resv = res = std::format("{:*>10.4?}", in); + VERIFY( strip_prefix(resv, 6, '*') ); + VERIFY( strip_quote(resv) ); + VERIFY( resv == inv.substr(0, 15) ); + + resv = res = std::format("{:*>10.1?}", in); + VERIFY( strip_prefix(resv, 9, '*') ); + VERIFY( strip_quote(resv) ); + VERIFY( resv == "" ); + + resv = res = std::format("{:*>10.0?}", in); + VERIFY( strip_prefix(resv, 10, '*') ); + VERIFY( resv == "" ); + + resv = res = std::format("{:*>240?}", in); + VERIFY( strip_prefix(resv, 46, '*') ); + VERIFY( strip_quotes(resv) ); + VERIFY( resv == inv ); + + resv = res = std::format("{:*>300.200?}", in); + VERIFY( strip_prefix(resv, 106, '*') ); + VERIFY( strip_quotes(resv) ); + VERIFY( resv == inv ); + + resv = res = std::format("{:*>240.200?}", in); + VERIFY( strip_prefix(resv, 46, '*') ); + VERIFY( strip_quotes(resv) ); + VERIFY( resv == inv ); + + resv = res = std::format("{:*>240.97?}", in); + VERIFY( strip_prefix(resv, 143, '*') ); + VERIFY( strip_quote(resv) ); + VERIFY( resv == inv.substr(0, 480) ); + + resv = res = std::format("{:*>240.13?}", in); + VERIFY( strip_prefix(resv, 227, '*') ); + VERIFY( strip_quote(resv) ); + VERIFY( resv == inv.substr(0, 60) ); + + resv = res = std::format("{:*>240.4?}", in); + VERIFY( strip_prefix(resv, 236, '*') ); + VERIFY( strip_quote(resv) ); + VERIFY( resv == inv.substr(0, 15) ); + + resv = res = std::format("{:*>240.1?}", in); + VERIFY( strip_prefix(resv, 239, '*') ); + VERIFY( strip_quote(resv) ); + VERIFY( resv == "" ); + + resv = res = std::format("{:*>240.0?}", in); + VERIFY( strip_prefix(resv, 240, '*') ); + VERIFY( resv == "" ); +#endif // UNICODE_ENC +} + void test_char_as_wchar() { std::wstring res; @@ -371,38 +759,38 @@ private: std::formatter<T, CharT> under; }; -template<typename _CharT, typename StrT> +template<typename CharT, typename StrT> void test_formatter_str() { - _CharT buf[]{ 'a', 'b', 'c', 0 }; + CharT buf[]{ 'a', 'b', 'c', 0 }; DebugWrapper<StrT> in{ buf }; - std::basic_string<_CharT> res = std::format(WIDEN("{:?}"), in ); + std::basic_string<CharT> res = std::format(WIDEN("{:?}"), in ); VERIFY( res == WIDEN(R"("abc")") ); } -template<typename _CharT> +template<typename CharT> void test_formatter_arr() { - std::basic_string<_CharT> res; + std::basic_string<CharT> res; - DebugWrapper<_CharT[3]> in3{ 'a', 'b', 'c' }; + DebugWrapper<CharT[3]> in3{ 'a', 'b', 'c' }; res = std::format(WIDEN("{:?}"), in3 ); VERIFY( res == WIDEN(R"("abc")") ); // We print all characters, including null-terminator - DebugWrapper<_CharT[4]> in4{ 'a', 'b', 'c', 0 }; + DebugWrapper<CharT[4]> in4{ 'a', 'b', 'c', 0 }; res = std::format(WIDEN("{:?}"), in4 ); VERIFY( res == WIDEN(R"("abc\u{0}")") ); } -template<typename _CharT, typename SrcT> +template<typename CharT, typename SrcT> void test_formatter_char() { DebugWrapper<SrcT> in{ 'a' }; - std::basic_string<_CharT> res = std::format(WIDEN("{:?}"), in); + std::basic_string<CharT> res = std::format(WIDEN("{:?}"), in); VERIFY( res == WIDEN(R"('a')") ); } @@ -435,7 +823,6 @@ int main() test_extended_ascii<char>(); test_extended_ascii<wchar_t>(); -#if UNICODE_ENC test_unicode_escapes<char>(); test_unicode_escapes<wchar_t>(); test_grapheme_extend<char>(); @@ -444,12 +831,13 @@ int main() test_replacement_char<wchar_t>(); test_ill_formed_utf8_seq(); test_ill_formed_utf32(); -#endif // UNICODE_ENC test_fill<char>(); test_fill<wchar_t>(); test_prec<char>(); test_prec<wchar_t>(); + test_padding(); + test_formatters_c(); } diff --git a/libstdc++-v3/testsuite/std/format/formatter/120625.cc b/libstdc++-v3/testsuite/std/format/formatter/120625.cc new file mode 100644 index 0000000..6b03af9 --- /dev/null +++ b/libstdc++-v3/testsuite/std/format/formatter/120625.cc @@ -0,0 +1,19 @@ +// { dg-do compile { target c++20 } } + +// Bug libstdc++/120625 +// std::formatter<__disabled> specializations cause errors in user code + +#include <format> + +enum X { }; + +// A concept that cannot be used with incomplete types: +template<typename T> +concept is_X = !std::is_empty_v<T> && std::is_same_v<X, T>; + +// A valid program-defined specialization: +template<typename T, typename C> requires is_X<T> +struct std::formatter<T, C> : std::formatter<int, C> { }; + +// Instantiate the program-defined formatter specialization: +auto s = sizeof(std::formatter<X, char>); diff --git a/libstdc++-v3/testsuite/std/format/formatter/lwg3944.cc b/libstdc++-v3/testsuite/std/format/formatter/lwg3944.cc index 1f3edc9..07e63af 100644 --- a/libstdc++-v3/testsuite/std/format/formatter/lwg3944.cc +++ b/libstdc++-v3/testsuite/std/format/formatter/lwg3944.cc @@ -18,7 +18,7 @@ void test_lwg3944() std::format(L"{}", "hello"); // { dg-error "here" } std::format(L"{}", std::string_view("hello")); // { dg-error "here" } std::format(L"{}", std::string("hello")); // { dg-error "here" } -#ifdef __glibcxx_format_ranges +#ifdef __cpp_lib_format_ranges // LWG 3944 does not change this, it's still valid. std::format(L"{}", std::vector{'h', 'e', 'l', 'l', 'o'}); #endif diff --git a/libstdc++-v3/testsuite/std/format/formatter/nonlocking.cc b/libstdc++-v3/testsuite/std/format/formatter/nonlocking.cc new file mode 100644 index 0000000..a726e9d --- /dev/null +++ b/libstdc++-v3/testsuite/std/format/formatter/nonlocking.cc @@ -0,0 +1,59 @@ +// { dg-do compile { target c++23 } } + +#include <format> +#include <string> + +template<typename CharT> +struct MyTraits : std::char_traits<CharT> +{}; + +template<typename CharT> +struct MyAlloc : std::allocator<CharT> +{ + using std::allocator<CharT>::allocator; +}; + +template<typename CharT> +void testCharacters() +{ + static_assert(std::enable_nonlocking_formatter_optimization< + CharT>); + static_assert(std::enable_nonlocking_formatter_optimization< + CharT*>); + static_assert(std::enable_nonlocking_formatter_optimization< + const CharT*>); + static_assert(std::enable_nonlocking_formatter_optimization< + CharT[5]>); + + static_assert(std::enable_nonlocking_formatter_optimization< + std::basic_string<CharT>>); + static_assert(std::enable_nonlocking_formatter_optimization< + std::basic_string<CharT, MyTraits<CharT>>>); + static_assert(std::enable_nonlocking_formatter_optimization< + std::basic_string<CharT, MyTraits<CharT>, MyAlloc<CharT>>>); + + static_assert(std::enable_nonlocking_formatter_optimization< + std::basic_string_view<CharT>>); + static_assert(std::enable_nonlocking_formatter_optimization< + std::basic_string_view<CharT, MyTraits<CharT>>>); +} + +void testAll() +{ + static_assert(std::enable_nonlocking_formatter_optimization< + int>); + static_assert(std::enable_nonlocking_formatter_optimization< + float>); + static_assert(std::enable_nonlocking_formatter_optimization< + void*>); + static_assert(std::enable_nonlocking_formatter_optimization< + const void*>); + static_assert(std::enable_nonlocking_formatter_optimization< + std::nullptr_t>); + + testCharacters<char>(); +#ifdef _GLIBCXX_USE_WCHAR_T + testCharacters<wchar_t>(); +#endif // USE_WCHAR_T +} + diff --git a/libstdc++-v3/testsuite/std/format/functions/format.cc b/libstdc++-v3/testsuite/std/format/functions/format.cc index 93c33b4..d342114 100644 --- a/libstdc++-v3/testsuite/std/format/functions/format.cc +++ b/libstdc++-v3/testsuite/std/format/functions/format.cc @@ -1,6 +1,7 @@ // { dg-options "-fexec-charset=UTF-8" } // { dg-do run { target c++20 } } // { dg-add-options no_pch } +// { dg-additional-options "-DUNICODE" { target 4byte_wchar_t } } #include <format> @@ -260,6 +261,16 @@ test_locale() s = std::format(eloc, "{0:Le} {0:Lf} {0:Lg}", -nan); VERIFY( s == "-nan -nan -nan" ); + // PR libstdc++/120548 format confuses a negative sign for a thousands digit + s = std::format(bloc, "{:L}", -123.45); + VERIFY( s == "-123.45" ); + s = std::format(bloc, "{:-L}", -876543.21); + VERIFY( s == "-876,543.21" ); + s = std::format(bloc, "{:+L}", 333.22); + VERIFY( s == "+333.22" ); + s = std::format(bloc, "{: L}", 999.44); + VERIFY( s == " 999.44" ); + // Restore std::locale::global(cloc); } @@ -511,6 +522,7 @@ test_bool() void test_unicode() { +#ifdef UNICODE // Similar to sC example in test_std_examples, but not from the standard. // Verify that the character "🤡" has estimated field width 2, // rather than estimated field width equal to strlen("🤡"), which would be 4, @@ -564,6 +576,7 @@ test_unicode() std::string sA = std::format("{:>5}", input[0]); VERIFY( sA == input[1] ); } +#endif } int main() diff --git a/libstdc++-v3/testsuite/std/format/parse_ctx.cc b/libstdc++-v3/testsuite/std/format/parse_ctx.cc index b338ac7..6294dcf 100644 --- a/libstdc++-v3/testsuite/std/format/parse_ctx.cc +++ b/libstdc++-v3/testsuite/std/format/parse_ctx.cc @@ -108,7 +108,7 @@ is_std_format_spec_for(std::string_view spec) } } -#if __glibcxx_format_ranges +#if __cpp_lib_format_ranges constexpr bool escaped_strings_supported = true; #else constexpr bool escaped_strings_supported = false; @@ -443,6 +443,8 @@ test_custom() } #if __cpp_lib_format >= 202305 +#include <stdfloat> + struct X { }; template<> @@ -458,13 +460,20 @@ struct std::formatter<X, char> if (spec == "int") { pc.check_dynamic_spec_integral(pc.next_arg_id()); - integer = true; + type = Type::integral; } else if (spec == "str") { pc.check_dynamic_spec_string(pc.next_arg_id()); - integer = false; + type = Type::string; + } + else if (spec == "float") + { + pc.check_dynamic_spec<float, double, long double>(pc.next_arg_id()); + type = Type::floating; } + else if (spec == "other") + type = Type::other; else throw std::format_error("invalid format-spec"); return pc.begin() + spec.size(); @@ -474,13 +483,44 @@ struct std::formatter<X, char> format(X, std::format_context& c) const { std::visit_format_arg([this]<typename T>(T) { // { dg-warning "deprecated" "" { target c++26 } } - if (is_integral_v<T> != this->integer) - throw std::format_error("invalid argument type"); + constexpr bool is_handle + = std::is_same_v<std::basic_format_arg<std::format_context>::handle, T>; + constexpr bool is_integral + = std::is_same_v<int, T> || std::is_same_v<unsigned int, T> + || is_same_v<long long, T> || std::is_same_v<unsigned long long, T>; + constexpr bool is_string + = std::is_same_v<const char*, T> || std::is_same_v<std::string_view, T>; + constexpr bool is_floating + = std::is_same_v<float, T> || std::is_same_v<double, T> + || std::is_same_v<long double, T>; + switch (this->type) + { + case Type::other: + if (is_handle) return; + break; + case Type::integral: + if (is_integral) return; + break; + case Type::string: + if (is_string) return; + break; + case Type::floating: + if (is_floating) return; + break; + } + throw std::format_error("invalid argument type"); }, c.arg(1)); return c.out(); } private: - bool integer = false; + enum class Type + { + other, + integral, + string, + floating, + }; + Type type = Type::other; }; #endif @@ -497,6 +537,28 @@ test_dynamic_type_check() (void) std::format("{:int}", X{}, 42L); (void) std::format("{:str}", X{}, "H2G2"); + (void) std::format("{:float}", X{}, 10.0); + +#ifdef __STDCPP_BFLOAT16_T__ + if constexpr (std::formattable<std::bfloat16_t, char>) + (void) std::format("{:other}", X{}, 10.0bf16); +#endif +#ifdef __STDCPP_FLOAT16_T__ + if constexpr (std::formattable<std::float16_t, char>) + (void) std::format("{:other}", X{}, 10.0f16); +#endif +#ifdef __STDCPP_FLOAT32_T__ + if constexpr (std::formattable<std::float32_t, char>) + (void) std::format("{:other}", X{}, 10.0f32); +#endif +#ifdef __STDCPP_FLOAT64_T__ + if constexpr (std::formattable<std::float64_t, char>) + (void) std::format("{:other}", X{}, 10.0f64); +#endif +#ifdef __STDCPP_FLOAT128_T__ + if constexpr (std::formattable<std::float128_t, char>) + (void) std::format("{:other}", X{}, 10.0f128); +#endif #endif } diff --git a/libstdc++-v3/testsuite/std/format/pr121765.cc b/libstdc++-v3/testsuite/std/format/pr121765.cc new file mode 100644 index 0000000..1358fc1 --- /dev/null +++ b/libstdc++-v3/testsuite/std/format/pr121765.cc @@ -0,0 +1,53 @@ +// { dg-do compile { target c++23 } } + +#include <format> +#include <utility> + +struct MyPair +{ + int x; + int y; +}; + +template<typename CharT> +struct std::formatter<MyPair, CharT> +{ + template<typename ParseContext> + auto parse(ParseContext& pc) + { return _formatter.parse(pc); } + + template<typename FormatContext> + auto format(const MyPair& mp, FormatContext& fc) const + { return _formatter.format(std::make_pair(mp.x, mp.y), fc); } + +private: + std::formatter<std::pair<int, int>, CharT> _formatter; +}; + +static_assert(std::formattable<MyPair, char>); +static_assert(std::formattable<MyPair, wchar_t>); + +struct MyRange +{ + int* begin; + int* end; +}; + +template<typename CharT> +struct std::formatter<MyRange, CharT> +{ + template<typename ParseContext> + auto parse(ParseContext& pc) + { return _formatter.parse(pc); } + + template<typename FormatContext> + auto format(const MyRange& mp, FormatContext& fc) const + { return _formatter.format(std::span<int>(mp.begin, mp.end), fc); } + +private: + std::formatter<std::span<int>, CharT> _formatter; +}; + +static_assert(std::formattable<MyRange, char>); +static_assert(std::formattable<MyRange, wchar_t>); + diff --git a/libstdc++-v3/testsuite/std/format/ranges/adaptors.cc b/libstdc++-v3/testsuite/std/format/ranges/adaptors.cc new file mode 100644 index 0000000..649eea4 --- /dev/null +++ b/libstdc++-v3/testsuite/std/format/ranges/adaptors.cc @@ -0,0 +1,165 @@ +// { dg-do run { target c++23 } } +// { dg-timeout-factor 2 } + +#include <format> +#include <queue> +#include <stack> +#include <testsuite_hooks.h> + +template<typename... Args> +bool +is_format_string_for(const char* str, Args&&... args) +{ + try { + (void) std::vformat(str, std::make_format_args(args...)); + return true; + } catch (const std::format_error&) { + return false; + } +} + +#define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S) +#define WIDEN(S) WIDEN_(CharT, S) + +template<template<typename Tp> class Adaptor> +void +test_format_string() +{ + Adaptor<int> q; + VERIFY( !is_format_string_for("{:?}", q) ); + VERIFY( !is_format_string_for("{:P}", q) ); + + // width needs to be integer type + VERIFY( !is_format_string_for("{:{}}", q, 1.0f) ); +} + +struct NoFormat +{ + friend auto operator<=>(NoFormat, NoFormat) = default; +}; + +struct MutFormat +{ + MutFormat() = default; + MutFormat(int p) : x(p) {} + + int x; + friend auto operator<=>(MutFormat, MutFormat) = default; +}; + +template<typename CharT> +struct std::formatter<MutFormat, CharT> + : std::formatter<int, CharT> +{ + template<typename Out> + Out format(MutFormat& mf, basic_format_context<Out, CharT>& ctx) const + { return std::formatter<int, CharT>::format(mf.x, ctx); } +}; + +template<typename T> +struct NotFormattableCont : std::vector<T> +{ + using std::vector<T>::vector; +}; + +template<typename T> +constexpr auto std::format_kind<NotFormattableCont<T>> + = std::range_format::disabled; + +template<typename CharT, + template<typename Tp, typename Cont = std::vector<Tp>> class Adaptor> +void +test_output() +{ + const std::vector<int> v{3, 2, 1}; + std::basic_string<CharT> res; + Adaptor<int, std::vector<int>> q(std::from_range, v); + + res = std::format(WIDEN("{}"), q); + VERIFY( res == WIDEN("[3, 2, 1]") ); + + res = std::format(WIDEN("{}"), std::as_const(q)); + VERIFY( res == WIDEN("[3, 2, 1]") ); + + res = std::format(WIDEN("{:n:#x}"), q); + VERIFY( res == WIDEN("0x3, 0x2, 0x1") ); + + res = std::format(WIDEN("{:=^23:#04x}"), q); + VERIFY( res == WIDEN("==[0x03, 0x02, 0x01]===") ); + + // Sequence output is always used + Adaptor<CharT, std::basic_string<CharT>> qs( + std::from_range, + std::basic_string_view<CharT>(WIDEN("321"))); + + res = std::format(WIDEN("{}"), qs); + VERIFY( res == WIDEN("['3', '2', '1']") ); + + res = std::format(WIDEN("{::}"), std::as_const(qs)); + VERIFY( res == WIDEN("[3, 2, 1]") ); + + res = std::format(WIDEN("{:?s}"), qs); + VERIFY( res == WIDEN(R"("321")") ); + + Adaptor<int, std::deque<int>> qd(std::from_range, v); + + res = std::format(WIDEN("{}"), qd); + VERIFY( res == WIDEN("[3, 2, 1]") ); + + res = std::format(WIDEN("{}"), std::as_const(qd)); + VERIFY( res == WIDEN("[3, 2, 1]") ); + + Adaptor<MutFormat> mq(std::from_range, v); + + res = std::format(WIDEN("{}"), mq); + VERIFY( res == WIDEN("[3, 2, 1]") ); + + static_assert(!std::formattable<const Adaptor<MutFormat>, CharT>); + + static_assert(!std::formattable<Adaptor<NoFormat>, CharT>); + static_assert(!std::formattable<const Adaptor<NoFormat>, CharT>); + + // Formatter check if container is formattable, not container elements. + static_assert(!std::formattable<Adaptor<int, NotFormattableCont<int>>, CharT>); + + static_assert(!std::enable_nonlocking_formatter_optimization< + Adaptor<int>>); + static_assert(!std::enable_nonlocking_formatter_optimization< + Adaptor<MutFormat>>); + static_assert(!std::enable_nonlocking_formatter_optimization< + Adaptor<int, std::deque<int>>>); + static_assert(!std::enable_nonlocking_formatter_optimization< + Adaptor<int, NotFormattableCont<int>>>); +} + +template<template<typename Tp, typename Cont = std::vector<Tp>> class Adaptor> +void +test_adaptor() +{ + test_format_string<Adaptor>(); + test_output<char, Adaptor>(); + test_output<wchar_t, Adaptor>(); + + static_assert(!std::formattable<Adaptor<int>, int>); + static_assert(!std::formattable<Adaptor<int>, char32_t>); +} + +template<typename CharT> +void +test_compare() +{ + const std::vector<int> v{3, 2, 1}; + std::basic_string<CharT> res; + std::priority_queue<int, std::vector<int>, std::greater<>> q( + std::from_range, v); + + res = std::format(WIDEN("{}"), q); + VERIFY( res == WIDEN("[1, 2, 3]") ); +} + +int main() +{ + test_adaptor<std::queue>(); + test_adaptor<std::priority_queue>(); + test_compare<char>(); +} diff --git a/libstdc++-v3/testsuite/std/format/ranges/feature_test.cc b/libstdc++-v3/testsuite/std/format/ranges/feature_test.cc new file mode 100644 index 0000000..80d2cea --- /dev/null +++ b/libstdc++-v3/testsuite/std/format/ranges/feature_test.cc @@ -0,0 +1,9 @@ +// { dg-do preprocess { target c++23 } } + +#include <format> + +#ifndef __cpp_lib_format_ranges +# error "Feature-test macro __cpp_lib_format_ranges missing in <format>" +#elif __cpp_lib_format_ranges != 202207L +# error "Feature-test macro __cpp_lib_format_ranges has wrong value in <format>" +#endif diff --git a/libstdc++-v3/testsuite/std/format/ranges/format_kind.cc b/libstdc++-v3/testsuite/std/format/ranges/format_kind.cc index 14b9ff2..1450fba 100644 --- a/libstdc++-v3/testsuite/std/format/ranges/format_kind.cc +++ b/libstdc++-v3/testsuite/std/format/ranges/format_kind.cc @@ -75,7 +75,9 @@ void test_override() CustFormat<int, std::range_format::set> setf{1, 2, 3}; VERIFY( std::format("{}", setf) == "{1, 2, 3}" ); - // TODO test map once formatter for pair is implenented + CustFormat<std::pair<int, int>, std::range_format::map> mapf + {{1, 11}, {2, 22}, {3, 33}}; + VERIFY( std::format("{}", mapf) == "{1: 11, 2: 22, 3: 33}" ); CustFormat<char, std::range_format::string> stringf{'a', 'b', 'c', 'd'}; VERIFY( std::format("{}", stringf) == "abcd" ); diff --git a/libstdc++-v3/testsuite/std/format/ranges/format_kind_neg.cc b/libstdc++-v3/testsuite/std/format/ranges/format_kind_neg.cc index bf8619d..0d761ae 100644 --- a/libstdc++-v3/testsuite/std/format/ranges/format_kind_neg.cc +++ b/libstdc++-v3/testsuite/std/format/ranges/format_kind_neg.cc @@ -5,9 +5,14 @@ #include <format> -template<auto> struct Tester { }; +void test() +{ + (void) std::format_kind<void>; // { dg-error "here" } + (void) std::format_kind<const void>; // { dg-error "here" } + (void) std::format_kind<int>; // { dg-error "here" } + (void) std::format_kind<int&>; // { dg-error "here" } + (void) std::format_kind<const int(&)[10]>; // { dg-error "here" } + (void) std::format_kind<void()>; // { dg-error "here" } +} -Tester<std::format_kind<const int(&)[1]>> t; // { dg-error "here" } - -// { dg-error "use of 'std::format_kind" "" { target *-*-* } 0 } -// { dg-error "primary_template_not_defined" "" { target *-*-* } 0 } +// { dg-error "cannot use primary template of 'std::format_kind'" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/std/format/ranges/formatter.cc b/libstdc++-v3/testsuite/std/format/ranges/formatter.cc index 00ce9f6..a50c5b1 100644 --- a/libstdc++-v3/testsuite/std/format/ranges/formatter.cc +++ b/libstdc++-v3/testsuite/std/format/ranges/formatter.cc @@ -4,9 +4,10 @@ #include <format> #include <testsuite_hooks.h> #include <vector> +#include <span> #define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S) -#define WIDEN(S) WIDEN_(_CharT, S) +#define WIDEN(S) WIDEN_(CharT, S) template<typename T, template<typename, typename> class Formatter = std::range_formatter> @@ -22,7 +23,6 @@ struct std::formatter<MyVector<T, Formatter>, CharT> { constexpr formatter() noexcept { - using _CharT = CharT; _formatter.set_brackets(WIDEN("<"), WIDEN(">")); _formatter.set_separator(WIDEN("; ")); } @@ -41,12 +41,12 @@ private: Formatter<T, CharT> _formatter; }; -template<typename _CharT, template<typename, typename> class Formatter> +template<typename CharT, template<typename, typename> class Formatter> void test_default() { MyVector<int, Formatter> vec{1, 2, 3}; - std::basic_string<_CharT> res; + std::basic_string<CharT> res; res = std::format(WIDEN("{}"), vec); VERIFY( res == WIDEN("<1; 2; 3>") ); @@ -93,13 +93,13 @@ test_default() VERIFY( res == WIDEN("< +1 ; +2 ; +3 >") ); } -template<typename _CharT, template<typename, typename> class Formatter> +template<typename CharT, template<typename, typename> class Formatter> void test_override() { - MyVector<_CharT, Formatter> vc{'a', 'b', 'c', 'd'}; + MyVector<CharT, Formatter> vc{'a', 'b', 'c', 'd'}; MyVector<std::pair<int, int>, Formatter> vp{{1, 11}, {2, 21}}; - std::basic_string<_CharT> res; + std::basic_string<CharT> res; res = std::format(WIDEN("{:s}"), vc); VERIFY( res == WIDEN("abcd") ); @@ -146,7 +146,7 @@ struct MyFlatMap : std::flat_map<int, int> template<typename CharT> struct std::formatter<MyFlatMap, CharT> - // This cannot apply format BitVector const&, because formatted type would + // We cannot format MyFlatMap const&, because formatted type would // be std::pair<int const&, int const&>, and formatter for // pair<int const&, int> cannot format it. : std::range_formatter<MyFlatMap::reference> @@ -162,10 +162,21 @@ void test_const_ref_type_mismatch() template<typename T, typename CharT> using VectorFormatter = std::formatter<std::vector<T>, CharT>; +template<template<typename> typename Range> +void test_nonblocking() +{ + static_assert(!std::enable_nonlocking_formatter_optimization< + Range<int>>); +} + int main() { test_outputs<std::range_formatter>(); test_outputs<VectorFormatter>(); test_nested(); test_const_ref_type_mismatch(); + + test_nonblocking<std::span>(); + test_nonblocking<std::vector>(); + test_nonblocking<MyVector>(); } diff --git a/libstdc++-v3/testsuite/std/format/ranges/map.cc b/libstdc++-v3/testsuite/std/format/ranges/map.cc index 34c5ed5..5e1b98f 100644 --- a/libstdc++-v3/testsuite/std/format/ranges/map.cc +++ b/libstdc++-v3/testsuite/std/format/ranges/map.cc @@ -1,4 +1,5 @@ // { dg-do run { target c++23 } } +// { dg-timeout-factor 2 } #include <flat_map> #include <format> @@ -56,7 +57,7 @@ bool is_range_formatter_spec_for(CharT const* spec, Rg&& rg) } #define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S) -#define WIDEN(S) WIDEN_(_CharT, S) +#define WIDEN(S) WIDEN_(CharT, S) void test_format_string() @@ -82,10 +83,10 @@ test_format_string() VERIFY( !is_format_string_for("{:{}m}", std::vector<std::pair<int, int>>(), 1.0f) ); } -template<typename _CharT, typename Range> +template<typename CharT, typename Range> void test_output(bool mapIsDefault) { - using Sv = std::basic_string_view<_CharT>; + using Sv = std::basic_string_view<CharT>; using Pt = std::ranges::range_value_t<Range>; using Ft = std::remove_cvref_t<std::tuple_element_t<0, Pt>>; using St = std::remove_cvref_t<std::tuple_element_t<1, Pt>>; @@ -93,7 +94,7 @@ void test_output(bool mapIsDefault) return Range(s.data(), s.data() + s.size()); }; - std::basic_string<_CharT> res; + std::basic_string<CharT> res; size_t size = 0; Ft f1[]{1, 2, 3}; diff --git a/libstdc++-v3/testsuite/std/format/ranges/pr119861_neg.cc b/libstdc++-v3/testsuite/std/format/ranges/pr119861_neg.cc new file mode 100644 index 0000000..9a6ed16 --- /dev/null +++ b/libstdc++-v3/testsuite/std/format/ranges/pr119861_neg.cc @@ -0,0 +1,52 @@ +// { dg-do compile { target c++23 } } + +#include <format> +#include <vector> + +// only format_kind::sequence provides set_brackets and set_separator methods + +template<std::range_format fk, typename T> +struct MyCont : std::vector<T> +{ + using std::vector<T>::vector; +}; + +template<std::range_format fk, typename T> +constexpr std::range_format std::format_kind<MyCont<fk, T>> = fk; + +void test_sequence() +{ + std::formatter<MyCont<std::range_format::sequence, int>, char> fmtter; + fmtter.set_brackets("{", "}"); + fmtter.set_separator(","); +} + +void test_map() +{ + std::formatter<MyCont<std::range_format::map, std::pair<int, int>>, char> fmtter; + fmtter.set_brackets("{", "}"); // { dg-error "here" } + fmtter.set_separator(","); // { dg-error "here" } +} + +void test_set() +{ + std::formatter<MyCont<std::range_format::set, int>, char> fmtter; + fmtter.set_brackets("{", "}"); // { dg-error "here" } + fmtter.set_separator(","); // { dg-error "here" } +} + +void test_string() +{ + std::formatter<MyCont<std::range_format::string, char>, char> fmtter; + fmtter.set_brackets("{", "}"); // { dg-error "here" } + fmtter.set_separator(","); // { dg-error "here" } +} + +void test_debug_string() +{ + std::formatter<MyCont<std::range_format::debug_string, char>, char> fmtter; + fmtter.set_brackets("{", "}"); // { dg-error "here" } + fmtter.set_separator(","); // { dg-error "here" } +} + +// { dg-error "no matching function for call to 'std::formatter<" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/std/format/ranges/sequence.cc b/libstdc++-v3/testsuite/std/format/ranges/sequence.cc index 61fc68e..7fb65f9 100644 --- a/libstdc++-v3/testsuite/std/format/ranges/sequence.cc +++ b/libstdc++-v3/testsuite/std/format/ranges/sequence.cc @@ -1,10 +1,13 @@ // { dg-do run { target c++23 } } +// { dg-options "-fexec-charset=UTF-8" } +// { dg-timeout-factor 2 } #include <array> #include <format> #include <list> #include <ranges> #include <span> +#include <string> #include <testsuite_hooks.h> #include <testsuite_iterators.h> #include <vector> @@ -73,12 +76,12 @@ test_format_string() } #define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S) -#define WIDEN(S) WIDEN_(_CharT, S) +#define WIDEN(S) WIDEN_(CharT, S) -template<typename _CharT, typename Range, typename Storage> +template<typename CharT, typename Range, typename Storage> void test_output() { - using Sv = std::basic_string_view<_CharT>; + using Sv = std::basic_string_view<CharT>; using T = std::ranges::range_value_t<Range>; auto makeRange = [](Storage& s) -> Range { if constexpr (std::is_same_v<std::remove_cvref_t<Range>, Storage>) @@ -88,7 +91,7 @@ void test_output() std::ranges::data(s) + std::ranges::size(s)); }; - std::basic_string<_CharT> res; + std::basic_string<CharT> res; size_t size = 0; Storage v1{1, 2, 3}; @@ -198,9 +201,132 @@ test_nested() VERIFY( res == "+[01, 02, 11, 12]+" ); } +bool strip_quote(std::string_view& v) +{ + if (!v.starts_with('"')) + return false; + v.remove_prefix(1); + return true; +} + +bool strip_prefix(std::string_view& v, std::string_view expected, bool quoted = false) +{ + if (quoted && !strip_quote(v)) + return false; + if (!v.starts_with(expected)) + return false; + v.remove_prefix(expected.size()); + if (quoted && !strip_quote(v)) + return false; + return true; +} + +bool strip_squares(std::string_view& v) +{ + if (!v.starts_with('[') || !v.ends_with(']')) + return false; + v.remove_prefix(1); + v.remove_suffix(1); + return true; +} + +bool strip_prefix(std::string_view& v, size_t n, char c) +{ + size_t pos = v.find_first_not_of(c); + if (pos == std::string_view::npos) + pos = v.size(); + if (pos != n) + return false; + v.remove_prefix(n); + return true; +} + +void test_padding() +{ + std::string res; + std::string_view resv; + + // width is 3, size is 15 + std::string in = "o\u0302\u0323i\u0302\u0323u\u0302\u0323"; + in += in; // width is 6, size is 30 + in += in; // width is 12, size is 60 + in += in; // width is 24, size is 120 + in += in; // width is 48, size is 240 + // width is 192, size is 960 + std::vector<std::string> const vs{in, in, in, in}; + + auto const check_elems = [=](std::string_view& v, bool quoted) + { + VERIFY( strip_prefix(v, in, quoted) ); + VERIFY( strip_prefix(v, ", ", false) ); + VERIFY( strip_prefix(v, in, quoted) ); + VERIFY( strip_prefix(v, ", ", false) ); + VERIFY( strip_prefix(v, in, quoted) ); + VERIFY( strip_prefix(v, ", ", false) ); + VERIFY( strip_prefix(v, in, quoted) ); + return v.empty(); + }; + + resv = res = std::format("{}", vs); + VERIFY( strip_squares(resv) ); + VERIFY( check_elems(resv, true) ); + + resv = res = std::format("{:n}", vs); + VERIFY( check_elems(resv, true) ); + + resv = res = std::format("{::}", vs); + VERIFY( strip_squares(resv) ); + VERIFY( check_elems(resv, false) ); + + resv = res = std::format("{:n:}", vs); + VERIFY( check_elems(resv, false) ); + + resv = res = std::format("{:*>10}", vs); + VERIFY( strip_squares(resv) ); + VERIFY( check_elems(resv, true) ); + + resv = res = std::format("{:*>10n}", vs); + VERIFY( check_elems(resv, true) ); + + resv = res = std::format("{:*>10:}", vs); + VERIFY( strip_squares(resv) ); + VERIFY( check_elems(resv, false) ); + + resv = res = std::format("{:*>10n:}", vs); + VERIFY( check_elems(resv, false) ); + + resv = res = std::format("{:*>256}", vs); + VERIFY( strip_prefix(resv, 48, '*') ); + VERIFY( strip_squares(resv) ); + VERIFY( check_elems(resv, true) ); + + resv = res = std::format("{:*>256n}", vs); + VERIFY( strip_prefix(resv, 50, '*') ); + VERIFY( check_elems(resv, true) ); + + resv = res = std::format("{:*>240}", vs); + VERIFY( strip_prefix(resv, 32, '*') ); + VERIFY( strip_squares(resv) ); + VERIFY( check_elems(resv, true) ); + + resv = res = std::format("{:*>240n}", vs); + VERIFY( strip_prefix(resv, 34, '*') ); + VERIFY( check_elems(resv, true) ); + + resv = res = std::format("{:*>240:}", vs); + VERIFY( strip_prefix(resv, 40, '*') ); + VERIFY( strip_squares(resv) ); + VERIFY( check_elems(resv, false) ); + + resv = res = std::format("{:*>240n:}", vs); + VERIFY( strip_prefix(resv, 42, '*') ); + VERIFY( check_elems(resv, false) ); +} + int main() { test_format_string(); test_outputs(); test_nested(); + test_padding(); } diff --git a/libstdc++-v3/testsuite/std/format/ranges/string.cc b/libstdc++-v3/testsuite/std/format/ranges/string.cc index 7f59f59..bef2cc7 100644 --- a/libstdc++-v3/testsuite/std/format/ranges/string.cc +++ b/libstdc++-v3/testsuite/std/format/ranges/string.cc @@ -1,6 +1,9 @@ // { dg-do run { target c++23 } } +// { dg-options "-fexec-charset=UTF-8" } +// { dg-timeout-factor 2 } #include <format> +#include <forward_list> #include <span> #include <testsuite_hooks.h> #include <testsuite_iterators.h> @@ -45,7 +48,7 @@ bool is_range_formatter_spec_for(CharT const* spec, Rg&& rg) } #define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S) -#define WIDEN(S) WIDEN_(_CharT, S) +#define WIDEN(S) WIDEN_(CharT, S) void test_format_string() @@ -78,14 +81,14 @@ test_format_string() template<typename Range> void test_output() { - using _CharT = std::ranges::range_value_t<Range>; - auto makeRange = [](std::basic_string<_CharT>& s) { + using CharT = std::ranges::range_value_t<Range>; + auto makeRange = [](std::basic_string<CharT>& s) { return Range(s.data(), s.data() + s.size()); }; - std::basic_string<_CharT> res; + std::basic_string<CharT> res; size_t size = 0; - std::basic_string<_CharT> s1 = WIDEN("abcd"); + std::basic_string<CharT> s1 = WIDEN("abcd"); res = std::format(WIDEN("{}"), makeRange(s1)); VERIFY( res == WIDEN("['a', 'b', 'c', 'd']") ); @@ -119,7 +122,7 @@ void test_output() res = std::format(WIDEN("{:=^8s}"), makeRange(s1)); VERIFY( res == WIDEN("==abcd==") ); - std::basic_string<_CharT> s2(512, static_cast<_CharT>('a')); + std::basic_string<CharT> s2(512, static_cast<CharT>('a')); res = std::format(WIDEN("{:=^8s}"), makeRange(s2)); VERIFY( res == s2 ); @@ -217,10 +220,160 @@ test_nested() VERIFY( std::format("{::?s}", vv) == R"(["str1", "str2"])" ); } +bool strip_quotes(std::string_view& v) +{ + if (!v.starts_with('"') || !v.ends_with('"')) + return false; + v.remove_prefix(1); + v.remove_suffix(1); + return true; +} + +bool strip_prefix(std::string_view& v, size_t n, char c) +{ + size_t pos = v.find_first_not_of(c); + if (pos == std::string_view::npos) + pos = v.size(); + if (pos != n) + return false; + v.remove_prefix(n); + return true; +} + + +void test_padding() +{ + std::string res; + std::string_view resv; + + // width is 3, size is 15 + std::string in = "o\u0302\u0323i\u0302\u0323u\u0302\u0323"; + in += in; // width is 6, size is 30 + in += in; // width is 12, size is 60 + in += in; // width is 24, size is 120 + in += in; // width is 48, size is 240 + in += in; // width is 96, size is 480 + in += in; // width is 192, size is 960 + + std::forward_list<char> lc(std::from_range, in); + + resv = res = std::format("{:s}", lc); + VERIFY( resv == in ); + + resv = res = std::format("{:*>10s}", lc); + VERIFY( resv == in ); + + resv = res = std::format("{:*>240s}", lc); + VERIFY( strip_prefix(resv, 48, '*') ); + VERIFY( resv == in ); + + resv = res = std::format("{:?s}", lc); + VERIFY( strip_quotes(resv) ); + VERIFY( resv == in ); + + resv = res = std::format("{:*>10?s}", lc); + VERIFY( strip_quotes(resv) ); + VERIFY( resv == in ); + + resv = res = std::format("{:*>240?s}", lc); + VERIFY( strip_prefix(resv, 46, '*') ); + VERIFY( strip_quotes(resv) ); + VERIFY( resv == in ); + + // width is 5, size is 15 + in = "\u2160\u2161\u2162\u2163\u2164"; + in += in; // width is 10, size is 30 + in += in; // width is 20, size is 60 + in += in; // width is 40, size is 120 + in += in; // width is 80, size is 240 + in += in; // width is 160, size is 480 + + lc.assign_range(in); + + resv = res = std::format("{:s}", lc); + VERIFY( resv == in ); + + resv = res = std::format("{:*>10s}", lc); + VERIFY( resv == in ); + + resv = res = std::format("{:*>200s}", lc); + VERIFY( strip_prefix(resv, 40, '*') ); + VERIFY( resv == in ); + + resv = res = std::format("{:?s}", lc); + VERIFY( strip_quotes(resv) ); + VERIFY( resv == in ); + + resv = res = std::format("{:*>10?s}", lc); + VERIFY( strip_quotes(resv) ); + VERIFY( resv == in ); + + resv = res = std::format("{:*>200?s}", lc); + VERIFY( strip_prefix(resv, 38, '*') ); + VERIFY( strip_quotes(resv) ); + VERIFY( resv == in ); +} + +void test_escaping() +{ + std::string res; + std::string_view resv; + + const std::string_view input = + "\t\n\r\\\"" + "\u008a" // Cc, Control, Line Tabulation Set, + "\u00ad" // Cf, Format, Soft Hyphen + "\u1d3d" // Lm, Modifier letter, Modifier Letter Capital Ou + "\u00a0" // Zs, Space Separator, No-Break Space (NBSP) + "\u2029" // Zp, Paragraph Separator, Paragraph Separator + "\U0001f984" // So, Other Symbol, Unicorn Face + ; + const std::string_view output = + R"(\t\n\r\\\")" + R"(\u{8a})" + R"(\u{ad})" + "\u1d3d" + R"(\u{a0})" + R"(\u{2029})" + "\U0001f984"; + + std::forward_list<char> lc(std::from_range, input); + resv = res = std::format("{:s}", lc); + VERIFY( resv == input ); + resv = res = std::format("{:?s}", lc); + VERIFY( strip_quotes(resv) ); + VERIFY( resv == output ); + + // width is 5, size is 15 + std::string in = "\u2160\u2161\u2162\u2163\u2164"; + in += in; // width is 10, size is 30 + in += in; // width is 20, size is 60 + in += in; // width is 40, size is 120 + in += in; // width is 80, size is 240 + in += in; // width is 160, size is 480 + std::string_view inv = in; + + // last charcter is incomplete + lc.assign_range(inv.substr(0, 479)); + + // non-debug format, chars copied as is + resv = res = std::format("{:s}", lc); + VERIFY( resv == inv.substr(0, 479) ); + + // debug-format, incomplete code-point sequence is esaped + resv = res = std::format("{:?s}", lc); + VERIFY( strip_quotes(resv) ); + VERIFY( resv.substr(0, 477) == inv.substr(0, 477) ); + resv.remove_prefix(477); + VERIFY( resv == R"(\x{e2}\x{85})" ); +} + int main() { test_format_string(); test_outputs<char>(); test_outputs<wchar_t>(); test_nested(); + test_padding(); + test_escaping(); } diff --git a/libstdc++-v3/testsuite/std/format/string.cc b/libstdc++-v3/testsuite/std/format/string.cc index 76614d4..ee987a1 100644 --- a/libstdc++-v3/testsuite/std/format/string.cc +++ b/libstdc++-v3/testsuite/std/format/string.cc @@ -62,7 +62,7 @@ test_indexing() VERIFY( ! is_format_string_for("{} {0}", 1) ); } -#if __glibcxx_format_ranges +#if __cpp_lib_format_ranges constexpr bool escaped_strings_supported = true; #else constexpr bool escaped_strings_supported = false; diff --git a/libstdc++-v3/testsuite/std/format/string_neg.cc b/libstdc++-v3/testsuite/std/format/string_neg.cc index 09cc9a2..acae88e 100644 --- a/libstdc++-v3/testsuite/std/format/string_neg.cc +++ b/libstdc++-v3/testsuite/std/format/string_neg.cc @@ -8,3 +8,5 @@ auto s = std::format(" {9} "); // { dg-error "call to consteval function" } struct X { }; std::format_string<X> str(""); // { dg-error "here" } // { dg-error "std::formatter must be specialized" "" { target *-*-* } 0 } + +// { dg-prune-output "use of deleted function" } diff --git a/libstdc++-v3/testsuite/std/format/tuple.cc b/libstdc++-v3/testsuite/std/format/tuple.cc index 62f9d29..eace827 100644 --- a/libstdc++-v3/testsuite/std/format/tuple.cc +++ b/libstdc++-v3/testsuite/std/format/tuple.cc @@ -1,4 +1,6 @@ // { dg-do run { target c++23 } } +// { dg-options "-fexec-charset=UTF-8" } +// { dg-timeout-factor 2 } #include <format> #include <string> @@ -37,7 +39,7 @@ is_format_string_for(const wchar_t* str, Args&&... args) } #define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S) -#define WIDEN(S) WIDEN_(_CharT, S) +#define WIDEN(S) WIDEN_(CharT, S) void test_format_string() @@ -60,13 +62,13 @@ test_format_string() VERIFY( !is_format_string_for("{:{}}", std::tuple<>(), 1.0f) ); } -template<typename _CharT> +template<typename CharT> void test_multi() { - using Sv = std::basic_string_view<_CharT>; - using Str = std::basic_string<_CharT>; + using Sv = std::basic_string_view<CharT>; + using Str = std::basic_string<CharT>; - std::basic_string<_CharT> res; + std::basic_string<CharT> res; std::size_t size = 0; std::tuple<int, Str, float> t1(1, WIDEN("test"), 2.1); @@ -120,10 +122,10 @@ void test_multi() } -template<typename _CharT, typename Tuple> +template<typename CharT, typename Tuple> void test_empty() { - std::basic_string<_CharT> res; + std::basic_string<CharT> res; Tuple e1; res = std::format(WIDEN("{}"), e1); @@ -139,13 +141,13 @@ void test_empty() VERIFY( res == WIDEN(R"(^^^^())") ); } -template<typename _CharT, typename Pair> +template<typename CharT, typename Pair> void test_pair() { using Ft = std::remove_cvref_t<std::tuple_element_t<0, Pair>>; using St = std::remove_cvref_t<std::tuple_element_t<1, Pair>>; - std::basic_string<_CharT> res; + std::basic_string<CharT> res; Ft f1 = 1; St s1 = WIDEN("abc"); @@ -185,7 +187,6 @@ struct std::formatter<MyPair<Pair>, CharT> { constexpr formatter() noexcept { - using _CharT = CharT; _formatter.set_brackets(WIDEN("<"), WIDEN(">")); _formatter.set_separator(WIDEN("; ")); } @@ -204,11 +205,11 @@ private: std::formatter<Pair, CharT> _formatter; }; -template<typename _CharT, template<typename, typename> class PairT> +template<typename CharT, template<typename, typename> class PairT> void test_custom() { - std::basic_string<_CharT> res; - MyPair<PairT<int, const _CharT*>> c1(1, WIDEN("abc")); + std::basic_string<CharT> res; + MyPair<PairT<int, const CharT*>> c1(1, WIDEN("abc")); res = std::format(WIDEN("{}"), c1); VERIFY( res == WIDEN(R"(<1; "abc">)") ); @@ -250,10 +251,137 @@ void test_nested() VERIFY( res == R"((): (1, "abc"))" ); } +bool strip_quote(std::string_view& v) +{ + if (!v.starts_with('"')) + return false; + v.remove_prefix(1); + return true; +} + +bool strip_prefix(std::string_view& v, std::string_view expected, bool quoted = false) +{ + if (quoted && !strip_quote(v)) + return false; + if (!v.starts_with(expected)) + return false; + v.remove_prefix(expected.size()); + if (quoted && !strip_quote(v)) + return false; + return true; +} + +bool strip_parens(std::string_view& v) +{ + if (!v.starts_with('(') || !v.ends_with(')')) + return false; + v.remove_prefix(1); + v.remove_suffix(1); + return true; +} + +bool strip_prefix(std::string_view& v, size_t n, char c) +{ + size_t pos = v.find_first_not_of(c); + if (pos == std::string_view::npos) + pos = v.size(); + if (pos != n) + return false; + v.remove_prefix(n); + return true; +} + +void test_padding() +{ + std::string res; + std::string_view resv; + + // width is 3, size is 15 + std::string in = "o\u0302\u0323i\u0302\u0323u\u0302\u0323"; + in += in; // width is 6, size is 30 + in += in; // width is 12, size is 60 + in += in; // width is 24, size is 120 + in += in; // width is 48, size is 240 + // width is 192, size is 960 + auto const ts = std::make_tuple(in, in, in, in); + + auto const check_elems = [=](std::string_view& v) + { + VERIFY( strip_prefix(v, in, true) ); + VERIFY( strip_prefix(v, ", ", false) ); + VERIFY( strip_prefix(v, in, true) ); + VERIFY( strip_prefix(v, ", ", false) ); + VERIFY( strip_prefix(v, in, true) ); + VERIFY( strip_prefix(v, ", ", false) ); + VERIFY( strip_prefix(v, in, true) ); + return v.empty(); + }; + + resv = res = std::format("{}", ts); + VERIFY( strip_parens(resv) ); + VERIFY( check_elems(resv) ); + + resv = res = std::format("{:n}", ts); + VERIFY( check_elems(resv) ); + + resv = res = std::format("{:*>10}", ts); + VERIFY( strip_parens(resv) ); + VERIFY( check_elems(resv) ); + + resv = res = std::format("{:*>10n}", ts); + VERIFY( check_elems(resv) ); + + resv = res = std::format("{:*>240}", ts); + VERIFY( strip_prefix(resv, 32, '*') ); + VERIFY( strip_parens(resv) ); + VERIFY( check_elems(resv) ); + + resv = res = std::format("{:*>240n}", ts); + VERIFY( strip_prefix(resv, 34, '*') ); + VERIFY( check_elems(resv) ); +} + +struct Custom {}; + +template<typename CharT> +struct std::formatter<Custom, CharT> +{ + constexpr std::basic_format_parse_context<CharT>::iterator + parse(const std::basic_format_parse_context<CharT>& pc) + { return pc.begin(); } + + template<typename Out> + typename std::basic_format_context<Out, CharT>::iterator + format(Custom, const std::basic_format_context<Out, CharT>& fc) const + { return fc.out(); } +}; + +template<template<typename...> typename Tuple> +void test_nonblocking() +{ + static_assert(std::enable_nonlocking_formatter_optimization< + Tuple<int, float>>); + static_assert(std::enable_nonlocking_formatter_optimization< + Tuple<const int, const float>>); + static_assert(std::enable_nonlocking_formatter_optimization< + Tuple<int&, float&>>); + + static_assert(!std::enable_nonlocking_formatter_optimization< + Tuple<Custom, float>>); + static_assert(!std::enable_nonlocking_formatter_optimization< + Tuple<const Custom, const float>>); + static_assert(!std::enable_nonlocking_formatter_optimization< + Tuple<Custom&, float&>>); +} + int main() { test_format_string(); test_outputs<char>(); test_outputs<wchar_t>(); test_nested(); + test_padding(); + + test_nonblocking<std::pair>(); + test_nonblocking<std::tuple>(); } diff --git a/libstdc++-v3/testsuite/std/memory/indirect/access.cc b/libstdc++-v3/testsuite/std/memory/indirect/access.cc new file mode 100644 index 0000000..cf21275 --- /dev/null +++ b/libstdc++-v3/testsuite/std/memory/indirect/access.cc @@ -0,0 +1,58 @@ +// { dg-do run { target c++26 } } + +#include <memory> +#include <vector> + +#include <testsuite_hooks.h> + +template<template<typename> class Indirect> +constexpr void +test_access() +{ + const std::vector<int> src{1, 2, 3, 4, 5}; + Indirect<std::vector<int>> i(src); + auto const& ci = i; + VERIFY( *i == src ); + VERIFY( *ci == src ); + VERIFY( *std::move(ci) == src ); + + std::vector<int>&& vr = *std::move(i); + VERIFY( vr == src ); + VERIFY( *i == src ); + + std::vector<int> vc = *std::move(i); + VERIFY( vc == src ); + VERIFY( vr.empty() ); + VERIFY( i->empty() ); + VERIFY( ci->empty() ); +} + +template<typename T> +struct PublicBase : std::indirect<T> +{ + using std::indirect<T>::indirect; +}; + +template<typename T> +class PrivateBase : std::indirect<T> +{ +public: + using std::indirect<T>::indirect; + using std::indirect<T>::operator*; + using std::indirect<T>::operator->; +}; + +constexpr bool +test_all() +{ + test_access<std::indirect>(); + test_access<PublicBase>(); + test_access<PrivateBase>(); + return true; +} + +int main() +{ + test_all(); + static_assert(test_all()); +} diff --git a/libstdc++-v3/testsuite/std/memory/indirect/copy.cc b/libstdc++-v3/testsuite/std/memory/indirect/copy.cc new file mode 100644 index 0000000..5ecfbbd --- /dev/null +++ b/libstdc++-v3/testsuite/std/memory/indirect/copy.cc @@ -0,0 +1,156 @@ +// { dg-do run { target c++26 } } + +#include <memory> +#include <scoped_allocator> +#include <utility> +#include <vector> + +#include <testsuite_hooks.h> +#include <testsuite_allocator.h> + +using __gnu_test::tracker_allocator; +using Counter = __gnu_test::tracker_allocator_counter; +using Vector = std::vector<int>; +using Indirect = std::indirect<Vector, tracker_allocator<Vector>>; +const Indirect src(std::in_place, {1, 2, 3}); + +void +test_ctor() +{ + Counter::reset(); + Indirect i1(src); + VERIFY( *i1 == *src ); + VERIFY( &*i1 != &*src ); + VERIFY( Counter::get_allocation_count() == sizeof(Vector) ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 1 ); + VERIFY( Counter::get_destruct_count() == 0 ); + + Counter::reset(); + Indirect i2(std::allocator_arg, {}, src); + VERIFY( *i2 == *src ); + VERIFY( &*i2 != &*src ); + VERIFY( Counter::get_allocation_count() == sizeof(Vector) ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 1 ); + VERIFY( Counter::get_destruct_count() == 0 ); +} + +void +test_assign() +{ + Indirect i1; + Counter::reset(); + + i1 = src; + VERIFY( *i1 == *src ); + VERIFY( &*i1 != &*src ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 0 ); + + auto(std::move(i1)); + Counter::reset(); + + i1 = src; + VERIFY( *i1 == *src ); + VERIFY( &*i1 != &*src ); + VERIFY( Counter::get_allocation_count() == sizeof(Vector) ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 1 ); + VERIFY( Counter::get_destruct_count() == 0 ); +} + +void +test_valueless() +{ + Indirect e; + auto(std::move(e)); + VERIFY( e.valueless_after_move() ); + + Counter::reset(); + Indirect i1(e); + VERIFY( i1.valueless_after_move() ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 0 ); + + Indirect i2(std::allocator_arg, {}, e); + VERIFY( i2.valueless_after_move() ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 0 ); + + Indirect i3(src); + Counter::reset(); + i3 = e; + VERIFY( i3.valueless_after_move() ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == sizeof(Vector) ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 1 ); + + Counter::reset(); + i3 = e; + VERIFY( i3.valueless_after_move() ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 0 ); +} + +constexpr void +test_constexpr() +{ + using Alloc = __gnu_test::uneq_allocator<Vector>; + using Indirect = std::indirect<Vector, Alloc>; + const Indirect src(std::in_place, {1, 2, 3}); + + Indirect i1(src); + VERIFY( *i1 == *src ); + VERIFY( &*i1 != &*src ); + VERIFY( i1.get_allocator() == Alloc{} ); + + Indirect i2(std::allocator_arg, Alloc{2}, src); + VERIFY( *i2 == *src ); + VERIFY( &*i2 != &*src ); + VERIFY( i2.get_allocator() == Alloc{2} ); + + Indirect i3(std::allocator_arg, Alloc{3}); + i3 = src; + VERIFY( *i3 == *src ); + VERIFY( &*i3 != &*src ); + VERIFY( i3.get_allocator() == Alloc{3} ); + + Indirect e; + auto(std::move(e)); + VERIFY( e.valueless_after_move() ); + + Indirect e1(e); + VERIFY( e1.valueless_after_move() ); + + Indirect e2(std::allocator_arg, {}, e); + VERIFY( e2.valueless_after_move() ); + + i3 = e; + VERIFY( i3.valueless_after_move() ); + + i3 = e; + VERIFY( i3.valueless_after_move() ); +} + +int main() +{ + test_ctor(); + test_assign(); + test_valueless(); + test_constexpr(); + + static_assert([] { + test_constexpr(); + return true; + }()); +} diff --git a/libstdc++-v3/testsuite/std/memory/indirect/copy_alloc.cc b/libstdc++-v3/testsuite/std/memory/indirect/copy_alloc.cc new file mode 100644 index 0000000..e48855a --- /dev/null +++ b/libstdc++-v3/testsuite/std/memory/indirect/copy_alloc.cc @@ -0,0 +1,222 @@ +// { dg-do run { target c++26 } } + +#include <memory> +#include <scoped_allocator> +#include <utility> +#include <vector> + +#include <testsuite_hooks.h> +#include <testsuite_allocator.h> + +using __gnu_test::propagating_allocator; +using __gnu_test::tracker_allocator; +using Counter = __gnu_test::tracker_allocator_counter; + +template<bool Propagate> +void +test_ctor() +{ + using PropAlloc = propagating_allocator<int, Propagate>; + using Vector = std::vector<int, PropAlloc>; + using ScopedAlloc = std::scoped_allocator_adaptor< + propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>, + PropAlloc>; + using Indirect = std::indirect<Vector, ScopedAlloc>; + + const Indirect src(std::allocator_arg, ScopedAlloc{11, 22}, + std::in_place, {1, 2, 3}); + + Counter::reset(); + Indirect i1(src); + VERIFY( *i1 == *src ); + VERIFY( &*i1 != &*src ); + if (Propagate) + { + VERIFY( i1->get_allocator().get_personality() == 22 ); + VERIFY( i1.get_allocator().get_personality() == 11 ); + } + else + { + VERIFY( i1->get_allocator().get_personality() == 0 ); + VERIFY( i1.get_allocator().get_personality() == 0 ); + } + VERIFY( Counter::get_allocation_count() == sizeof(Vector) ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 1 ); + VERIFY( Counter::get_destruct_count() == 0 ); + + + Counter::reset(); + Indirect i2(std::allocator_arg, ScopedAlloc{33, 44}, src); + VERIFY( *i2 == *src ); + VERIFY( &*i2 != &*src ); + VERIFY( i2->get_allocator().get_personality() == 44 ); + VERIFY( i2.get_allocator().get_personality() == 33 ); + VERIFY( Counter::get_allocation_count() == sizeof(Vector) ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 1 ); + VERIFY( Counter::get_destruct_count() == 0 ); +} + +template<bool Propagate> +void +test_assign() +{ + using PropAlloc = propagating_allocator<int, Propagate>; + using Vector = std::vector<int, PropAlloc>; + using ScopedAlloc = std::scoped_allocator_adaptor< + propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>, + PropAlloc>; + using Indirect = std::indirect<Vector, ScopedAlloc>; + + const Indirect src(std::allocator_arg, ScopedAlloc{11, 22}, + std::in_place, {1, 2, 3}); + + Indirect i1(std::allocator_arg, ScopedAlloc{11, 22}); + Counter::reset(); + + i1 = src; + VERIFY( *i1 == *src ); + VERIFY( &*i1 != &*src ); + VERIFY( i1->get_allocator().get_personality() == 22 ); + VERIFY( i1.get_allocator().get_personality() == 11 ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 0 ); + + Indirect i2(std::allocator_arg, ScopedAlloc{33, 44}); + Counter::reset(); + + i2 = src; + VERIFY( *i2 == *src ); + VERIFY( &*i2 != &*src ); + if (Propagate) + { + VERIFY( i2->get_allocator().get_personality() == 22 ); + VERIFY( i2.get_allocator().get_personality() == 11 ); + } + else + { + VERIFY( i2->get_allocator().get_personality() == 44 ); + VERIFY( i2.get_allocator().get_personality() == 33 ); + } + VERIFY( Counter::get_allocation_count() == sizeof(Vector) ); + VERIFY( Counter::get_deallocation_count() == sizeof(Vector) ); + VERIFY( Counter::get_construct_count() == 1 ); + VERIFY( Counter::get_destruct_count() == 1 ); + + Indirect i3(std::allocator_arg, ScopedAlloc{11, 22}); + auto(std::move(i3)); + Counter::reset(); + + i3 = src; + VERIFY( *i3 == *src ); + VERIFY( &*i3 != &*src ); + VERIFY( i3->get_allocator().get_personality() == 22 ); + VERIFY( i3.get_allocator().get_personality() == 11 ); + VERIFY( Counter::get_allocation_count() == sizeof(Vector) ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 1 ); + VERIFY( Counter::get_destruct_count() == 0 ); + + Indirect i4(std::allocator_arg, ScopedAlloc{33, 44}); + auto(std::move(i4)); + Counter::reset(); + + i4 = src; + VERIFY( *i4 == *src ); + VERIFY( &*i4 != &*src ); + if (Propagate) + { + VERIFY( i4->get_allocator().get_personality() == 22 ); + VERIFY( i4.get_allocator().get_personality() == 11 ); + } + else + { + VERIFY( i4->get_allocator().get_personality() == 44 ); + VERIFY( i4.get_allocator().get_personality() == 33 ); + } + VERIFY( Counter::get_allocation_count() == sizeof(Vector) ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 1 ); + VERIFY( Counter::get_destruct_count() == 0 ); +} + +template<bool Propagate> +void +test_valueless() +{ + using PropAlloc = propagating_allocator<int, Propagate>; + using Vector = std::vector<int, PropAlloc>; + using ScopedAlloc = std::scoped_allocator_adaptor< + propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>, + PropAlloc>; + using Indirect = std::indirect<Vector, ScopedAlloc>; + + Indirect e(std::allocator_arg, ScopedAlloc{11, 22}); + auto(std::move(e)); + VERIFY( e.valueless_after_move() ); + + Counter::reset(); + Indirect i1(e); + VERIFY( i1.valueless_after_move() ); + if (Propagate) + VERIFY( i1.get_allocator().get_personality() == 11 ); + else + VERIFY( i1.get_allocator().get_personality() == 0 ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 0 ); + + Counter::reset(); + Indirect i2(std::allocator_arg, ScopedAlloc{33, 44}, e); + VERIFY( i2.valueless_after_move() ); + VERIFY( i2.get_allocator().get_personality() == 33 ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 0 ); + + Indirect i3(std::allocator_arg, ScopedAlloc{33, 44}); + Counter::reset(); + + i3 = e; + VERIFY( i3.valueless_after_move() ); + if (Propagate) + VERIFY( i3.get_allocator().get_personality() == 11 ); + else + VERIFY( i3.get_allocator().get_personality() == 33 ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == sizeof(Vector) ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 1 ); + + Counter::reset(); + i2 = e; + VERIFY( i2.valueless_after_move() ); + if (Propagate) + VERIFY( i2.get_allocator().get_personality() == 11 ); + else + VERIFY( i2.get_allocator().get_personality() == 33 ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 0 ); +} + +template<bool Propagate> +constexpr void +test_all() +{ + test_ctor<Propagate>(); + test_assign<Propagate>(); + test_valueless<Propagate>(); +} + +int main() +{ + test_all<true>(); + test_all<false>(); +} diff --git a/libstdc++-v3/testsuite/std/memory/indirect/ctor.cc b/libstdc++-v3/testsuite/std/memory/indirect/ctor.cc new file mode 100644 index 0000000..dfd9341 --- /dev/null +++ b/libstdc++-v3/testsuite/std/memory/indirect/ctor.cc @@ -0,0 +1,213 @@ +// { dg-do run { target c++26 } } + +#include <memory> +#include <scoped_allocator> +#include <utility> +#include <vector> + +#ifndef __cpp_lib_indirect +# error __cpp_lib_indirect feature test macro missing in <memory> +#elif __cpp_lib_indirect != 202502 +# error __cpp_lib_indirect feature test macro has wrong value in <memory> +#endif + +#include <testsuite_hooks.h> +#include <testsuite_allocator.h> + +using __gnu_test::uneq_allocator; +using UneqAlloc = uneq_allocator<int>; +using ScopedAlloc = std::scoped_allocator_adaptor< + uneq_allocator<std::vector<int, UneqAlloc>>, + UneqAlloc>; + +struct Obj +{ + int i; + char c[2]; +}; + +constexpr void +test_deduction_guides() +{ + const Obj o{}; + std::indirect i1(o); + static_assert(std::is_same_v<decltype(i1), std::indirect<Obj>>); + + using Alloc = __gnu_test::SimpleAllocator<Obj>; + Alloc a; + std::indirect i2(std::allocator_arg, a, o); + static_assert(std::is_same_v<decltype(i2), std::indirect<Obj, Alloc>>); +} + +constexpr void +test_default_ctor() +{ + using __gnu_test::default_init_allocator; + + std::indirect<Obj, default_init_allocator<Obj>> i1; + default_init_allocator<int> a{}; + + // The contained object and the allocator should be value-initialized. + VERIFY( i1->i == 0 ); + VERIFY( i1->c[0] == 0 ); + VERIFY( i1->c[1] == 0 ); + VERIFY( i1.get_allocator() == a ); + + a.state = 5; + // Allocator-extended default constructor: + std::indirect<Obj, default_init_allocator<Obj>> i2(std::allocator_arg, a); + VERIFY( i2.get_allocator() == a ); + + if (std::is_constant_evaluated()) + return; + + // Object is constructed using allocator-aware constructor. + std::indirect<std::vector<int, UneqAlloc>, ScopedAlloc> + i3(std::allocator_arg, ScopedAlloc(11, 22)); + VERIFY( i3->empty() ); + VERIFY( i3->get_allocator().get_personality() == 22 ); + VERIFY( i3.get_allocator().get_personality() == 11 ); +} + +constexpr void +test_forwarding_ctor() +{ + Obj obj{1, {'2', '3'}}; + auto verify = [](std::indirect<Obj> const& i) + { + VERIFY( i->i == 1 ); + VERIFY( i->c[0] == '2' ); + VERIFY( i->c[1] == '3' ); + }; + + std::indirect<Obj> i1(std::as_const(obj)); + verify(i1); + std::indirect<Obj> i2(std::move(std::as_const(obj))); + verify(i2); + std::indirect<Obj> i3(obj); + verify(i3); + std::indirect<Obj> i4(std::move(obj)); + verify(i4); + + std::indirect<Obj> i5({1, {'2', '3'}}); + verify(i5); + + // Aggregate parens init + std::indirect<Obj> i6(7); + VERIFY( i6->i == 7 ); + + if (std::is_constant_evaluated()) + return; + + std::vector<int, UneqAlloc> v{1, 2, 3, 4, 5}; + // Object is constructed using allocator-aware constructor. + std::indirect<std::vector<int, UneqAlloc>, ScopedAlloc> + i7(std::allocator_arg, ScopedAlloc(11, 22), v); + VERIFY( i7->size() == 5 ); + VERIFY( v.size() == 5 ); + VERIFY( i7->get_allocator().get_personality() == 22 ); + VERIFY( i7.get_allocator().get_personality() == 11 ); + + std::indirect<std::vector<int, UneqAlloc>, ScopedAlloc> + i8(std::allocator_arg, ScopedAlloc(11, 22), std::move(v)); + VERIFY( i8->size() == 5 ); + VERIFY( v.size() == 0 ); + VERIFY( i8->get_allocator().get_personality() == 22 ); + VERIFY( i8.get_allocator().get_personality() == 11 ); +} + +constexpr void +test_inplace_ctor() +{ + std::indirect<Obj> i1(std::in_place); + VERIFY( i1->i == 0 ); + VERIFY( i1->c[0] == 0 ); + VERIFY( i1->c[1] == 0 ); + + std::indirect<Obj> i2(std::in_place, 10); + VERIFY( i2->i == 10 ); + VERIFY( i2->c[0] == 0 ); + VERIFY( i2->c[1] == 0 ); + + std::indirect<Obj, uneq_allocator<Obj>> + i3(std::allocator_arg, 42, std::in_place); + VERIFY( i3->i == 0 ); + VERIFY( i3->c[0] == 0 ); + VERIFY( i3->c[1] == 0 ); + VERIFY( i3.get_allocator().get_personality() == 42 ); + + std::indirect<Obj, uneq_allocator<Obj>> + i4(std::allocator_arg, 42, std::in_place, 10); + VERIFY( i4->i == 10 ); + VERIFY( i4->c[0] == 0 ); + VERIFY( i4->c[1] == 0 ); + VERIFY( i4.get_allocator().get_personality() == 42 ); + + std::indirect<std::vector<int>> i5(std::in_place); + VERIFY( i5->size() == 0 ); + + std::indirect<std::vector<int>> i6(std::in_place, 5, 13); + VERIFY( i6->size() == 5 ); + VERIFY( i6->at(0) == 13 ); + + std::indirect<std::vector<int>> i7(std::in_place, {1, 2, 3, 4}); + VERIFY( i7->size() == 4 ); + VERIFY( i7->at(2) == 3 ); + + std::indirect<std::vector<int, UneqAlloc>> + i8(std::in_place, UneqAlloc{42}); + VERIFY( i8->size() == 0 ); + VERIFY( i8->get_allocator().get_personality() == 42 ); + + std::indirect<std::vector<int, UneqAlloc>> + i9(std::in_place, 5, 13, UneqAlloc{42}); + VERIFY( i9->size() == 5 ); + VERIFY( i9->at(0) == 13 ); + VERIFY( i9->get_allocator().get_personality() == 42 ); + + std::indirect<std::vector<int, UneqAlloc>> + i10(std::in_place, {1, 2, 3, 4}, UneqAlloc{42}); + VERIFY( i10->size() == 4 ); + VERIFY( i10->at(2) == 3 ); + VERIFY( i10->get_allocator().get_personality() == 42 ); + + if (std::is_constant_evaluated()) + return; + + std::indirect<std::vector<int, UneqAlloc>, ScopedAlloc> + i14(std::allocator_arg, ScopedAlloc(11, 22), + std::in_place); + VERIFY( i14->size() == 0 ); + VERIFY( i14->get_allocator().get_personality() == 22 ); + VERIFY( i14.get_allocator().get_personality() == 11 ); + + std::indirect<std::vector<int, UneqAlloc>, ScopedAlloc> + i15(std::allocator_arg, ScopedAlloc(11, 22), + std::in_place, 5, 13); + VERIFY( i15->size() == 5 ); + VERIFY( i15->at(0) == 13 ); + VERIFY( i15->get_allocator().get_personality() == 22 ); + VERIFY( i15.get_allocator().get_personality() == 11 ); + + std::indirect<std::vector<int, UneqAlloc>, ScopedAlloc> + i16(std::allocator_arg, ScopedAlloc(11, 22), + std::in_place, {1, 2, 3, 4}); + VERIFY( i16->size() == 4 ); + VERIFY( i16->at(2) == 3 ); + VERIFY( i16->get_allocator().get_personality() == 22 ); + VERIFY( i16.get_allocator().get_personality() == 11 ); +} + +int main() +{ + test_default_ctor(); + test_forwarding_ctor(); + test_inplace_ctor(); + + static_assert([] { + test_default_ctor(); + test_forwarding_ctor(); + test_inplace_ctor(); + return true; + }()); +} diff --git a/libstdc++-v3/testsuite/std/memory/indirect/incomplete.cc b/libstdc++-v3/testsuite/std/memory/indirect/incomplete.cc new file mode 100644 index 0000000..1faf13d --- /dev/null +++ b/libstdc++-v3/testsuite/std/memory/indirect/incomplete.cc @@ -0,0 +1,38 @@ +// { dg-do compile { target c++26 } } + +#include <memory> + +struct Incomplete; +bool operator==(const Incomplete&, const Incomplete&); +std::strong_ordering operator<=>(const Incomplete&, const Incomplete&); + +template<> +struct std::hash<Incomplete> +{ + static std::size_t operator()(const Incomplete& c); +}; + +std::indirect<Incomplete>* +test_move(std::indirect<Incomplete>& i1, std::indirect<Incomplete>& i2) +{ + i2.swap(i2); + return new std::indirect<Incomplete>(std::move(i1)); +} + +void +test_relops(std::indirect<Incomplete> const& i1, Incomplete const& o) +{ + void(i1 == i1); + void(i1 < i1); + void(i1 >= i1); + + void(i1 != o); + void(i1 < o); +} + +void +test_hash(std::indirect<Incomplete> const& i1) +{ + std::hash<std::indirect<Incomplete>> h; + h(i1); +} diff --git a/libstdc++-v3/testsuite/std/memory/indirect/invalid_neg.cc b/libstdc++-v3/testsuite/std/memory/indirect/invalid_neg.cc new file mode 100644 index 0000000..82e7e84 --- /dev/null +++ b/libstdc++-v3/testsuite/std/memory/indirect/invalid_neg.cc @@ -0,0 +1,28 @@ +// { dg-do compile { target c++26 } } + +#include <memory> + +// In every specialization indirect<T, Allocator>, if the type +// allocator_traits<Allocator>::value_type is not the same type as T, +// the program is ill-formed. +using T1 = std::indirect<int, std::allocator<long>>::value_type; // { dg-error "here" } + +// A program that instantiates the definition of the template +// indirect<T, Allocator> with a type for the T parameter that is +// a non-object type, an array type, in_place_t, +// a specialization of in_place_type_t, or a cv-qualified type is ill-formed. + +using T2 = std::indirect<int&>::value_type; // { dg-error "here" } + +using T3 = std::indirect<int[1]>::value_type; // { dg-error "here" } + +using T4 = std::indirect<std::in_place_t>::value_type; // { dg-error "here" } + +using T5 = std::indirect<std::in_place_type_t<int>>::value_type; // { dg-error "here" } + +using T6 = std::indirect<const int>::value_type; // { dg-error "here" } + +using T7 = std::indirect<volatile int>::value_type; // { dg-error "here" } + +// { dg-error "static assertion failed" "" { target *-*-* } 0 } +// { dg-prune-output "forming pointer to reference" } diff --git a/libstdc++-v3/testsuite/std/memory/indirect/move.cc b/libstdc++-v3/testsuite/std/memory/indirect/move.cc new file mode 100644 index 0000000..9800f7f --- /dev/null +++ b/libstdc++-v3/testsuite/std/memory/indirect/move.cc @@ -0,0 +1,201 @@ +// { dg-do run { target c++26 } } + +#include <memory> +#include <scoped_allocator> +#include <utility> +#include <vector> +#include <optional> + +#include <testsuite_hooks.h> +#include <testsuite_allocator.h> + +using __gnu_test::tracker_allocator; +using Counter = __gnu_test::tracker_allocator_counter; +using Vector = std::vector<int>; +using Indirect = std::indirect<Vector, tracker_allocator<Vector>>; +const Indirect val(std::in_place, {1, 2, 3}); + +void +verifyNoAllocations() +{ + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 0 ); +} + +void +test_ctor() +{ + std::optional<Indirect> src; + auto make = [&src] -> Indirect&& { + src.emplace(val); + Counter::reset(); + return std::move(*src); + }; + + Indirect i1(make()); + VERIFY( src->valueless_after_move() ); + VERIFY( *i1 == *val ); + verifyNoAllocations(); + + Indirect i2(std::allocator_arg, {}, make()); + VERIFY( src->valueless_after_move() ); + VERIFY( *i2 == *val ); + verifyNoAllocations(); +} + +void +test_assign() +{ + std::optional<Indirect> src; + auto make = [&src] -> Indirect&& { + src.emplace(val); + Counter::reset(); + return std::move(*src); + }; + + Indirect i1; + + i1 = make(); + VERIFY( src->valueless_after_move() ); + VERIFY( *i1 == *val ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == sizeof(Vector) ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 1 ); + + auto(std::move(i1)); + i1 = make(); + VERIFY( *i1 == *val ); + VERIFY( src->valueless_after_move() ); + verifyNoAllocations(); +} + +void +test_swap() +{ + const Indirect val1(std::in_place, {1, 2, 3}); + const Indirect val2(std::in_place, {2, 4, 6}); + + Indirect i1(val1); + Indirect i2(val2); + Counter::reset(); + i1.swap(i2); + VERIFY( *i2 == *val1 ); + VERIFY( *i1 == *val2 ); + verifyNoAllocations(); + + auto(std::move(i1)); + + Counter::reset(); + i1.swap(i2); + VERIFY( *i1 == *val1 ); + VERIFY( i2.valueless_after_move() ); + verifyNoAllocations(); +} + +void +test_valueless() +{ + auto e = [] { + Indirect res; + auto(std::move(res)); + Counter::reset(); + return res; + }; + + Indirect i1(e()); + VERIFY( i1.valueless_after_move() ); + verifyNoAllocations(); + + Indirect i2(std::allocator_arg, {}, e()); + VERIFY( i2.valueless_after_move() ); + verifyNoAllocations(); + + Indirect i3(val); + i3 = e(); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == sizeof(Vector) ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 1 ); + + i3 = e(); + verifyNoAllocations(); +} + +constexpr void +test_constexpr() +{ + using Alloc = __gnu_test::uneq_allocator<Vector>; + using Indirect = std::indirect<Vector, Alloc>; + const Indirect val(std::in_place, {1, 2, 3}); + + std::optional<Indirect> src; + auto make = [&src, &val] -> Indirect&& { + src.emplace(val); + return std::move(*src); + }; + + Indirect i1(make()); + VERIFY( src->valueless_after_move() ); + VERIFY( *i1 == *val ); + + Indirect i2(std::allocator_arg, {}, make()); + VERIFY( src->valueless_after_move() ); + VERIFY( *i2 == *val ); + + i2 = make(); + VERIFY( src->valueless_after_move() ); + VERIFY( *i2 == *val ); + + auto(std::move(i2)); + i2 = make(); + VERIFY( *i2 == *val ); + VERIFY( src->valueless_after_move() ); + + const Indirect val1(std::in_place, {1, 2, 3}); + const Indirect val2(std::in_place, {2, 4, 6}); + + Indirect s1(val1); + Indirect s2(val2); + s1.swap(s2); + VERIFY( *s2 == *val1 ); + VERIFY( *s1 == *val2 ); + + auto(std::move(s1)); + + s1.swap(s2); + VERIFY( *s1 == *val1 ); + VERIFY( s2.valueless_after_move() ); + + auto e = [] { + Indirect res; + auto(std::move(res)); + return res; + }; + + Indirect e1(e()); + VERIFY( e1.valueless_after_move() ); + + Indirect e2(std::allocator_arg, {}, e()); + VERIFY( e2.valueless_after_move() ); + + Indirect e3(val); + e3 = e(); + e3 = e(); +} + +int main() +{ + test_ctor(); + test_assign(); + test_swap(); + test_valueless(); + test_constexpr(); + + static_assert([] { + test_constexpr(); + return true; + }()); +} diff --git a/libstdc++-v3/testsuite/std/memory/indirect/move_alloc.cc b/libstdc++-v3/testsuite/std/memory/indirect/move_alloc.cc new file mode 100644 index 0000000..cf35e83 --- /dev/null +++ b/libstdc++-v3/testsuite/std/memory/indirect/move_alloc.cc @@ -0,0 +1,290 @@ +// { dg-do run { target c++26 } } + +#include <memory> +#include <scoped_allocator> +#include <utility> +#include <vector> +#include <optional> + +#include <testsuite_hooks.h> +#include <testsuite_allocator.h> + +using __gnu_test::propagating_allocator; +using __gnu_test::tracker_allocator; +using Counter = __gnu_test::tracker_allocator_counter; + +void +verifyNoAllocations() +{ + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 0 ); +} + +template<bool Propagate> +void +test_ctor() +{ + using PropAlloc = propagating_allocator<int, Propagate>; + using Vector = std::vector<int, PropAlloc>; + using ScopedAlloc = std::scoped_allocator_adaptor< + propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>, + PropAlloc>; + using Indirect = std::indirect<Vector, ScopedAlloc>; + + const Indirect val(std::in_place, {1, 2, 3}); + std::optional<Indirect> src; + auto make = [&val, &src] -> Indirect&& { + src.emplace(std::allocator_arg, ScopedAlloc{11, 22}, val); + Counter::reset(); + return std::move(*src); + }; + + Indirect i1(make()); + VERIFY( src->valueless_after_move() ); + VERIFY( *i1 == *val ); + VERIFY( i1->get_allocator().get_personality() == 22 ); + VERIFY( i1.get_allocator().get_personality() == 11 ); + verifyNoAllocations(); + + Indirect i2(std::allocator_arg, ScopedAlloc{11, 22}, make()); + VERIFY( src->valueless_after_move() ); + VERIFY( *i2 == *val ); + VERIFY( i2->get_allocator().get_personality() == 22 ); + VERIFY( i2.get_allocator().get_personality() == 11 ); + verifyNoAllocations(); + + Indirect i3(std::allocator_arg, ScopedAlloc{33, 44}, make()); + // We move-from contained object + VERIFY( !src->valueless_after_move() ); + VERIFY( *i3 == *val ); + VERIFY( i3->get_allocator().get_personality() == 44 ); + VERIFY( i3.get_allocator().get_personality() == 33 ); + VERIFY( Counter::get_allocation_count() == sizeof(Vector) ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 1 ); + VERIFY( Counter::get_destruct_count() == 0 ); +} + +template<bool Propagate> +void +test_assign() +{ + using PropAlloc = propagating_allocator<int, Propagate>; + using Vector = std::vector<int, PropAlloc>; + using ScopedAlloc = std::scoped_allocator_adaptor< + propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>, + PropAlloc>; + using Indirect = std::indirect<Vector, ScopedAlloc>; + + const Indirect val(std::in_place, {1, 2, 3}); + std::optional<Indirect> src; + auto make = [&val, &src] -> Indirect&& { + src.emplace(std::allocator_arg, ScopedAlloc{11, 22}, val); + Counter::reset(); + return std::move(*src); + }; + + Indirect i1(std::allocator_arg, ScopedAlloc{11, 22}); + + i1 = make(); + VERIFY( src->valueless_after_move() ); + VERIFY( *i1 == *val ); + VERIFY( i1->get_allocator().get_personality() == 22 ); + VERIFY( i1.get_allocator().get_personality() == 11 ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == sizeof(Vector) ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 1 ); + + Indirect i2(std::allocator_arg, ScopedAlloc{33, 44}); + + i2 = make(); + VERIFY( *i2 == *val ); + if (Propagate) + { + VERIFY( src->valueless_after_move() ); + VERIFY( i2->get_allocator().get_personality() == 22 ); + VERIFY( i2.get_allocator().get_personality() == 11 ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + } + else + { + // We allocate new holder and move-from contained object + VERIFY( !src->valueless_after_move() ); + VERIFY( i2->get_allocator().get_personality() == 44 ); + VERIFY( i2.get_allocator().get_personality() == 33 ); + VERIFY( Counter::get_allocation_count() == sizeof(Vector) ); + VERIFY( Counter::get_construct_count() == 1 ); + } + VERIFY( Counter::get_deallocation_count() == sizeof(Vector) ); + VERIFY( Counter::get_destruct_count() == 1 ); + + Indirect i3(std::allocator_arg, ScopedAlloc{11, 22}); + auto(std::move(i3)); + + i3 = make(); + VERIFY( *i3 == *val ); + VERIFY( src->valueless_after_move() ); + VERIFY( i3->get_allocator().get_personality() == 22 ); + VERIFY( i3.get_allocator().get_personality() == 11 ); + verifyNoAllocations(); + + Indirect i4(std::allocator_arg, ScopedAlloc{33, 44}); + auto(std::move(i4)); + + i4 = make(); + VERIFY( *i4 == *val ); + if (Propagate) + { + VERIFY( src->valueless_after_move() ); + VERIFY( i4->get_allocator().get_personality() == 22 ); + VERIFY( i4.get_allocator().get_personality() == 11 ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + } + else + { + // We allocate new holder and move-from contained object + VERIFY( !src->valueless_after_move() ); + VERIFY( i4->get_allocator().get_personality() == 44 ); + VERIFY( i4.get_allocator().get_personality() == 33 ); + VERIFY( Counter::get_allocation_count() == sizeof(Vector) ); + VERIFY( Counter::get_construct_count() == 1 ); + } + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 0 ); +} + +template<bool Propagate> +void +test_swap() +{ + using PropAlloc = propagating_allocator<int, Propagate>; + using Vector = std::vector<int, PropAlloc>; + using ScopedAlloc = std::scoped_allocator_adaptor< + propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>, + PropAlloc>; + using Indirect = std::indirect<Vector, ScopedAlloc>; + + const Indirect val1(std::in_place, {1, 2, 3}); + const Indirect val2(std::in_place, {2, 4, 6}); + + Indirect i1(std::allocator_arg, ScopedAlloc{11, 22}, val1); + Indirect i2(std::allocator_arg, ScopedAlloc{11, 22}, val2); + Counter::reset(); + i1.swap(i2); + VERIFY( *i2 == *val1 ); + VERIFY( *i1 == *val2 ); + verifyNoAllocations(); + + auto(std::move(i1)); + + Counter::reset(); + i1.swap(i2); + VERIFY( *i1 == *val1 ); + VERIFY( i2.valueless_after_move() ); + verifyNoAllocations(); + + if (!Propagate) + return; + + Indirect i3(std::allocator_arg, ScopedAlloc{33, 44}, val2); + Counter::reset(); + i1.swap(i3); + VERIFY( *i1 == *val2 ); + VERIFY( i1->get_allocator().get_personality() == 44 ); + VERIFY( i1.get_allocator().get_personality() == 33 ); + VERIFY( *i3 == *val1 ); + VERIFY( i3->get_allocator().get_personality() == 22 ); + VERIFY( i3.get_allocator().get_personality() == 11 ); + verifyNoAllocations(); + + i1.swap(i2); + VERIFY( i1.valueless_after_move() ); + VERIFY( i1.get_allocator().get_personality() == 11 ); + VERIFY( *i2 == *val2 ); + VERIFY( i2->get_allocator().get_personality() == 44 ); + VERIFY( i2.get_allocator().get_personality() == 33 ); + verifyNoAllocations(); +} + +template<bool Propagate> +void +test_valueless() +{ + using PropAlloc = propagating_allocator<int, Propagate>; + using Vector = std::vector<int, PropAlloc>; + using ScopedAlloc = std::scoped_allocator_adaptor< + propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>, + PropAlloc>; + using Indirect = std::indirect<Vector, ScopedAlloc>; + + auto e = [] { + Indirect res(std::allocator_arg, ScopedAlloc{11, 22}); + auto(std::move(res)); + Counter::reset(); + return res; + }; + + Indirect i1(e()); + VERIFY( i1.valueless_after_move() ); + VERIFY( i1.get_allocator().get_personality() == 11 ); + verifyNoAllocations(); + + Indirect i2(std::allocator_arg, ScopedAlloc{33, 44}, e()); + VERIFY( i2.valueless_after_move() ); + VERIFY( i2.get_allocator().get_personality() == 33 ); + verifyNoAllocations(); + + Indirect i3(std::allocator_arg, ScopedAlloc{33, 44}); + + i3 = e(); + VERIFY( i3.valueless_after_move() ); + if (Propagate) + VERIFY( i3.get_allocator().get_personality() == 11 ); + else + VERIFY( i3.get_allocator().get_personality() == 33 ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == sizeof(Vector) ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 1 ); + + i2 = e(); + VERIFY( i2.valueless_after_move() ); + if (Propagate) + VERIFY( i2.get_allocator().get_personality() == 11 ); + else + VERIFY( i2.get_allocator().get_personality() == 33 ); + verifyNoAllocations(); + + i3.swap(i2); + VERIFY( i2.valueless_after_move() ); + VERIFY( i1.valueless_after_move() ); + verifyNoAllocations(); + + if (!Propagate) + return; + + Indirect i4(std::allocator_arg, ScopedAlloc{33, 44}, e()); + i4.swap(i1); + verifyNoAllocations(); +} + +template<bool Propagate> +void +test_all() +{ + test_ctor<Propagate>(); + test_assign<Propagate>(); + test_swap<Propagate>(); + test_valueless<Propagate>(); +} + +int main() +{ + test_all<true>(); + test_all<false>(); +} diff --git a/libstdc++-v3/testsuite/std/memory/indirect/relops.cc b/libstdc++-v3/testsuite/std/memory/indirect/relops.cc new file mode 100644 index 0000000..77d599c --- /dev/null +++ b/libstdc++-v3/testsuite/std/memory/indirect/relops.cc @@ -0,0 +1,82 @@ +// { dg-do run { target c++26 } } + +#include <memory> +#include <testsuite_hooks.h> + +struct Obj +{ + int i; + constexpr auto operator<=>(const Obj&) const = default; +}; + +template<> +struct std::hash<Obj> +{ + static size_t operator()(Obj const& obj) + { return std::hash<int>{}(obj.i); } +}; + +constexpr void +test_relops() +{ + std::indirect<Obj> i1; + VERIFY( i1 == i1 ); + VERIFY( i1 <= i1 ); + VERIFY( i1 >= i1 ); + + std::indirect<Obj> i2 = std::move(i1); // make i1 valueless + VERIFY( i1 == i1 ); + VERIFY( i2 == i2 ); + VERIFY( i2 != i1 ); + VERIFY( i1 < i2 ); + VERIFY( i2 >= i1 ); + + std::indirect<Obj> i3 = std::move(i2); // make i2 valueless + VERIFY( i2 == i1 ); + VERIFY( i2 >= i1 ); + VERIFY( i2 <= i1 ); + VERIFY( i3 > i2 ); +} + +constexpr void +test_comp_with_t() +{ + std::indirect<Obj> i1; + Obj o{2}; + VERIFY( i1 != o ); + VERIFY( i1 < o ); + + std::indirect<Obj> i2(Obj{2}); + VERIFY( i2 == o ); + VERIFY( i2 <= o ); + VERIFY( o <= i2 ); + + std::indirect<Obj> i3 = std::move(i2); // make i2 valueless + VERIFY( i2 != o ); + VERIFY( i2 < o ); +} + +void +test_hash() +{ + Obj o{5}; + std::indirect<Obj> i(o); + VERIFY( std::hash<std::indirect<Obj>>{}(i) + == std::hash<Obj>{}(o) ); + + auto(std::move(i)); // make i valueless + (void)std::hash<std::indirect<Obj>>{}(i); +} + +int main() +{ + test_relops(); + test_comp_with_t(); + test_hash(); + + static_assert([] { + test_relops(); + test_comp_with_t(); + return true; + }()); +} diff --git a/libstdc++-v3/testsuite/std/memory/polymorphic/access.cc b/libstdc++-v3/testsuite/std/memory/polymorphic/access.cc new file mode 100644 index 0000000..7b95bb1 --- /dev/null +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/access.cc @@ -0,0 +1,53 @@ +// { dg-do run { target c++26 } } + +#include <memory> +#include <vector> + +#include <testsuite_hooks.h> + +template<template<typename> class Polymorhpic> +constexpr void +test_access() +{ + const std::vector<int> src{1, 2, 3, 4, 5}; + Polymorhpic<std::vector<int>> i(src); + auto const& ci = i; + VERIFY( *i == src ); + VERIFY( *ci == src ); + VERIFY( *std::move(ci) == src ); + + auto&& vr = *std::move(i); + static_assert( std::is_same_v<decltype(vr), std::vector<int>&> ); + VERIFY( vr == src ); + VERIFY( *i == src ); +} + +template<typename T> +struct PublicBase : std::polymorphic<T> +{ + using std::polymorphic<T>::polymorphic; +}; + +template<typename T> +class PrivateBase : std::polymorphic<T> +{ +public: + using std::polymorphic<T>::polymorphic; + using std::polymorphic<T>::operator*; + using std::polymorphic<T>::operator->; +}; + +constexpr bool +test_all() +{ + test_access<std::polymorphic>(); + test_access<PublicBase>(); + test_access<PrivateBase>(); + return true; +} + +int main() +{ + test_all(); +// static_assert(test_all()); +} diff --git a/libstdc++-v3/testsuite/std/memory/polymorphic/copy.cc b/libstdc++-v3/testsuite/std/memory/polymorphic/copy.cc new file mode 100644 index 0000000..34c7822 --- /dev/null +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/copy.cc @@ -0,0 +1,192 @@ +// { dg-do run { target c++26 } } + +#include <memory> +#include <scoped_allocator> +#include <utility> +#include <vector> + +#include <testsuite_hooks.h> +#include <testsuite_allocator.h> + +struct Base { + friend constexpr + bool operator==(const Base& lhs, const Base& rhs) + { return lhs.eq(rhs); } + +private: + constexpr virtual bool + eq(const Base& other) const = 0; +}; + +struct Derived : Base +{ + constexpr Derived() + : x(0), y(0), z(0) + { } + + constexpr Derived(int a, int b, int c) + : x(a), y(b), z(c) + { } + +private: + constexpr bool + eq(const Base& other) const override + { + if (auto op = dynamic_cast<const Derived*>(&other)) + return this->x == op->x && this->y == op->y && this->z == op->z; + return false; + } + + int x; + int y; + int z; +}; + +using __gnu_test::tracker_allocator; +using Counter = __gnu_test::tracker_allocator_counter; +using Polymorphic = std::polymorphic<Base, tracker_allocator<Base>>; +const Polymorphic src(std::in_place_type<Derived>, 1, 2, 3); + +void +test_ctor() +{ + Counter::reset(); + Polymorphic i1(src); + VERIFY( *i1 == *src ); + VERIFY( &*i1 != &*src ); + VERIFY( Counter::get_allocation_count() >= sizeof(Derived) ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 2 ); + VERIFY( Counter::get_destruct_count() == 0 ); + + Counter::reset(); + Polymorphic i2(std::allocator_arg, {}, src); + VERIFY( *i2 == *src ); + VERIFY( &*i2 != &*src ); + VERIFY( Counter::get_allocation_count() >= sizeof(Derived) ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 2 ); + VERIFY( Counter::get_destruct_count() == 0 ); +} + +void +test_assign() +{ + Counter::reset(); + Polymorphic i1(std::in_place_type<Derived>); + const size_t holderSize = Counter::get_allocation_count(); + VERIFY( holderSize >= sizeof(Derived) ); + Counter::reset(); + + i1 = src; + VERIFY( *i1 == *src ); + VERIFY( &*i1 != &*src ); + VERIFY( Counter::get_allocation_count() == holderSize ); + VERIFY( Counter::get_deallocation_count() == holderSize ); + VERIFY( Counter::get_construct_count() == 2 ); + VERIFY( Counter::get_destruct_count() == 1 ); + + auto(std::move(i1)); + Counter::reset(); + + i1 = src; + VERIFY( *i1 == *src ); + VERIFY( &*i1 != &*src ); + VERIFY( Counter::get_allocation_count() == holderSize ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 2 ); + VERIFY( Counter::get_destruct_count() == 0 ); +} + +void +test_valueless() +{ + Polymorphic e(std::in_place_type<Derived>); + auto(std::move(e)); + VERIFY( e.valueless_after_move() ); + + Counter::reset(); + Polymorphic i1(e); + VERIFY( i1.valueless_after_move() ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 0 ); + + Polymorphic i2(std::allocator_arg, {}, e); + VERIFY( i2.valueless_after_move() ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 0 ); + + Polymorphic i3(src); + Counter::reset(); + i3 = e; + VERIFY( i3.valueless_after_move() ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() >= sizeof(Derived) ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 1 ); + + Counter::reset(); + i3 = e; + VERIFY( i3.valueless_after_move() ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 0 ); +} + +constexpr void +test_constexpr() +{ + using Polymorphic = std::polymorphic<Base, __gnu_test::uneq_allocator<Base>>; + const Polymorphic src(std::in_place_type<Derived>, 1, 2, 3); + + Polymorphic i1(src); + VERIFY( *i1 == *src ); + VERIFY( &*i1 != &*src ); + + Polymorphic i2(std::allocator_arg, {}, src); + VERIFY( *i2 == *src ); + VERIFY( &*i2 != &*src ); + + i1 = Polymorphic(std::in_place_type<Derived>); + VERIFY( *i1 != *src ); + i1 = src; + VERIFY( *i1 == *src ); + VERIFY( &*i1 != &*src ); + + auto(std::move(i1)); + i1 = src; + VERIFY( *i1 == *src ); + VERIFY( &*i1 != &*src ); + + Polymorphic e(std::in_place_type<Derived>); + auto(std::move(e)); + VERIFY( e.valueless_after_move() ); + + Polymorphic e1(e); + VERIFY( e1.valueless_after_move() ); + + Polymorphic e2(std::allocator_arg, {}, e); + VERIFY( e2.valueless_after_move() ); + + Polymorphic e3(src); + e3 = e; + VERIFY( e3.valueless_after_move() ); +} + +int main() +{ + test_ctor(); + test_assign(); + test_valueless(); + test_constexpr(); + + static_assert([] { + test_constexpr(); + return true; + }()); +} diff --git a/libstdc++-v3/testsuite/std/memory/polymorphic/copy_alloc.cc b/libstdc++-v3/testsuite/std/memory/polymorphic/copy_alloc.cc new file mode 100644 index 0000000..f149fc8 --- /dev/null +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/copy_alloc.cc @@ -0,0 +1,264 @@ +// { dg-do run { target c++26 } } + +#include <memory> +#include <scoped_allocator> +#include <utility> +#include <vector> + +#include <testsuite_hooks.h> +#include <testsuite_allocator.h> + +struct Base { + friend + bool operator==(const Base& lhs, const Base& rhs) + { return lhs.eq(rhs); } + + virtual int + get_alloc_personality() const + { return -1; } + +private: + virtual bool + eq(const Base& other) const = 0; +}; + +template<typename T, typename Allocator> +struct VecDerived : Base, std::vector<T, Allocator> +{ + using VecBase = std::vector<T, Allocator>; + + using VecBase::VecBase; + + int + get_alloc_personality() const override + { return this->get_allocator().get_personality(); } + +private: + + bool + eq(const Base& other) const override + { + if (auto op = dynamic_cast<const VecDerived*>(&other)) + return *static_cast<const VecBase*>(this) + == *static_cast<const VecBase*>(op); + return false; + } +}; + +using __gnu_test::propagating_allocator; +using __gnu_test::tracker_allocator; +using Counter = __gnu_test::tracker_allocator_counter; + +template<bool Propagate> +void +test_ctor() +{ + using PropAlloc = propagating_allocator<int, Propagate>; + using Vector = VecDerived<int, PropAlloc>; + using ScopedAlloc = std::scoped_allocator_adaptor< + propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>, + PropAlloc>; + using Polymorphic = std::polymorphic<Vector, ScopedAlloc>; + + const Polymorphic src(std::allocator_arg, ScopedAlloc{11, 22}, + std::in_place_type<Vector>, {1, 2, 3}); + + Counter::reset(); + Polymorphic i1(src); + VERIFY( *i1 == *src ); + VERIFY( &*i1 != &*src ); + if (Propagate) + { + VERIFY( i1->get_alloc_personality() == 22 ); + VERIFY( i1.get_allocator().get_personality() == 11 ); + } + else + { + VERIFY( i1->get_alloc_personality() == 0 ); + VERIFY( i1.get_allocator().get_personality() == 0 ); + } + VERIFY( Counter::get_allocation_count() >= sizeof(Vector) ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 2 ); + VERIFY( Counter::get_destruct_count() == 0 ); + + + Counter::reset(); + Polymorphic i2(std::allocator_arg, ScopedAlloc{33, 44}, src); + VERIFY( *i2 == *src ); + VERIFY( &*i2 != &*src ); + VERIFY( i2->get_alloc_personality() == 44 ); + VERIFY( i2.get_allocator().get_personality() == 33 ); + VERIFY( Counter::get_allocation_count() >= sizeof(Vector) ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 2 ); + VERIFY( Counter::get_destruct_count() == 0 ); +} + +template<bool Propagate> +void +test_assign() +{ + using PropAlloc = propagating_allocator<int, Propagate>; + using Vector = VecDerived<int, PropAlloc>; + using ScopedAlloc = std::scoped_allocator_adaptor< + propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>, + PropAlloc>; + using Polymorphic = std::polymorphic<Vector, ScopedAlloc>; + + const Polymorphic src(std::allocator_arg, ScopedAlloc{11, 22}, + std::in_place_type<Vector>, {1, 2, 3}); + + Counter::reset(); + Polymorphic i1(std::allocator_arg, ScopedAlloc{11, 22}, + std::in_place_type<Vector>); + const size_t holderSize = Counter::get_allocation_count(); + VERIFY( holderSize >= sizeof(Vector) ); + Counter::reset(); + + i1 = src; + VERIFY( *i1 == *src ); + VERIFY( &*i1 != &*src ); + VERIFY( i1->get_alloc_personality() == 22 ); + VERIFY( i1.get_allocator().get_personality() == 11 ); + VERIFY( Counter::get_allocation_count() == holderSize ); + VERIFY( Counter::get_deallocation_count() == holderSize ); + VERIFY( Counter::get_construct_count() == 2 ); + VERIFY( Counter::get_destruct_count() == 1 ); + + Polymorphic i2(std::allocator_arg, ScopedAlloc{33, 44}); + Counter::reset(); + + i2 = src; + VERIFY( *i2 == *src ); + VERIFY( &*i2 != &*src ); + if (Propagate) + { + VERIFY( i2->get_alloc_personality() == 22 ); + VERIFY( i2.get_allocator().get_personality() == 11 ); + } + else + { + VERIFY( i2->get_alloc_personality() == 44 ); + VERIFY( i2.get_allocator().get_personality() == 33 ); + } + VERIFY( Counter::get_allocation_count() == holderSize ); + VERIFY( Counter::get_deallocation_count() == holderSize ); + VERIFY( Counter::get_construct_count() == 2 ); + VERIFY( Counter::get_destruct_count() == 1 ); + + Polymorphic i3(std::allocator_arg, ScopedAlloc{11, 22}); + auto(std::move(i3)); + Counter::reset(); + + i3 = src; + VERIFY( *i3 == *src ); + VERIFY( &*i3 != &*src ); + VERIFY( i3->get_alloc_personality() == 22 ); + VERIFY( i3.get_allocator().get_personality() == 11 ); + VERIFY( Counter::get_allocation_count() == holderSize ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 2 ); + VERIFY( Counter::get_destruct_count() == 0 ); + + Polymorphic i4(std::allocator_arg, ScopedAlloc{33, 44}); + auto(std::move(i4)); + Counter::reset(); + + i4 = src; + VERIFY( *i4 == *src ); + VERIFY( &*i4 != &*src ); + if (Propagate) + { + VERIFY( i4->get_alloc_personality() == 22 ); + VERIFY( i4.get_allocator().get_personality() == 11 ); + } + else + { + VERIFY( i4->get_alloc_personality() == 44 ); + VERIFY( i4.get_allocator().get_personality() == 33 ); + } + VERIFY( Counter::get_allocation_count() == holderSize ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 2 ); + VERIFY( Counter::get_destruct_count() == 0 ); +} + +template<bool Propagate> +void +test_valueless() +{ + using PropAlloc = propagating_allocator<int, Propagate>; + using Vector = VecDerived<int, PropAlloc>; + using ScopedAlloc = std::scoped_allocator_adaptor< + propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>, + PropAlloc>; + using Polymorphic = std::polymorphic<Vector, ScopedAlloc>; + + Polymorphic e(std::allocator_arg, ScopedAlloc{11, 22}, + std::in_place_type<Vector>); + auto(std::move(e)); + VERIFY( e.valueless_after_move() ); + + Counter::reset(); + Polymorphic i1(e); + VERIFY( i1.valueless_after_move() ); + if (Propagate) + VERIFY( i1.get_allocator().get_personality() == 11 ); + else + VERIFY( i1.get_allocator().get_personality() == 0 ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 0 ); + + Counter::reset(); + Polymorphic i2(std::allocator_arg, ScopedAlloc{33, 44}, e); + VERIFY( i2.valueless_after_move() ); + VERIFY( i2.get_allocator().get_personality() == 33 ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 0 ); + + Polymorphic i3(std::allocator_arg, ScopedAlloc{33, 44}); + Counter::reset(); + + i3 = e; + VERIFY( i3.valueless_after_move() ); + if (Propagate) + VERIFY( i3.get_allocator().get_personality() == 11 ); + else + VERIFY( i3.get_allocator().get_personality() == 33 ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() >= sizeof(Vector) ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 1 ); + + Counter::reset(); + i2 = e; + VERIFY( i2.valueless_after_move() ); + if (Propagate) + VERIFY( i2.get_allocator().get_personality() == 11 ); + else + VERIFY( i2.get_allocator().get_personality() == 33 ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 0 ); +} + +template<bool Propagate> +void +test_all() +{ + test_ctor<Propagate>(); + test_assign<Propagate>(); + test_valueless<Propagate>(); +} + +int main() +{ + test_all<true>(); + test_all<false>(); +} diff --git a/libstdc++-v3/testsuite/std/memory/polymorphic/ctor.cc b/libstdc++-v3/testsuite/std/memory/polymorphic/ctor.cc new file mode 100644 index 0000000..4d043db --- /dev/null +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/ctor.cc @@ -0,0 +1,199 @@ +// { dg-do run { target c++26 } } + +#include <memory> +#include <scoped_allocator> +#include <utility> +#include <vector> + +#ifndef __cpp_lib_polymorphic +# error __cpp_lib_polymorphic feature test macro missing in <memory> +#elif __cpp_lib_polymorphic != 202502 +# error __cpp_lib_polymorphic feature test macro has wrong value in <memory> +#endif + +#include <testsuite_hooks.h> +#include <testsuite_allocator.h> + +using __gnu_test::uneq_allocator; +using UneqAlloc = uneq_allocator<int>; +using ScopedAlloc = std::scoped_allocator_adaptor< + uneq_allocator<std::vector<int, UneqAlloc>>, + UneqAlloc>; + +struct Obj +{ + int i; + char c[2]; +}; + +constexpr void +test_default_ctor() +{ + using __gnu_test::default_init_allocator; + + std::polymorphic<Obj, default_init_allocator<Obj>> i1; + default_init_allocator<int> a{}; + + // The contained object and the allocator should be value-initialized. + VERIFY( i1->i == 0 ); + VERIFY( i1->c[0] == 0 ); + VERIFY( i1->c[1] == 0 ); + VERIFY( i1.get_allocator() == a ); + + a.state = 5; + // Allocator-extended default constructor: + std::polymorphic<Obj, default_init_allocator<Obj>> i2(std::allocator_arg, a); + VERIFY( i2.get_allocator() == a ); + + if (std::is_constant_evaluated()) + return; + + // Object is constructed using allocator-aware constructor. + std::polymorphic<std::vector<int, UneqAlloc>, ScopedAlloc> + i3(std::allocator_arg, ScopedAlloc(11, 22)); + VERIFY( i3->empty() ); + VERIFY( i3->get_allocator().get_personality() == 22 ); + VERIFY( i3.get_allocator().get_personality() == 11 ); +} + +constexpr void +test_forwarding_ctor() +{ + Obj obj{1, {'2', '3'}}; + auto verify = [](std::polymorphic<Obj> const& i) + { + VERIFY( i->i == 1 ); + VERIFY( i->c[0] == '2' ); + VERIFY( i->c[1] == '3' ); + }; + + std::polymorphic<Obj> i1(std::as_const(obj)); + verify(i1); + std::polymorphic<Obj> i2(std::move(std::as_const(obj))); + verify(i2); + std::polymorphic<Obj> i3(obj); + verify(i3); + std::polymorphic<Obj> i4(std::move(obj)); + verify(i4); + + std::polymorphic<Obj> i5({1, {'2', '3'}}); + verify(i5); + + if (std::is_constant_evaluated()) + return; + + std::vector<int, UneqAlloc> v{1, 2, 3, 4, 5}; + // Object is constructed using allocator-aware constructor. + std::polymorphic<std::vector<int, UneqAlloc>, ScopedAlloc> + i7(std::allocator_arg, ScopedAlloc(11, 22), v); + VERIFY( i7->size() == 5 ); + VERIFY( v.size() == 5 ); + VERIFY( i7->get_allocator().get_personality() == 22 ); + VERIFY( i7.get_allocator().get_personality() == 11 ); + + std::polymorphic<std::vector<int, UneqAlloc>, ScopedAlloc> + i8(std::allocator_arg, ScopedAlloc(11, 22), std::move(v)); + VERIFY( i8->size() == 5 ); + VERIFY( v.size() == 0 ); + VERIFY( i8->get_allocator().get_personality() == 22 ); + VERIFY( i8.get_allocator().get_personality() == 11 ); +} + +constexpr void +test_inplace_ctor() +{ + std::polymorphic<Obj> i1(std::in_place_type<Obj>); + VERIFY( i1->i == 0 ); + VERIFY( i1->c[0] == 0 ); + VERIFY( i1->c[1] == 0 ); + + std::polymorphic<Obj> i2(std::in_place_type<Obj>, 10); + VERIFY( i2->i == 10 ); + VERIFY( i2->c[0] == 0 ); + VERIFY( i2->c[1] == 0 ); + + std::polymorphic<Obj, uneq_allocator<Obj>> + i3(std::allocator_arg, 42, std::in_place_type<Obj>); + VERIFY( i3->i == 0 ); + VERIFY( i3->c[0] == 0 ); + VERIFY( i3->c[1] == 0 ); + VERIFY( i3.get_allocator().get_personality() == 42 ); + + std::polymorphic<Obj, uneq_allocator<Obj>> + i4(std::allocator_arg, 42, std::in_place_type<Obj>, 10); + VERIFY( i4->i == 10 ); + VERIFY( i4->c[0] == 0 ); + VERIFY( i4->c[1] == 0 ); + VERIFY( i4.get_allocator().get_personality() == 42 ); + + std::polymorphic<std::vector<int>> + i5(std::in_place_type<std::vector<int>>); + VERIFY( i5->size() == 0 ); + + std::polymorphic<std::vector<int>> + i6(std::in_place_type<std::vector<int>>, 5, 13); + VERIFY( i6->size() == 5 ); + VERIFY( i6->at(0) == 13 ); + + std::polymorphic<std::vector<int>> + i7(std::in_place_type<std::vector<int>>, {1, 2, 3, 4}); + VERIFY( i7->size() == 4 ); + VERIFY( i7->at(2) == 3 ); + + std::polymorphic<std::vector<int, UneqAlloc>> + i8(std::in_place_type<std::vector<int, UneqAlloc>>, UneqAlloc{42}); + VERIFY( i8->size() == 0 ); + VERIFY( i8->get_allocator().get_personality() == 42 ); + + std::polymorphic<std::vector<int, UneqAlloc>> + i9(std::in_place_type<std::vector<int, UneqAlloc>>, 5, 13, UneqAlloc{42}); + VERIFY( i9->size() == 5 ); + VERIFY( i9->at(0) == 13 ); + VERIFY( i9->get_allocator().get_personality() == 42 ); + + std::polymorphic<std::vector<int, UneqAlloc>> + i10(std::in_place_type<std::vector<int, UneqAlloc>>, {1, 2, 3, 4}, UneqAlloc{42}); + VERIFY( i10->size() == 4 ); + VERIFY( i10->at(2) == 3 ); + VERIFY( i10->get_allocator().get_personality() == 42 ); + + if (std::is_constant_evaluated()) + return; + + std::polymorphic<std::vector<int, UneqAlloc>, ScopedAlloc> + i14(std::allocator_arg, ScopedAlloc(11, 22), + std::in_place_type<std::vector<int, UneqAlloc>>); + VERIFY( i14->size() == 0 ); + VERIFY( i14->get_allocator().get_personality() == 22 ); + VERIFY( i14.get_allocator().get_personality() == 11 ); + + std::polymorphic<std::vector<int, UneqAlloc>, ScopedAlloc> + i15(std::allocator_arg, ScopedAlloc(11, 22), + std::in_place_type<std::vector<int, UneqAlloc>>, 5, 13); + VERIFY( i15->size() == 5 ); + VERIFY( i15->at(0) == 13 ); + VERIFY( i15->get_allocator().get_personality() == 22 ); + VERIFY( i15.get_allocator().get_personality() == 11 ); + + std::polymorphic<std::vector<int, UneqAlloc>, ScopedAlloc> + i16(std::allocator_arg, ScopedAlloc(11, 22), + std::in_place_type<std::vector<int, UneqAlloc>>, {1, 2, 3, 4}); + VERIFY( i16->size() == 4 ); + VERIFY( i16->at(2) == 3 ); + VERIFY( i16->get_allocator().get_personality() == 22 ); + VERIFY( i16.get_allocator().get_personality() == 11 ); +} + +int main() +{ + test_default_ctor(); + test_forwarding_ctor(); + test_inplace_ctor(); + + static_assert([] { + test_default_ctor(); + test_forwarding_ctor(); + test_inplace_ctor(); + return true; + }()); +} diff --git a/libstdc++-v3/testsuite/std/memory/polymorphic/ctor_poly.cc b/libstdc++-v3/testsuite/std/memory/polymorphic/ctor_poly.cc new file mode 100644 index 0000000..cb18031 --- /dev/null +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/ctor_poly.cc @@ -0,0 +1,226 @@ +// { dg-do run { target c++26 } } + +#include <memory> +#include <scoped_allocator> +#include <utility> +#include <vector> + +#ifndef __cpp_lib_polymorphic +# error __cpp_lib_polymorphic feature test macro missing in <memory> +#elif __cpp_lib_polymorphic != 202502 +# error __cpp_lib_polymorphic feature test macro has wrong value in <memory> +#endif + +#include <testsuite_hooks.h> +#include <testsuite_allocator.h> + +struct Base { + friend constexpr + bool operator==(const Base& lhs, const Base& rhs) + { return lhs.eq(rhs); } + + virtual constexpr int + get_personality() const + { return -1; } + +private: + constexpr virtual bool + eq(const Base& other) const + { return true; } +}; + +struct ObjDerived : Base +{ + constexpr ObjDerived() + : x(0), y(0), z(0) + { } + + constexpr ObjDerived(int a, int b, int c) + : x(a), y(b), z(c) + { } + + virtual constexpr int + get_personality() const + { return -2; } + +private: + constexpr bool + eq(const Base& other) const override + { + if (auto op = dynamic_cast<const ObjDerived*>(&other)) + return this->x == op->x && this->y == op->y && this->z == op->z; + return false; + } + + int x; + int y; + int z; +}; + +template<typename T, typename Allocator> +struct VecDerived : Base, std::vector<T, Allocator> +{ + using VecBase = std::vector<T, Allocator>; + + using VecBase::VecBase; + + constexpr int + get_personality() const override + { return this->get_allocator().get_personality(); } + +private: + + constexpr bool + eq(const Base& other) const override + { + if (auto op = dynamic_cast<const VecDerived*>(&other)) + return *static_cast<const VecBase*>(this) + == *static_cast<const VecBase*>(op); + return false; + } +}; + +using __gnu_test::uneq_allocator; +using UneqAlloc = uneq_allocator<int>; +using ScopedAlloc = std::scoped_allocator_adaptor< + uneq_allocator<Base>, + UneqAlloc>; + +constexpr void +test_default_ctor() +{ + using __gnu_test::default_init_allocator; + + std::polymorphic<Base, default_init_allocator<Base>> i1; + default_init_allocator<int> a{}; + + // The contained object and the allocator should be value-initialized. + VERIFY( *i1 == Base() ); + VERIFY( i1->get_personality() == -1 ); + VERIFY( i1.get_allocator() == a ); + + a.state = 5; + // Allocator-extended default constructor: + std::polymorphic<Base, default_init_allocator<Base>> i2(std::allocator_arg, a); + VERIFY( *i1 == Base() ); + VERIFY( i1->get_personality() == -1 ); +} + +constexpr void +test_forwarding_ctor() +{ + const ObjDerived src(1, 2, 3); + + std::polymorphic<Base> i1(src); + VERIFY( *i1 == src ); + VERIFY( i1->get_personality() == -2 ); + std::polymorphic<Base> i2(std::move(src)); + VERIFY( *i2 == src ); + VERIFY( i2->get_personality() == -2 ); + + ObjDerived obj = src; + std::polymorphic<Base> i3(obj); + VERIFY( *i3 == src ); + VERIFY( i3->get_personality() == -2 ); + std::polymorphic<Base> i4(std::move(obj)); + VERIFY( *i4 == src ); + VERIFY( i4->get_personality() == -2 ); + + if (std::is_constant_evaluated()) + return; + + const VecDerived<int, UneqAlloc> v{1, 2, 3, 4, 5}; + // Object is constructed using allocator-aware constructor. + std::polymorphic<Base, ScopedAlloc> + i5(std::allocator_arg, ScopedAlloc(11, 22), v); + VERIFY( *i5 == v ); + VERIFY( i5->get_personality() == 22 ); + VERIFY( i5.get_allocator().get_personality() == 11 ); + + std::polymorphic<Base, ScopedAlloc> + i6(std::allocator_arg, ScopedAlloc(11, 22), auto(v)); + VERIFY( *i6 == v ); + VERIFY( i6->get_personality() == 22 ); + VERIFY( i6.get_allocator().get_personality() == 11 ); +} + +constexpr void +test_inplace_ctor() +{ + std::polymorphic<Base> i1(std::in_place_type<ObjDerived>); + VERIFY( *i1 == ObjDerived() ); + VERIFY( i1->get_personality() == -2 ); + + std::polymorphic<Base> i2(std::in_place_type<ObjDerived>, 10, 20, 30); + VERIFY( *i2 == ObjDerived(10, 20, 30) ); + VERIFY( i2->get_personality() == -2 ); + + std::polymorphic<Base, uneq_allocator<Base>> + i3(std::allocator_arg, 42, std::in_place_type<ObjDerived>); + VERIFY( *i3 == ObjDerived() ); + VERIFY( i3->get_personality() == -2 ); + VERIFY( i3.get_allocator().get_personality() == 42 ); + + std::polymorphic<Base, uneq_allocator<Base>> + i4(std::allocator_arg, 42, std::in_place_type<ObjDerived>, 10, 20, 30); + VERIFY( *i4 == ObjDerived(10, 20, 30) ); + VERIFY( i4->get_personality() == -2 ); + VERIFY( i4.get_allocator().get_personality() == 42 ); + + const VecDerived<int, UneqAlloc> ze; + const VecDerived<int, UneqAlloc> fe(5, 13); + const VecDerived<int, UneqAlloc> il{1, 2, 3 ,4}; + + std::polymorphic<Base> + i5(std::in_place_type<VecDerived<int, UneqAlloc>>, UneqAlloc{42}); + VERIFY( *i5 == ze ); + VERIFY( i5->get_personality() == 42 ); + + std::polymorphic<Base> + i6(std::in_place_type<VecDerived<int, UneqAlloc>>, 5, 13, UneqAlloc{42}); + VERIFY( *i6 == fe ); + VERIFY( i6->get_personality() == 42 ); + + std::polymorphic<Base> + i7(std::in_place_type<VecDerived<int, UneqAlloc>>, {1, 2, 3, 4}, UneqAlloc{42}); + VERIFY( *i7 == il ); + VERIFY( i7->get_personality() == 42 ); + + if (std::is_constant_evaluated()) + return; + + std::polymorphic<Base, ScopedAlloc> + i8(std::allocator_arg, ScopedAlloc(11, 22), + std::in_place_type<VecDerived<int, UneqAlloc>>); + VERIFY( *i8 == ze ); + VERIFY( i8->get_personality() == 22 ); + VERIFY( i8.get_allocator().get_personality() == 11 ); + + std::polymorphic<Base, ScopedAlloc> + i9(std::allocator_arg, ScopedAlloc(11, 22), + std::in_place_type<VecDerived<int, UneqAlloc>>, 5, 13); + VERIFY( *i9 == fe ); + VERIFY( i9->get_personality() == 22 ); + VERIFY( i9.get_allocator().get_personality() == 11 ); + + std::polymorphic<Base, ScopedAlloc> + i10(std::allocator_arg, ScopedAlloc(11, 22), + std::in_place_type<VecDerived<int, UneqAlloc>>, {1, 2, 3, 4}); + VERIFY( *i10 == il ); + VERIFY( i10->get_personality() == 22 ); + VERIFY( i10.get_allocator().get_personality() == 11 ); +} + +int main() +{ + test_default_ctor(); + test_forwarding_ctor(); + test_inplace_ctor(); + + static_assert([] { + test_default_ctor(); + test_forwarding_ctor(); + test_inplace_ctor(); + return true; + }()); +} diff --git a/libstdc++-v3/testsuite/std/memory/polymorphic/incomplete.cc b/libstdc++-v3/testsuite/std/memory/polymorphic/incomplete.cc new file mode 100644 index 0000000..e5dd78f --- /dev/null +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/incomplete.cc @@ -0,0 +1,13 @@ +// { dg-do compile { target c++26 } } + +#include <memory> + +struct Incomplete; + +std::polymorphic<Incomplete>* +test_move(std::polymorphic<Incomplete>& i1, std::polymorphic<Incomplete>& i2) +{ + i1 = std::move(i2); + swap(i1, i2); + return new std::polymorphic<Incomplete>(std::move(i1)); +} diff --git a/libstdc++-v3/testsuite/std/memory/polymorphic/invalid_neg.cc b/libstdc++-v3/testsuite/std/memory/polymorphic/invalid_neg.cc new file mode 100644 index 0000000..a01af3f --- /dev/null +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/invalid_neg.cc @@ -0,0 +1,28 @@ +// { dg-do compile { target c++26 } } + +#include <memory> + +// In every specialization polymorphic<T, Allocator>, if the type +// allocator_traits<Allocator>::value_type is not the same type as T, +// the program is ill-formed. +using T1 = std::polymorphic<int, std::allocator<long>>::value_type; // { dg-error "here" } + +// A program that instantiates the definition of the template +// polymorphic<T, Allocator> with a type for the T parameter that is +// a non-object type, an array type, in_place_t, +// a specialization of in_place_type_t, or a cv-qualified type is ill-formed. + +using T2 = std::polymorphic<int&>::value_type; // { dg-error "here" } + +using T3 = std::polymorphic<int[1]>::value_type; // { dg-error "here" } + +using T4 = std::polymorphic<std::in_place_t>::value_type; // { dg-error "here" } + +using T5 = std::polymorphic<std::in_place_type_t<int>>::value_type; // { dg-error "here" } + +using T6 = std::polymorphic<const int>::value_type; // { dg-error "here" } + +using T7 = std::polymorphic<volatile int>::value_type; // { dg-error "here" } + +// { dg-error "static assertion failed" "" { target *-*-* } 0 } +// { dg-prune-output "forming pointer to reference" } diff --git a/libstdc++-v3/testsuite/std/memory/polymorphic/move.cc b/libstdc++-v3/testsuite/std/memory/polymorphic/move.cc new file mode 100644 index 0000000..97e598e --- /dev/null +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/move.cc @@ -0,0 +1,232 @@ +// { dg-do run { target c++26 } } + +#include <memory> +#include <scoped_allocator> +#include <utility> +#include <vector> +#include <optional> + +#include <testsuite_hooks.h> +#include <testsuite_allocator.h> + +struct Base { + friend constexpr + bool operator==(const Base& lhs, const Base& rhs) + { return lhs.eq(rhs); } + +private: + constexpr virtual bool + eq(const Base& other) const = 0; +}; + +struct Derived : Base +{ + constexpr Derived() + : x(0), y(0), z(0) + { } + + constexpr Derived(int a, int b, int c) + : x(a), y(b), z(c) + { } + +private: + constexpr bool + eq(const Base& other) const override + { + if (auto op = dynamic_cast<const Derived*>(&other)) + return this->x == op->x && this->y == op->y && this->z == op->z; + return false; + } + + int x; + int y; + int z; +}; + +using __gnu_test::tracker_allocator; +using Counter = __gnu_test::tracker_allocator_counter; +using Polymorphic = std::polymorphic<Base, tracker_allocator<Base>>; +const Polymorphic val(std::in_place_type<Derived>, 1, 2, 3); + +void +verifyNoAllocations() +{ + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 0 ); +} + +void +test_ctor() +{ + std::optional<Polymorphic> src; + auto make = [&src] -> Polymorphic&& { + src.emplace(val); + Counter::reset(); + return std::move(*src); + }; + + Polymorphic i1(make()); + VERIFY( src->valueless_after_move() ); + VERIFY( *i1 == *val ); + verifyNoAllocations(); + + Polymorphic i2(std::allocator_arg, {}, make()); + VERIFY( src->valueless_after_move() ); + VERIFY( *i2 == *val ); + verifyNoAllocations(); +} + +void +test_assign() +{ + std::optional<Polymorphic> src; + auto make = [&src] -> Polymorphic&& { + src.emplace(val); + Counter::reset(); + return std::move(*src); + }; + + Polymorphic i1(std::in_place_type<Derived>); + + i1 = make(); + VERIFY( src->valueless_after_move() ); + VERIFY( *i1 == *val ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() >= sizeof(Derived) ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 1 ); + + auto(std::move(i1)); + i1 = make(); + VERIFY( *i1 == *val ); + VERIFY( src->valueless_after_move() ); + verifyNoAllocations(); +} + +void +test_swap() +{ + const Polymorphic val1(std::in_place_type<Derived>, 1, 2, 3); + const Polymorphic val2(std::in_place_type<Derived>, 2, 4, 6); + + Polymorphic i1(val1); + Polymorphic i2(val2); + Counter::reset(); + i1.swap(i2); + VERIFY( *i2 == *val1 ); + VERIFY( *i1 == *val2 ); + verifyNoAllocations(); + + auto(std::move(i1)); + + Counter::reset(); + i1.swap(i2); + VERIFY( *i1 == *val1 ); + VERIFY( i2.valueless_after_move() ); + verifyNoAllocations(); +} + +void +test_valueless() +{ + auto e = [] { + Polymorphic res(std::in_place_type<Derived>); + auto(std::move(res)); + Counter::reset(); + return res; + }; + + Polymorphic i1(e()); + VERIFY( i1.valueless_after_move() ); + verifyNoAllocations(); + + Polymorphic i2(std::allocator_arg, {}, e()); + VERIFY( i2.valueless_after_move() ); + verifyNoAllocations(); + + Polymorphic i3(val); + i3 = e(); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() >= sizeof(Derived) ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 1 ); + + i3 = e(); + verifyNoAllocations(); +} + +constexpr void +test_constexpr() +{ + using Polymorphic = std::polymorphic<Base, __gnu_test::uneq_allocator<Base>>; + const Polymorphic val(std::in_place_type<Derived>, 1, 2, 3); + + std::optional<Polymorphic> src; + auto make = [&src, &val] -> Polymorphic&& { + src.emplace(val); + return std::move(*src); + }; + + Polymorphic i1(make()); + VERIFY( src->valueless_after_move() ); + VERIFY( *i1 == *val ); + + Polymorphic i2(std::allocator_arg, {}, make()); + VERIFY( src->valueless_after_move() ); + VERIFY( *i2 == *val ); + + i1 = make(); + VERIFY( src->valueless_after_move() ); + VERIFY( *i1 == *val ); + + auto(std::move(i1)); + i1 = make(); + VERIFY( *i1 == *val ); + VERIFY( src->valueless_after_move() ); + + const Polymorphic val1(std::in_place_type<Derived>, 1, 2, 3); + const Polymorphic val2(std::in_place_type<Derived>, 2, 4, 6); + + Polymorphic s1(val1); + Polymorphic s2(val2); + s1.swap(s2); + VERIFY( *s2 == *val1 ); + VERIFY( *s1 == *val2 ); + + auto(std::move(s1)); + s1.swap(s2); + VERIFY( *s1 == *val1 ); + VERIFY( s2.valueless_after_move() ); + + auto e = [] { + Polymorphic res(std::in_place_type<Derived>); + auto(std::move(res)); + return res; + }; + + Polymorphic e1(e()); + VERIFY( e1.valueless_after_move() ); + + Polymorphic e2(std::allocator_arg, {}, e()); + VERIFY( e2.valueless_after_move() ); + + Polymorphic e3(val); + e3 = e(); + e3 = e(); +} + +int main() +{ + test_ctor(); + test_assign(); + test_swap(); + test_valueless(); + test_constexpr(); + + static_assert([] { + test_constexpr(); + return true; + }()); +} diff --git a/libstdc++-v3/testsuite/std/memory/polymorphic/move_alloc.cc b/libstdc++-v3/testsuite/std/memory/polymorphic/move_alloc.cc new file mode 100644 index 0000000..490bffb --- /dev/null +++ b/libstdc++-v3/testsuite/std/memory/polymorphic/move_alloc.cc @@ -0,0 +1,333 @@ +// { dg-do run { target c++26 } } + +#include <memory> +#include <scoped_allocator> +#include <utility> +#include <vector> +#include <optional> + +#include <testsuite_hooks.h> +#include <testsuite_allocator.h> + +struct Base { + friend constexpr + bool operator==(const Base& lhs, const Base& rhs) + { return lhs.eq(rhs); } + + virtual constexpr int + get_alloc_personality() const + { return -1; } + +private: + constexpr virtual bool + eq(const Base& other) const = 0; +}; + +template<typename T, typename Allocator> +struct VecDerived : Base, std::vector<T, Allocator> +{ + using VecBase = std::vector<T, Allocator>; + + using VecBase::VecBase; + + constexpr int + get_alloc_personality() const override + { return this->get_allocator().get_personality(); } + +private: + + constexpr bool + eq(const Base& other) const override + { + if (auto op = dynamic_cast<const VecDerived*>(&other)) + return *static_cast<const VecBase*>(this) + == *static_cast<const VecBase*>(op); + return false; + } +}; + +using __gnu_test::propagating_allocator; +using __gnu_test::tracker_allocator; +using Counter = __gnu_test::tracker_allocator_counter; + +void +verifyNoAllocations() +{ + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 0 ); +} + +template<bool Propagate> +void +test_ctor() +{ + using PropAlloc = propagating_allocator<int, Propagate>; + using Vector = VecDerived<int, PropAlloc>; + using ScopedAlloc = std::scoped_allocator_adaptor< + propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>, + PropAlloc>; + using Polymorphic = std::polymorphic<Vector, ScopedAlloc>; + + const Polymorphic val(std::in_place_type<Vector>, {1, 2, 3}); + std::optional<Polymorphic> src; + auto make = [&val, &src] -> Polymorphic&& { + src.emplace(std::allocator_arg, ScopedAlloc{11, 22}, val); + Counter::reset(); + return std::move(*src); + }; + + Polymorphic i1(make()); + VERIFY( src->valueless_after_move() ); + VERIFY( *i1 == *val ); + VERIFY( i1->get_alloc_personality() == 22 ); + VERIFY( i1.get_allocator().get_personality() == 11 ); + verifyNoAllocations(); + + Polymorphic i2(std::allocator_arg, ScopedAlloc{11, 22}, make()); + VERIFY( src->valueless_after_move() ); + VERIFY( *i2 == *val ); + VERIFY( i2->get_alloc_personality() == 22 ); + VERIFY( i2.get_allocator().get_personality() == 11 ); + verifyNoAllocations(); + + Polymorphic i3(std::allocator_arg, ScopedAlloc{33, 44}, make()); + // We move-from contained object + VERIFY( !src->valueless_after_move() ); + VERIFY( *i3 == *val ); + VERIFY( i3->get_alloc_personality() == 44 ); + VERIFY( i3.get_allocator().get_personality() == 33 ); + VERIFY( Counter::get_allocation_count() >= sizeof(Vector) ); + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 2 ); + VERIFY( Counter::get_destruct_count() == 0 ); +} + +template<bool Propagate> +void +test_assign() +{ + using PropAlloc = propagating_allocator<int, Propagate>; + using Vector = VecDerived<int, PropAlloc>; + using ScopedAlloc = std::scoped_allocator_adaptor< + propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>, + PropAlloc>; + using Polymorphic = std::polymorphic<Vector, ScopedAlloc>; + + const Polymorphic val(std::in_place_type<Vector>, {1, 2, 3}); + std::optional<Polymorphic> src; + auto make = [&val, &src] -> Polymorphic&& { + src.emplace(std::allocator_arg, ScopedAlloc{11, 22}, val); + Counter::reset(); + return std::move(*src); + }; + + Counter::reset(); + Polymorphic i1(std::allocator_arg, ScopedAlloc{11, 22}); + const std::size_t holderSize = Counter::get_allocation_count(); + VERIFY( holderSize >= sizeof(Vector) ); + + i1 = make(); + VERIFY( src->valueless_after_move() ); + VERIFY( *i1 == *val ); + VERIFY( i1->get_alloc_personality() == 22 ); + VERIFY( i1.get_allocator().get_personality() == 11 ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() == holderSize ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 1 ); + + Polymorphic i2(std::allocator_arg, ScopedAlloc{33, 44}); + + i2 = make(); + VERIFY( *i2 == *val ); + if (Propagate) + { + VERIFY( src->valueless_after_move() ); + VERIFY( i2->get_alloc_personality() == 22 ); + VERIFY( i2.get_allocator().get_personality() == 11 ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + } + else + { + // We allocate new holder and move-from contained object + VERIFY( !src->valueless_after_move() ); + VERIFY( i2->get_alloc_personality() == 44 ); + VERIFY( i2.get_allocator().get_personality() == 33 ); + VERIFY( Counter::get_allocation_count() == holderSize ); + VERIFY( Counter::get_construct_count() == 2 ); + } + VERIFY( Counter::get_deallocation_count() == holderSize ); + VERIFY( Counter::get_destruct_count() == 1 ); + + Polymorphic i3(std::allocator_arg, ScopedAlloc{11, 22}, + std::in_place_type<Vector>); + auto(std::move(i3)); + + i3 = make(); + VERIFY( *i3 == *val ); + VERIFY( src->valueless_after_move() ); + VERIFY( i3->get_alloc_personality() == 22 ); + VERIFY( i3.get_allocator().get_personality() == 11 ); + verifyNoAllocations(); + + Polymorphic i4(std::allocator_arg, ScopedAlloc{33, 44}, + std::in_place_type<Vector>); + auto(std::move(i4)); + + i4 = make(); + VERIFY( *i4 == *val ); + if (Propagate) + { + VERIFY( src->valueless_after_move() ); + VERIFY( i4->get_alloc_personality() == 22 ); + VERIFY( i4.get_allocator().get_personality() == 11 ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_construct_count() == 0 ); + } + else + { + // We allocate new holder and move-from contained object + VERIFY( !src->valueless_after_move() ); + VERIFY( i4->get_alloc_personality() == 44 ); + VERIFY( i4.get_allocator().get_personality() == 33 ); + VERIFY( Counter::get_allocation_count() == holderSize ); + VERIFY( Counter::get_construct_count() == 2 ); + } + VERIFY( Counter::get_deallocation_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 0 ); +} + +template<bool Propagate> +void +test_swap() +{ + using PropAlloc = propagating_allocator<int, Propagate>; + using Vector = VecDerived<int, PropAlloc>; + using ScopedAlloc = std::scoped_allocator_adaptor< + propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>, + PropAlloc>; + using Polymorphic = std::polymorphic<Vector, ScopedAlloc>; + + const Polymorphic val1(std::in_place_type<Vector>, {1, 2, 3}); + const Polymorphic val2(std::in_place_type<Vector>, {2, 4, 6}); + + Polymorphic i1(std::allocator_arg, ScopedAlloc{11, 22}, val1); + Polymorphic i2(std::allocator_arg, ScopedAlloc{11, 22}, val2); + Counter::reset(); + i1.swap(i2); + VERIFY( *i2 == *val1 ); + VERIFY( *i1 == *val2 ); + verifyNoAllocations(); + + auto(std::move(i1)); + + Counter::reset(); + i1.swap(i2); + VERIFY( *i1 == *val1 ); + VERIFY( i2.valueless_after_move() ); + verifyNoAllocations(); + + if (!Propagate) + return; + + Polymorphic i3(std::allocator_arg, ScopedAlloc{33, 44}, val2); + Counter::reset(); + i1.swap(i3); + VERIFY( *i1 == *val2 ); + VERIFY( i1->get_alloc_personality() == 44 ); + VERIFY( i1.get_allocator().get_personality() == 33 ); + VERIFY( *i3 == *val1 ); + VERIFY( i3->get_alloc_personality() == 22 ); + VERIFY( i3.get_allocator().get_personality() == 11 ); + verifyNoAllocations(); + + i1.swap(i2); + VERIFY( i1.valueless_after_move() ); + VERIFY( i1.get_allocator().get_personality() == 11 ); + VERIFY( *i2 == *val2 ); + VERIFY( i2->get_alloc_personality() == 44 ); + VERIFY( i2.get_allocator().get_personality() == 33 ); + verifyNoAllocations(); +} + +template<bool Propagate> +void +test_valueless() +{ + using PropAlloc = propagating_allocator<int, Propagate>; + using Vector = VecDerived<int, PropAlloc>; + using ScopedAlloc = std::scoped_allocator_adaptor< + propagating_allocator<Vector, Propagate, tracker_allocator<Vector>>, + PropAlloc>; + using Polymorphic = std::polymorphic<Vector, ScopedAlloc>; + + auto e = [] { + Polymorphic res(std::allocator_arg, ScopedAlloc{11, 22}, + std::in_place_type<Vector>); + auto(std::move(res)); + Counter::reset(); + return res; + }; + + Polymorphic i1(e()); + VERIFY( i1.valueless_after_move() ); + VERIFY( i1.get_allocator().get_personality() == 11 ); + verifyNoAllocations(); + + Polymorphic i2(std::allocator_arg, ScopedAlloc{33, 44}, e()); + VERIFY( i2.valueless_after_move() ); + VERIFY( i2.get_allocator().get_personality() == 33 ); + verifyNoAllocations(); + + Polymorphic i3(std::allocator_arg, ScopedAlloc{33, 44}); + + i3 = e(); + VERIFY( i3.valueless_after_move() ); + if (Propagate) + VERIFY( i3.get_allocator().get_personality() == 11 ); + else + VERIFY( i3.get_allocator().get_personality() == 33 ); + VERIFY( Counter::get_allocation_count() == 0 ); + VERIFY( Counter::get_deallocation_count() >= sizeof(Vector) ); + VERIFY( Counter::get_construct_count() == 0 ); + VERIFY( Counter::get_destruct_count() == 1 ); + + i2 = e(); + VERIFY( i2.valueless_after_move() ); + if (Propagate) + VERIFY( i2.get_allocator().get_personality() == 11 ); + else + VERIFY( i2.get_allocator().get_personality() == 33 ); + verifyNoAllocations(); + + i3.swap(i2); + VERIFY( i2.valueless_after_move() ); + VERIFY( i1.valueless_after_move() ); + verifyNoAllocations(); + + if (!Propagate) + return; + + Polymorphic i4(std::allocator_arg, ScopedAlloc{33, 44}, e()); + i4.swap(i1); + verifyNoAllocations(); +} + +template<bool Propagate> +void +test_all() +{ + test_ctor<Propagate>(); + test_assign<Propagate>(); + test_swap<Propagate>(); + test_valueless<Propagate>(); +} + +int main() +{ + test_all<true>(); + test_all<false>(); +} diff --git a/libstdc++-v3/testsuite/std/memory/start_lifetime_as/start_lifetime_as.cc b/libstdc++-v3/testsuite/std/memory/start_lifetime_as/start_lifetime_as.cc new file mode 100644 index 0000000..c32e4de --- /dev/null +++ b/libstdc++-v3/testsuite/std/memory/start_lifetime_as/start_lifetime_as.cc @@ -0,0 +1,94 @@ +// { dg-do run { target c++23 } } + +#include <bit> +#include <memory> + +#include <testsuite_hooks.h> +#include <testsuite_allocator.h> + +struct S { int a; int b; }; +struct T { long long c; }; + +template<typename S, typename T> +void +test01() +{ +} + +template<typename S, typename T> +requires (sizeof(S) == sizeof(T)) +void +test01() +{ + union U { unsigned char a[sizeof(S)]; S b; T c; } u; + u.a[0] = 1; + T v = std::bit_cast<T> (S{1, 2}); + union V { unsigned char a[3 * sizeof(S)]; S b[3]; T c[3]; } w; + T x = std::bit_cast<T> (S{3, 4}); + T y = std::bit_cast<T> (S{5, 6}); + S* d = std::start_lifetime_as<S>(reinterpret_cast<void*>(&u.a)); + d->a = 1; + d->b = 2; + T* e = std::start_lifetime_as<T>(reinterpret_cast<void*>(d)); + VERIFY( e->c == v.c ); + const T* f = std::start_lifetime_as<T>(reinterpret_cast<const void*>(d)); + VERIFY( f->c == v.c ); + volatile T* g + = std::start_lifetime_as<T>(reinterpret_cast<volatile void*>(d)); + VERIFY( g->c == v.c ); + const volatile T* h + = std::start_lifetime_as<T>(reinterpret_cast<const volatile void*>(d)); + VERIFY( h->c == v.c ); + S* i = std::start_lifetime_as_array<S>(reinterpret_cast<void*>(&w.a), 3); + i[0].a = 1; + i[0].b = 2; + i[1].a = 3; + i[1].b = 4; + i[2].a = 5; + i[2].b = 6; + T* j = std::start_lifetime_as_array<T>(reinterpret_cast<void*>(i), 3); + VERIFY( j[0].c == v.c && j[1].c == x.c && j[2].c == y.c ); + const T* k + = std::start_lifetime_as_array<T>(reinterpret_cast<const void*>(i), 3); + VERIFY( k[0].c == v.c && k[1].c == x.c && k[2].c == y.c ); + volatile T* l + = std::start_lifetime_as_array<T>(reinterpret_cast<volatile void*>(i), 3); + VERIFY( l[0].c == v.c && l[1].c == x.c && l[2].c == y.c ); + const volatile T* m + = std::start_lifetime_as_array<T>(reinterpret_cast<const volatile void*>(i), + 3); + VERIFY( m[0].c == v.c && m[1].c == x.c && m[2].c == y.c ); + T* n = std::start_lifetime_as_array<T>(static_cast<void*>(nullptr), 0); + VERIFY( n == nullptr ); + const T* o + = std::start_lifetime_as_array<T>(static_cast<const void*>(nullptr), 0); + VERIFY( o == nullptr ); + volatile T* p + = std::start_lifetime_as_array<T>(static_cast<volatile void*>(nullptr), 0); + VERIFY( p == nullptr ); + const volatile T* q + = std::start_lifetime_as_array<T>(static_cast<const volatile void*>(nullptr), + 0); + VERIFY( q == nullptr ); + VERIFY( std::start_lifetime_as_array<T>(reinterpret_cast<void*>(&w.a), 0) + == &w.c[0] ); + VERIFY( std::start_lifetime_as_array<T>(reinterpret_cast<const void*>(&w.a), 0) + == static_cast<const T*>(&w.c[0]) ); + VERIFY( std::start_lifetime_as_array<T>(reinterpret_cast<volatile void*>(&w.a), + 0) + == static_cast<volatile T*>(&w.c[0]) ); + VERIFY( std::start_lifetime_as_array<T>(reinterpret_cast<const volatile void*>(&w.a), + 0) + == static_cast<const volatile T*>(&w.c[0]) ); + static const S r[] = { { 5, 6 }, { 3, 4 } }; + const T* s = std::start_lifetime_as<T>(&r[1]); + VERIFY( s->c == x.c ); + const T* t = std::start_lifetime_as_array<T>(&r[0], 2); + VERIFY( t[0].c == y.c && t[1].c == x.c ); +} + +int +main() +{ + test01<S, T>(); +} diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent/1.cc index 085cd4a..0a5c67f 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent/1.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent/1.cc @@ -114,6 +114,18 @@ test04() return true; } +constexpr bool +test05() +{ + // PR libstdc++/121956 + int a[2]{}; + __gnu_test::test_random_access_range r(a); + auto v = r | views::pairwise; + static_assert( std::is_same_v<ranges::range_value_t<decltype(v)>, + std::tuple<int, int>> ); + return true; +} + int main() { @@ -121,4 +133,5 @@ main() static_assert(test02()); static_assert(test03()); static_assert(test04()); + static_assert(test05()); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc index 772e4b3..6890618 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/adjacent_transform/1.cc @@ -113,6 +113,47 @@ test04() static_assert( requires { x | views::pairwise_transform(move_only{}); } ); } +template<size_t FuncSize, typename Fn> +void +test05(Fn f) +{ + int x[] = {1,2,3,4,5,6}; + auto v = x | views::pairwise_transform(f); + static_assert(sizeof(v.begin()) == 2*sizeof(int*) + FuncSize); +} + +void +test05all() +{ + test05<0>(std::equal_to<>()); + test05<0>(std::equal_to<>()); + test05<0>(std::not_equal_to<>()); + test05<0>(std::greater<>()); + test05<0>(std::less<>()); + test05<0>(std::greater_equal<>()); + test05<0>(std::less_equal<>()); + + test05<0>(std::ranges::equal_to()); + test05<0>(std::ranges::not_equal_to()); + test05<0>(std::ranges::greater()); + test05<0>(std::ranges::less()); + test05<0>(std::ranges::greater_equal()); + test05<0>(std::ranges::less_equal()); + + test05<0>(std::plus<>()); + test05<0>(std::minus<>()); + test05<0>(std::multiplies<>()); + test05<0>(std::divides<>()); + test05<0>(std::modulus<>()); + + test05<0>(std::logical_and<>()); + test05<0>(std::logical_or<>()); + + test05<0>(std::bit_and<>()); + test05<0>(std::bit_or<>()); + test05<0>(std::bit_xor<>()); +} + int main() { diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc index 4583fb0..57db806 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/drop.cc @@ -243,7 +243,7 @@ test08() long b[10]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; test_range<long, ra_test_wrapper> rb(b); - ranges::subrange sized = {rb.begin(), rb.begin()+6}; + ranges::subrange sized = {rb.begin(), ranges::next(rb.begin(), 6)}; using Sized = decltype(sized); static_assert( ranges::random_access_range<Sized> ); static_assert( ranges::sized_range<Sized> ); diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc index 2861115..db77490 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join.cc @@ -233,6 +233,24 @@ test14() VERIFY( ranges::equal(v | views::join, (int[]){1, 2, 3}) ); } +void +test15() +{ + // PR libstdc++/119962 - __maybe_present_t misses initialization + constexpr decltype(views::join(views::single(views::single(0))).begin()) it; +} + +void +test16() +{ + // PR libstdc++/121804 - join_view::iterator::_M_get_inner should be noexcept + std::vector<std::vector<int>> vv; + ranges::join_view j{vv}; + auto jit = j.begin(); + static_assert(noexcept(ranges::iter_move(jit))); + static_assert(noexcept(ranges::iter_swap(jit, jit))); +} + int main() { @@ -250,4 +268,6 @@ main() test12(); test13(); test14(); + test15(); + test16(); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/join_with/1.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/join_with/1.cc index 8ab30a5..4d55c9d 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/join_with/1.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/join_with/1.cc @@ -94,6 +94,13 @@ test04() return true; } +void +test05() +{ + // PR libstdc++/119962 - __maybe_present_t misses initialization + constexpr decltype(views::join_with(views::single(views::single(0)), 0).begin()) it; +} + int main() { @@ -105,4 +112,5 @@ main() #else VERIFY(test04()); #endif + test05(); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc index 81fc60b..321ae27 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/lazy_split.cc @@ -232,6 +232,13 @@ test12() return true; } +void +test13() +{ + // PR libstdc++/119962 - __maybe_present_t misses initialization + constexpr decltype(views::lazy_split(views::single(0), 0).begin()) it; +} + int main() { @@ -247,4 +254,5 @@ main() test10(); test11(); static_assert(test12()); + test13(); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc index 1673772..9584c57 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/take.cc @@ -118,6 +118,20 @@ test06() static_assert(!requires { views::all | take; }); } +void +test07() +{ + // PR libstdc++/111550 + struct Five { + operator int() & { return 5; } + operator int() && = delete; + }; + auto take_five = views::take(Five{}); + auto r = take_five(views::iota(0)); + auto take_five_piped = views::take(Five{}) | views::transform(std::identity{}); + auto s = take_five_piped(views::iota(0)); +} + int main() { @@ -127,4 +141,5 @@ main() test04(); test05(); test06(); + test07(); } diff --git a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc index 1788db1..3a21a1f 100644 --- a/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc +++ b/libstdc++-v3/testsuite/std/ranges/adaptors/transform.cc @@ -18,6 +18,7 @@ // { dg-do run { target c++20 } } #include <algorithm> +#include <cstdint> #include <ranges> #include <testsuite_hooks.h> #include <testsuite_iterators.h> @@ -28,12 +29,12 @@ using __gnu_test::random_access_iterator_wrapper; namespace ranges = std::ranges; namespace views = std::ranges::views; +template<typename Fn> void -test01() +test01(Fn f) { int x[] = {1,2,3,4,5}; - auto is_odd = [] (int i) { return i%2==1; }; - auto v = x | views::transform(is_odd); + auto v = x | views::transform(f); VERIFY( ranges::equal(v, (int[]){1,0,1,0,1}) ); using R = decltype(v); static_assert(std::same_as<bool, decltype(*ranges::begin(v))>); @@ -42,24 +43,51 @@ test01() static_assert(ranges::random_access_range<R>); } +void +test01a() +{ + auto is_odd = [] (int i) { return i%2==1; }; + test01(is_odd); +} + +void +test01b() +{ +#if __cpp_static_call_operator >= 202207L + auto is_odd = [] (int i) static { return i%2==1; }; + test01(is_odd); +#endif +} + +void +test01c() +{ + bool(*is_odd)(int) = [] (int i) { return i%2==1; }; + test01(is_odd); +} + struct X { int i,j; + int& first() { return i; } }; +template<size_t FuncSize, typename Fn> void -test02() +test02(Fn f) { X x[] = {{1,2},{3,4},{5,6},{7,8},{9,10}}; test_range<X, random_access_iterator_wrapper> rx(x); - auto v = rx | views::transform(&X::i); + auto v = rx | views::transform(f); VERIFY( ranges::size(v) == 5 ); VERIFY( ranges::distance(v.begin(), v.end()) == 5 ); VERIFY( ranges::equal(v, (int[]){1,3,5,7,9}) ); VERIFY( ranges::equal(v | views::reverse, (int[]){9,7,5,3,1}) ); using R = decltype(v); + using It = ranges::iterator_t<R>; static_assert(std::same_as<int&, decltype(*ranges::begin(v))>); - static_assert(std::same_as<int, std::iter_value_t<ranges::iterator_t<R>>>); + static_assert(std::same_as<int, std::iter_value_t<It>>); + static_assert(sizeof(It) == sizeof(rx.begin()) + FuncSize); static_assert(ranges::view<R>); static_assert(ranges::sized_range<R>); static_assert(!ranges::common_range<R>); @@ -67,6 +95,73 @@ test02() } void +test02a() +{ test02<sizeof(int X::*)>(&X::i); } + +void +test02b() +{ test02<sizeof(int(X::*)())>(&X::first); } + +void +test02c() +{ + auto first = [](X& x) -> int& { return x.i; }; + test02<sizeof(void*)>(first); +} + +void +test02d() +{ +#if __cpp_static_call_operator >= 202207L + auto first = [](X& x) static -> int& { return x.i; }; + test02<0>(first); +#endif +} + +void +test02e() +{ + int&(*fptr)(X&) = [](X& x) -> int& { return x.i; }; + test02<sizeof(void(*)())>(fptr); +} + +void +test02f() +{ +#if __cpp_static_call_operator >= 202207L + struct PickStatic + { + static constexpr int& + operator()(X& x) + { return x.i; } + + constexpr int + operator()(char*) const + { return 0; }; + }; + test02<0>(PickStatic{}); +#endif +} + +void +test02g() +{ +#if __cpp_static_call_operator >= 202207L + struct PickObject + { + constexpr int& + operator()(X& x) const + { return x.i; } + + static constexpr int + operator()(char*) + { return 0; }; + }; + test02<sizeof(void*)>(PickObject{}); +#endif +} + +void test03() { auto id = [] (int i) { return i; }; @@ -227,11 +322,75 @@ test11() static_assert(std::same_as<cat, std::random_access_iterator_tag>); } +void +test12() +{ + struct Obfuscate + { + int operator()(int x) const + { return x + reinterpret_cast<std::uintptr_t>(this); } + }; + + int x[]{1, 2, 3, 4, 5}; + auto v = x | views::transform(Obfuscate{}); + VERIFY( ranges::equal(v, v) ); +}; + +void +test13() +{ +#if __cpp_static_call_operator >= 202207L + struct StaticWins { + static int operator()(int i) { return 0; } + int operator()(float f) const { return 1; } + }; + + int x[]{1, 2, 3, 4, 5}; + auto vs = x | views::transform(StaticWins{}); + VERIFY( vs.front() == 0 ); + static_assert( sizeof(vs.begin()) == sizeof(int*) ); + + struct MemberWins { + static int operator()(float f) { return 0; } + int operator()(int i) const { return 1; } + }; + + auto vm = x | views::transform(MemberWins{}); + VERIFY( vm.front() == 1 ); + static_assert( sizeof(vm.begin()) > sizeof(int*) ); +#endif +} + +template<size_t FuncSize, typename Fn> +void +test14(Fn f) +{ + int x[] = {1,2,3,4,5,6}; + auto v = x | views::transform(std::negate<>()); + static_assert(sizeof(v.begin()) == sizeof(int*) + FuncSize); +} + +void +test14all() +{ + test14<0>(std::identity()); + test14<0>(std::negate<>()); + test14<0>(std::bit_not<>()); +} + int main() { - test01(); - test02(); + test01a(); + test01b(); + test01c(); + test02a(); + test02b(); + test02c(); + test02d(); + test02e(); + test02f(); + test02g(); test03(); test04(); test05(); @@ -241,4 +400,7 @@ main() test09(); test10(); test11(); + test12(); + test13(); + test14all(); } diff --git a/libstdc++-v3/testsuite/std/ranges/concat/1.cc b/libstdc++-v3/testsuite/std/ranges/concat/1.cc index 1672191..f78ed08 100644 --- a/libstdc++-v3/testsuite/std/ranges/concat/1.cc +++ b/libstdc++-v3/testsuite/std/ranges/concat/1.cc @@ -99,6 +99,18 @@ test04() using type = decltype(v); } +void +test05() +{ + // PR libstdc++/120934 - views::concat is ill-formed depending on argument order + auto v1 = views::single(1); + std::vector<int> vec = {2, 3}; + auto v2 = views::join(views::transform(vec, views::single)); + + static_assert( ranges::range<decltype(views::concat(v1, v2))> ); + static_assert( ranges::range<decltype(views::concat(v2, v1))> ); +} + int main() { @@ -107,4 +119,5 @@ main() test02(); test03(); test04(); + test05(); } diff --git a/libstdc++-v3/testsuite/std/ranges/indices/1.cc b/libstdc++-v3/testsuite/std/ranges/indices/1.cc new file mode 100644 index 0000000..038b38f --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/indices/1.cc @@ -0,0 +1,45 @@ +// { dg-do run { target c++26 } } + +#include <testsuite_hooks.h> + +#include <ranges> +#include <type_traits> +#include <stddef.h> + +template <typename T> +constexpr bool test(T n) { + auto indices_view = std::ranges::views::indices(n); + static_assert( + std::is_same_v<T, std::ranges::range_value_t<decltype(indices_view)>>); + static_assert(noexcept(std::ranges::views::indices(n))); + + VERIFY(indices_view.size() == n); + for (T i = 0; i < n; ++i) VERIFY(indices_view[i] == i); + + return true; +} + +int main() { + VERIFY(test<int>(41)); + static_assert(test<int>(41)); + VERIFY(test<short>(42)); + static_assert(test<short>(42)); + VERIFY(test<long>(43)); + static_assert(test<long>(43)); + VERIFY(test<size_t>(44)); + static_assert(test<size_t>(44)); +} + +template<typename T> +constexpr size_t test_wider(T n) +{ + // If indices(n) works, try again with ranges::distance(indices(n)), + // which will be a wider type, until we get to an unsupported type. + // This verifies that indices(n) is SFINAE-friendly, because otherwise we + // would get a hard error outside the immediate context checked by requires. + if constexpr (requires { std::views::indices(n); }) + return test_wider(std::ranges::distance(std::views::indices(n))); + return sizeof(T); +} + +static_assert(test_wider(0) > sizeof(long long)); diff --git a/libstdc++-v3/testsuite/std/ranges/iota/max_size_type.cc b/libstdc++-v3/testsuite/std/ranges/iota/max_size_type.cc index 3e6f954..fbd783b 100644 --- a/libstdc++-v3/testsuite/std/ranges/iota/max_size_type.cc +++ b/libstdc++-v3/testsuite/std/ranges/iota/max_size_type.cc @@ -352,6 +352,9 @@ static_assert(numeric_limits<max_size_t>::is_specialized); static_assert(!numeric_limits<max_size_t>::is_signed); static_assert(numeric_limits<max_size_t>::is_integer); static_assert(numeric_limits<max_size_t>::is_exact); +static_assert(numeric_limits<max_size_t>::is_bounded); +static_assert(numeric_limits<max_size_t>::is_modulo); +static_assert(numeric_limits<max_size_t>::radix == 2); // We can't unconditionally use numeric_limits here because __int128 is an // integral type only in GNU mode. #if __SIZEOF_INT128__ @@ -379,6 +382,9 @@ static_assert(numeric_limits<max_diff_t>::is_specialized); static_assert(numeric_limits<max_diff_t>::is_signed); static_assert(numeric_limits<max_diff_t>::is_integer); static_assert(numeric_limits<max_diff_t>::is_exact); +static_assert(numeric_limits<max_diff_t>::is_bounded); +static_assert(!numeric_limits<max_diff_t>::is_modulo); +static_assert(numeric_limits<max_diff_t>::radix == 2); static_assert(numeric_limits<max_diff_t>::digits == numeric_limits<max_size_t>::digits - 1); static_assert(numeric_limits<max_diff_t>::digits10 @@ -400,6 +406,38 @@ static_assert(max_diff_t(max_size_t(1) << (numeric_limits<max_size_t>::digits-1)) == numeric_limits<max_diff_t>::min()); +template <typename integer_class> +constexpr bool verify_numeric_limits_values_not_meaningful_for = true + && (numeric_limits<integer_class>::max_digits10 == 0) + && (numeric_limits<integer_class>::min_exponent == 0) + && (numeric_limits<integer_class>::min_exponent10 == 0) + && (numeric_limits<integer_class>::max_exponent == 0) + && (numeric_limits<integer_class>::max_exponent10 == 0) + && !numeric_limits<integer_class>::is_iec559 + && !numeric_limits<integer_class>::has_infinity + && !numeric_limits<integer_class>::has_quiet_NaN + && !numeric_limits<integer_class>::has_signaling_NaN + && !numeric_limits<integer_class>::has_denorm_loss + && !numeric_limits<integer_class>::tinyness_before + && (numeric_limits<integer_class>::has_denorm == std::denorm_absent) + && (numeric_limits<integer_class>::round_style == std::round_toward_zero) + && (numeric_limits<integer_class>::denorm_min() == 0) + && (numeric_limits<integer_class>::epsilon() == 0) + && (numeric_limits<integer_class>::round_error() == 0) + && (numeric_limits<integer_class>::infinity() == 0) + && (numeric_limits<integer_class>::quiet_NaN() == 0) + && (numeric_limits<integer_class>::signaling_NaN() == 0); + +static_assert(verify_numeric_limits_values_not_meaningful_for<max_size_t>); +static_assert(verify_numeric_limits_values_not_meaningful_for<max_diff_t>); + +// Verify that the types are structural types and can therefore be used +// as NTTP types. +template<max_size_t V> struct Su { static_assert(V*V == V+132); }; +template<max_diff_t V> struct Ss { static_assert(V*V == V+132); }; +template struct Su<12>; +template struct Ss<12>; + int main() { diff --git a/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc b/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc index 9a0ad38..8524a14 100644 --- a/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc +++ b/libstdc++-v3/testsuite/std/ranges/zip_transform/1.cc @@ -132,6 +132,97 @@ test04() static_assert( requires { views::zip_transform(move_only{}, x, x); } ); } +struct X +{ + int i; + constexpr int add(int b) const + { return i+b; } +}; + +template<size_t ExtraSize, typename Fn> +constexpr bool +test05(Fn f) +{ + using namespace __gnu_test; + X x[] = {{1},{2},{3},{4},{5}}; + int y[] = {500,400,300,200,100}; + test_range<X, random_access_iterator_wrapper> rx(x); + test_range<int, random_access_iterator_wrapper> ry(y); + + auto v = views::zip_transform(f, rx, ry); + VERIFY( ranges::size(v) == 5 ); + VERIFY( ranges::distance(v.begin(), v.end()) == 5 ); + VERIFY( ranges::equal(v, (int[]){501,402,303,204,105}) ); + VERIFY( ranges::equal(v | views::reverse, (int[]){105,204,303,402,501}) ); + using R = decltype(v); + using It = ranges::iterator_t<R>; + static_assert(std::same_as<int, decltype(*ranges::begin(v))>); + static_assert(std::same_as<int, std::iter_value_t<It>>); + static_assert(sizeof(It) == sizeof(rx.begin()) + sizeof(ry.begin()) + ExtraSize); + static_assert(ranges::view<R>); + static_assert(ranges::sized_range<R>); + static_assert(ranges::common_range<R>); + static_assert(ranges::random_access_range<R>); + return true; +} + +constexpr bool +test05a() +{ + auto add = [](const X& x, int v) { return x.i + v; }; + return test05<sizeof(void*)>(add); +} + +constexpr bool +test05b() +{ + auto add = [](const X& x, int v) static { return x.i + v; }; + return test05<0>(add); +} + +constexpr bool +test05c() +{ + int(*ptr)(const X&, int) = [](const X& x, int v) { return x.i + v; }; + return test05<sizeof(void(*)())>(ptr); +} + +constexpr bool +test05d() +{ return test05<sizeof(int(X::*)())>(&X::add); } + +constexpr bool +test05e() +{ + struct PickStatic + { + static constexpr int + operator()(const X& x1, int v) + { return x1.i + v; } + + constexpr int + operator()(int x, int y) const + { return x + y; }; + }; + return test05<0>(PickStatic{}); +} + +constexpr bool +test05f() +{ + struct PickObject + { + constexpr int + operator()(const X& x1, int v) const + { return x1.i + v; } + + static constexpr int + operator()(int x, int y) + { return x + y; }; + }; + return test05<sizeof(void*)>(PickObject{}); +} + int main() { @@ -139,4 +230,10 @@ main() static_assert(test02()); static_assert(test03()); test04(); + static_assert(test05a()); + static_assert(test05b()); + static_assert(test05c()); + static_assert(test05d()); + static_assert(test05e()); + static_assert(test05f()); } diff --git a/libstdc++-v3/testsuite/std/time/clock/local/io.cc b/libstdc++-v3/testsuite/std/time/clock/local/io.cc index b4d562f..6efd81a 100644 --- a/libstdc++-v3/testsuite/std/time/clock/local/io.cc +++ b/libstdc++-v3/testsuite/std/time/clock/local/io.cc @@ -89,6 +89,9 @@ test_format() s = std::format("{}", local_seconds{}); VERIFY( s == "1970-01-01 00:00:00" ); + + s = std::format("{}", local_days{}); // PR libstdc++/120293 + VERIFY( s == "1970-01-01" ); } void @@ -122,6 +125,14 @@ test_parse() VERIFY( tp.time_since_epoch() == 0s ); } +// LWG 4257. Stream insertion for chrono::local_time should be constrained +template<typename T> +concept ostream_insertable = requires (std::ostream& o, const T& t) { o << t; }; +using D = std::chrono::duration<double>; +static_assert( ostream_insertable<std::chrono::local_days> ); +static_assert( ostream_insertable<std::chrono::local_seconds> ); +static_assert( ! ostream_insertable<std::chrono::local_time<D>> ); + int main() { test_ostream(); diff --git a/libstdc++-v3/testsuite/std/time/format/custom_rep.h b/libstdc++-v3/testsuite/std/time/format/custom_rep.h new file mode 100644 index 0000000..8363eaa --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/format/custom_rep.h @@ -0,0 +1,92 @@ +#include <chrono> +#include <ostream> + +#define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S) +#define WIDEN(S) WIDEN_(CharT, S) + +template<typename Ret = void, typename Under = long> +struct Rep +{ + using Return + = std::conditional_t<std::is_void_v<Ret>, Rep, Ret>; + + Rep(Under v = 0) : val(v) {} + + template<typename ORet, typename OUnder> + Rep(Rep<ORet, OUnder> o) : val(o.val) {} + + operator Under() const + { return val; } + + Return + operator+() const + { return val; } + + Rep + operator-() const + { return -val; } + + friend Rep + operator+(Rep lhs, Rep rhs) + { return lhs.val + rhs.val; } + + friend Rep + operator-(Rep lhs, Rep rhs) + { return lhs.val - rhs.val; } + + friend Rep + operator*(Rep lhs, Rep rhs) + { return lhs.val * rhs.val; } + + friend Rep + operator/(Rep lhs, Rep rhs) + { return lhs.val / rhs.val; } + + friend auto operator<=>(Rep, Rep) = default; + + template<typename CharT> + friend std::basic_ostream<CharT>& + operator<<(std::basic_ostream<CharT>& os, const Rep& t) + { return os << t.val << WIDEN("[via <<]"); } + + Under val; +}; + +template<typename Ret, typename Under1, typename Under2> +struct std::common_type<Rep<Ret, Under1>, Rep<Ret, Under2>> +{ + using type = Rep<Ret, std::common_type_t<Under1, Under2>>; +}; + +template<typename Ret, typename Under, typename Other> + requires std::is_integral_v<Other> +struct std::common_type<Rep<Ret, Under>, Other> +{ + using type = Rep<Ret, std::common_type_t<Under, Other>>; +}; + +template<typename Ret, typename Under, typename Other> + requires std::is_integral_v<Other> +struct std::common_type<Other, Rep<Ret, Under>> + : std::common_type<Rep<Ret, Under>, Other> +{ }; + +template<typename Ret, typename Under> +struct std::numeric_limits<Rep<Ret, Under>> + : std::numeric_limits<Under> +{ }; + +template<typename Ret, typename Under, typename CharT> +struct std::formatter<Rep<Ret, Under>, CharT> + : std::formatter<Under, CharT> +{ + template<typename Out> + typename std::basic_format_context<Out, CharT>::iterator + format(const Rep<Ret>& t, std::basic_format_context<Out, CharT>& ctx) const + { + constexpr std::basic_string_view<CharT> suffix = WIDEN("[via format]"); + auto out = std::formatter<Under, CharT>::format(t.val, ctx); + return std::ranges::copy(suffix, out).out; + } +}; + diff --git a/libstdc++-v3/testsuite/std/time/format/data_not_present_neg.cc b/libstdc++-v3/testsuite/std/time/format/data_not_present_neg.cc new file mode 100644 index 0000000..cb8f916 --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/format/data_not_present_neg.cc @@ -0,0 +1,164 @@ +// { dg-do compile { target c++20 } } + +#include <chrono> +#include <format> + +using namespace std::chrono; + +auto d1 = std::format("{:%w}", 10d); // { dg-error "call to consteval function" } +auto d2 = std::format("{:%m}", 10d); // { dg-error "call to consteval function" } +auto d3 = std::format("{:%y}", 10d); // { dg-error "call to consteval function" } +auto d4 = std::format("{:%F}", 10d); // { dg-error "call to consteval function" } +auto d5 = std::format("{:%T}", 10d); // { dg-error "call to consteval function" } +auto d6 = std::format("{:%Q}", 10d); // { dg-error "call to consteval function" } +auto d7 = std::format("{:%Z}", 10d); // { dg-error "call to consteval function" } + +auto w1 = std::format("{:%d}", Thursday); // { dg-error "call to consteval function" } +auto w2 = std::format("{:%m}", Thursday); // { dg-error "call to consteval function" } +auto w3 = std::format("{:%y}", Thursday); // { dg-error "call to consteval function" } +auto w4 = std::format("{:%F}", Thursday); // { dg-error "call to consteval function" } +auto w5 = std::format("{:%T}", Thursday); // { dg-error "call to consteval function" } +auto w6 = std::format("{:%Q}", Thursday); // { dg-error "call to consteval function" } +auto w7 = std::format("{:%Z}", Thursday); // { dg-error "call to consteval function" } + +auto wi1 = std::format("{:%d}", Thursday[2]); // { dg-error "call to consteval function" } +auto wi2 = std::format("{:%m}", Thursday[2]); // { dg-error "call to consteval function" } +auto wi3 = std::format("{:%y}", Thursday[2]); // { dg-error "call to consteval function" } +auto wi4 = std::format("{:%F}", Thursday[2]); // { dg-error "call to consteval function" } +auto wi5 = std::format("{:%T}", Thursday[2]); // { dg-error "call to consteval function" } +auto wi6 = std::format("{:%Q}", Thursday[2]); // { dg-error "call to consteval function" } +auto wi7 = std::format("{:%Z}", Thursday[2]); // { dg-error "call to consteval function" } + +auto wl1 = std::format("{:%d}", Thursday[last]); // { dg-error "call to consteval function" } +auto wl2 = std::format("{:%m}", Thursday[last]); // { dg-error "call to consteval function" } +auto wl3 = std::format("{:%y}", Thursday[last]); // { dg-error "call to consteval function" } +auto wl4 = std::format("{:%F}", Thursday[last]); // { dg-error "call to consteval function" } +auto wl5 = std::format("{:%T}", Thursday[last]); // { dg-error "call to consteval function" } +auto wl6 = std::format("{:%Q}", Thursday[last]); // { dg-error "call to consteval function" } +auto wl7 = std::format("{:%Z}", Thursday[last]); // { dg-error "call to consteval function" } + +auto m1 = std::format("{:%d}", January); // { dg-error "call to consteval function" } +auto m2 = std::format("{:%w}", January); // { dg-error "call to consteval function" } +auto m3 = std::format("{:%y}", January); // { dg-error "call to consteval function" } +auto m4 = std::format("{:%F}", January); // { dg-error "call to consteval function" } +auto m5 = std::format("{:%T}", January); // { dg-error "call to consteval function" } +auto m6 = std::format("{:%Q}", January); // { dg-error "call to consteval function" } +auto m7 = std::format("{:%Z}", January); // { dg-error "call to consteval function" } + +auto yr1 = std::format("{:%d}", 2025y); // { dg-error "call to consteval function" } +auto yr2 = std::format("{:%w}", 2025y); // { dg-error "call to consteval function" } +auto yr3 = std::format("{:%m}", 2025y); // { dg-error "call to consteval function" } +auto yr4 = std::format("{:%F}", 2025y); // { dg-error "call to consteval function" } +auto yr5 = std::format("{:%T}", 2025y); // { dg-error "call to consteval function" } +auto yr6 = std::format("{:%Q}", 2025y); // { dg-error "call to consteval function" } +auto yr7 = std::format("{:%Z}", 2025y); // { dg-error "call to consteval function" } + +auto md1 = std::format("{:%w}", January/10d); // { dg-error "call to consteval function" } +auto md2 = std::format("{:%y}", January/10d); // { dg-error "call to consteval function" } +auto md3 = std::format("{:%F}", January/10d); // { dg-error "call to consteval function" } +auto md4 = std::format("{:%T}", January/10d); // { dg-error "call to consteval function" } +auto md5 = std::format("{:%Q}", January/10d); // { dg-error "call to consteval function" } +auto md6 = std::format("{:%Z}", January/10d); // { dg-error "call to consteval function" } + +auto mwi1 = std::format("{:%d}", January/Thursday[2]); // { dg-error "call to consteval function" } +auto mwi2 = std::format("{:%y}", January/Thursday[2]); // { dg-error "call to consteval function" } +auto mwi3 = std::format("{:%F}", January/Thursday[2]); // { dg-error "call to consteval function" } +auto mwi4 = std::format("{:%T}", January/Thursday[2]); // { dg-error "call to consteval function" } +auto mwi5 = std::format("{:%Q}", January/Thursday[2]); // { dg-error "call to consteval function" } +auto mwi6 = std::format("{:%Z}", January/Thursday[2]); // { dg-error "call to consteval function" } + +auto mwl1 = std::format("{:%d}", January/Thursday[last]); // { dg-error "call to consteval function" } +auto mwl2 = std::format("{:%y}", January/Thursday[last]); // { dg-error "call to consteval function" } +auto mwl3 = std::format("{:%F}", January/Thursday[last]); // { dg-error "call to consteval function" } +auto mwl4 = std::format("{:%T}", January/Thursday[last]); // { dg-error "call to consteval function" } +auto mwl5 = std::format("{:%Q}", January/Thursday[last]); // { dg-error "call to consteval function" } +auto mwl6 = std::format("{:%Z}", January/Thursday[last]); // { dg-error "call to consteval function" } + +auto ml1 = std::format("{:%d}", January/last); // { dg-error "call to consteval function" } +auto ml2 = std::format("{:%w}", January/last); // { dg-error "call to consteval function" } +auto ml3 = std::format("{:%y}", January/last); // { dg-error "call to consteval function" } +auto ml4 = std::format("{:%F}", January/last); // { dg-error "call to consteval function" } +auto ml5 = std::format("{:%T}", January/last); // { dg-error "call to consteval function" } +auto ml6 = std::format("{:%Q}", January/last); // { dg-error "call to consteval function" } +auto ml7 = std::format("{:%Z}", January/last); // { dg-error "call to consteval function" } + +auto ym1 = std::format("{:%d}", 2024y/March); // { dg-error "call to consteval function" } +auto ym2 = std::format("{:%w}", 2024y/March); // { dg-error "call to consteval function" } +auto ym3 = std::format("{:%F}", 2024y/March); // { dg-error "call to consteval function" } +auto ym4 = std::format("{:%T}", 2024y/March); // { dg-error "call to consteval function" } +auto ym5 = std::format("{:%Q}", 2024y/March); // { dg-error "call to consteval function" } +auto ym6 = std::format("{:%Z}", 2024y/March); // { dg-error "call to consteval function" } + +auto ymd1 = std::format("{:%T}", 2021y/January/10d); // { dg-error "call to consteval function" } +auto ymd2 = std::format("{:%Q}", 2021y/January/10d); // { dg-error "call to consteval function" } +auto ymd3 = std::format("{:%Z}", 2021y/January/10d); // { dg-error "call to consteval function" } + +auto ymwi1 = std::format("{:%T}", 2021y/January/Thursday[2]); // { dg-error "call to consteval function" } +auto ymwi2 = std::format("{:%Q}", 2021y/January/Thursday[2]); // { dg-error "call to consteval function" } +auto ymwi3 = std::format("{:%Z}", 2021y/January/Thursday[2]); // { dg-error "call to consteval function" } + +auto ymwl1 = std::format("{:%T}", 2021y/January/Thursday[last]); // { dg-error "call to consteval function" } +auto ymwl2 = std::format("{:%Q}", 2021y/January/Thursday[last]); // { dg-error "call to consteval function" } +auto ymwl3 = std::format("{:%Z}", 2021y/January/Thursday[last]); // { dg-error "call to consteval function" } + +auto yml1 = std::format("{:%T}", 2021y/January/last); // { dg-error "call to consteval function" } +auto yml2 = std::format("{:%Q}", 2021y/January/last); // { dg-error "call to consteval function" } +auto yml3 = std::format("{:%Z}", 2021y/January/last); // { dg-error "call to consteval function" } + +auto ls1 = std::format("{:%Q}", local_seconds(20s)); // { dg-error "call to consteval function" } +auto ls2 = std::format("{:%Z}", local_seconds(10s)); // { dg-error "call to consteval function" } +auto ld1 = std::format("{:%Q}", local_days(days(20))); // { dg-error "call to consteval function" } +auto ld2 = std::format("{:%Z}", local_days(days(10))); // { dg-error "call to consteval function" } + +auto ss1 = std::format("{:%Q}", sys_seconds(20s)); // { dg-error "call to consteval function" } +auto sd1 = std::format("{:%Q}", sys_days(days(20))); // { dg-error "call to consteval function" } + +auto utc = std::format("{:%Q}", utc_clock::now()); // { dg-error "call to consteval function" } +auto gps = std::format("{:%Q}", gps_clock::now()); // { dg-error "call to consteval function" } +auto tai = std::format("{:%Q}", tai_clock::now()); // { dg-error "call to consteval function" } +auto file = std::format("{:%Q}", file_clock::now()); // { dg-error "call to consteval function" } + +const auto ltc = local_seconds(10s); +#if _GLIBCXX_USE_CXX11_ABI +const auto zt = zoned_time<seconds>("Europe/Sofia", local_seconds(10s)); +auto zt1 = std::format("{:%Q}", zt); // { dg-error "call to consteval function" "" { target cxx11_abi } } +#endif +auto lf1 = std::format("{:%Q}", local_time_format(ltc)); // { dg-error "call to consteval function" } + +auto dur1 = std::format("{:%d}", 123s); // { dg-error "call to consteval function" } +auto dur2 = std::format("{:%w}", 123s); // { dg-error "call to consteval function" } +auto dur3 = std::format("{:%m}", 123s); // { dg-error "call to consteval function" } +auto dur4 = std::format("{:%y}", 123s); // { dg-error "call to consteval function" } +auto dur5 = std::format("{:%F}", 123s); // { dg-error "call to consteval function" } +auto dur6 = std::format("{:%Z}", 123s); // { dg-error "call to consteval function" } + +using HMS = hh_mm_ss<seconds>; +auto hms1 = std::format("{:%d}", HMS(1255s)); // { dg-error "call to consteval function" } +auto hms2 = std::format("{:%w}", HMS(1255s)); // { dg-error "call to consteval function" } +auto hms3 = std::format("{:%m}", HMS(1255s)); // { dg-error "call to consteval function" } +auto hms4 = std::format("{:%y}", HMS(1255s)); // { dg-error "call to consteval function" } +auto hms5 = std::format("{:%F}", HMS(1255s)); // { dg-error "call to consteval function" } +auto hms6 = std::format("{:%Q}", HMS(1255s)); // { dg-error "call to consteval function" } +auto hms7 = std::format("{:%Z}", HMS(1255s)); // { dg-error "call to consteval function" } + +#if _GLIBCXX_USE_CXX11_ABI +auto li1 = std::format("{:%d}", local_info()); // { dg-error "call to consteval function" "" { target cxx11_abi } } +auto li2 = std::format("{:%w}", local_info()); // { dg-error "call to consteval function" "" { target cxx11_abi } } +auto li3 = std::format("{:%m}", local_info()); // { dg-error "call to consteval function" "" { target cxx11_abi } } +auto li4 = std::format("{:%y}", local_info()); // { dg-error "call to consteval function" "" { target cxx11_abi } } +auto li5 = std::format("{:%F}", local_info()); // { dg-error "call to consteval function" "" { target cxx11_abi } } +auto li6 = std::format("{:%T}", local_info()); // { dg-error "call to consteval function" "" { target cxx11_abi } } +auto li7 = std::format("{:%Q}", local_info()); // { dg-error "call to consteval function" "" { target cxx11_abi } } +auto li8 = std::format("{:%Z}", local_info()); // { dg-error "call to consteval function" "" { target cxx11_abi } } + +auto si1 = std::format("{:%d}", sys_info()); // { dg-error "call to consteval function" "" { target cxx11_abi } } +auto si2 = std::format("{:%w}", sys_info()); // { dg-error "call to consteval function" "" { target cxx11_abi } } +auto si3 = std::format("{:%m}", sys_info()); // { dg-error "call to consteval function" "" { target cxx11_abi } } +auto si4 = std::format("{:%y}", sys_info()); // { dg-error "call to consteval function" "" { target cxx11_abi } } +auto si5 = std::format("{:%F}", sys_info()); // { dg-error "call to consteval function" "" { target cxx11_abi } } +auto si6 = std::format("{:%T}", sys_info()); // { dg-error "call to consteval function" "" { target cxx11_abi } } +auto si7 = std::format("{:%Q}", sys_info()); // { dg-error "call to consteval function" "" { target cxx11_abi } } +auto si8 = std::format("{:%Z}", sys_info()); // { dg-error "call to consteval function" "" { target cxx11_abi } } +#endif + +// { dg-error "call to non-'constexpr' function" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/std/time/format/empty_spec.cc b/libstdc++-v3/testsuite/std/time/format/empty_spec.cc new file mode 100644 index 0000000..b84f84a --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/format/empty_spec.cc @@ -0,0 +1,841 @@ +// { dg-do run { target c++20 } } +// { dg-require-effective-target hosted } +// { dg-timeout-factor 5 } + +#include <chrono> +#include <ranges> +#include <sstream> +#include <testsuite_hooks.h> +#include "custom_rep.h" + +using namespace std::chrono; + + +template<typename CharT, typename T> +void +test_no_empty_spec() +{ + try + { + T t{}; + + if constexpr (std::is_same_v<CharT, char>) + (void)std::vformat("{}", std::make_format_args(t)); +#ifdef _GLIBCXX_USE_WCHAR_T + else + (void)std::vformat(L"{}", std::make_wformat_args(t)); +#endif // _GLIBCXX_USE_WCHAR_T + VERIFY(false); + } + catch (const std::format_error&) + { + VERIFY(true); + } +} + +template<typename T, typename CharT> +void verify(const T& t, std::basic_string_view<CharT> str) +{ + std::basic_string<CharT> res; + + res = std::format(WIDEN("{}"), t); + VERIFY( res == str ); + + std::basic_stringstream<CharT> os; + os << t; + res = std::move(os).str(); + VERIFY( res == str ); +} + +template<typename T, typename CharT> +void verify(const T& t, const CharT* str) +{ verify(t, std::basic_string_view<CharT>(str)); } + +template<typename CharT> +void +test_padding() +{ + std::basic_string<CharT> res; + + res = std::format(WIDEN("{:5}"), day(2)); + VERIFY( res == WIDEN("02 ") ); + + res = std::format(WIDEN("{:>6}"), weekday(4)); + VERIFY( res == WIDEN(" Thu") ); + + res = std::format(WIDEN("{:^7}"), month(3)); + VERIFY( res == WIDEN(" Mar ") ); + + res = std::format(WIDEN("{:-<4}"), day(30)); + VERIFY( res == WIDEN("30--") ); + + res = std::format(WIDEN("{:+>30}"), weekday(9)); + VERIFY( res == WIDEN("++++++9 is not a valid weekday") ); + + res = std::format(WIDEN("{:=^27}"), month(16)); + VERIFY( res == WIDEN("==16 is not a valid month==") ); +} + +using deciseconds = duration<seconds::rep, std::deci>; + +template<typename CharT> +void +test_duration() +{ + std::basic_string<CharT> res; + + const milliseconds di(40); + verify( di, WIDEN("40ms") ); + res = std::format(WIDEN("{:>6}"), di); + VERIFY( res == WIDEN(" 40ms") ); + + verify( -di, WIDEN("-40ms") ); + res = std::format(WIDEN("{:>6}"), -di); + VERIFY( res == WIDEN(" -40ms") ); +} + +template<typename CharT> +void +test_duration_fp() +{ + std::basic_string<CharT> res; + + const duration<double> df(11.22); + verify( df, WIDEN("11.22s") ); + res = std::format(WIDEN("{:=^12}"), df); + VERIFY( res == WIDEN("===11.22s===") ); + + verify( -df, WIDEN("-11.22s") ); + res = std::format(WIDEN("{:=^12}"), -df); + VERIFY( res == WIDEN("==-11.22s===") ); + + // precision accepted but ignored + res = std::format(WIDEN("{:.6}"), df); + VERIFY( res == WIDEN("11.22s") ); +} + +template<typename CharT> +void +test_duration_cust() +{ + std::basic_string<CharT> res; + const duration<char, std::ratio<1, 10>> charRep(123); + verify( charRep, WIDEN("123ds") ); + + // +asLong returns long, so formatted as long + const duration<Rep<long>> asLong(20); + verify( asLong, WIDEN("20s") ); + res = std::format(WIDEN("{:>6}"), asLong); + VERIFY( res == WIDEN(" 20s") ); + + verify( -asLong, WIDEN("-20s") ); + res = std::format(WIDEN("{:>6}"), -asLong); + VERIFY( res == WIDEN(" -20s") ); + + res = std::format(WIDEN("{:%Q}"), asLong); + VERIFY( res == WIDEN("20") ); + res = std::format(WIDEN("{:+<7%Q}"), asLong); + VERIFY( res == WIDEN("20+++++") ); + + // +asRep returns Rep<>, so formatted as Rep<> + const duration<Rep<>> asRep(10); + verify( asRep, WIDEN("10[via <<]s") ); + res = std::format(WIDEN("{:=^15}"), asRep); + VERIFY( res == WIDEN("==10[via <<]s==") ); + + verify( -asRep, WIDEN("-10[via <<]s") ); + res = std::format(WIDEN("{:=^15}"), -asRep); + VERIFY( res == WIDEN("=-10[via <<]s==") ); + + res = std::format(WIDEN("{:%Q}"), asRep); + VERIFY( res == WIDEN("10[via format]") ); + res = std::format(WIDEN("{:=^18%Q}"), asRep); + VERIFY( res == WIDEN("==10[via format]==") ); + + const duration<Rep<>, std::milli> milliRep(10); + verify( milliRep, WIDEN("10[via <<]ms") ); + res = std::format(WIDEN("{:=^15}"), milliRep); + VERIFY( res == WIDEN("=10[via <<]ms==") ); + + verify( -milliRep, WIDEN("-10[via <<]ms") ); + res = std::format(WIDEN("{:=^15}"), -milliRep); + VERIFY( res == WIDEN("=-10[via <<]ms=") ); + + res = std::format(WIDEN("{:%Q}"), milliRep); + VERIFY( res == WIDEN("10[via format]") ); + res = std::format(WIDEN("{:=^18%Q}"), milliRep); + VERIFY( res == WIDEN("==10[via format]==") ); +} + +template<typename Ratio, typename Rep, typename Period> +constexpr auto +hms(const duration<Rep, Period>& d) +{ + using Dur = duration<Rep, typename Ratio::period>; + return hh_mm_ss<Dur>(duration_cast<Dur>(d)); +} + +template<typename CharT> +void +test_hh_mm_ss() +{ + auto dt = 22h + 24min + 54s + 111222333ns; + verify( hms<nanoseconds>(dt), + WIDEN("22:24:54.111222333") ); + verify( hms<microseconds>(dt), + WIDEN("22:24:54.111222") ); + verify( hms<milliseconds>(dt), + WIDEN("22:24:54.111") ); + verify( hms<deciseconds>(dt), + WIDEN("22:24:54.1") ); + verify( hms<seconds>(dt), + WIDEN("22:24:54") ); + verify( hms<minutes>(dt), + WIDEN("22:24:00") ); + verify( hms<hours>(dt), + WIDEN("22:00:00") ); + verify( hms<nanoseconds>(-dt), + WIDEN("-22:24:54.111222333") ); + verify( hms<microseconds>(-dt), + WIDEN("-22:24:54.111222") ); + verify( hms<milliseconds>(-dt), + WIDEN("-22:24:54.111") ); + verify( hms<deciseconds>(-dt), + WIDEN("-22:24:54.1") ); + verify( hms<seconds>(-dt), + WIDEN("-22:24:54") ); + verify( hms<minutes>(-dt), + WIDEN("-22:24:00") ); + verify( hms<hours>(-dt), + WIDEN("-22:00:00") ); + + verify( hms<nanoseconds>(-dt), + WIDEN("-22:24:54.111222333") ); + + dt += 300h; + verify( hms<nanoseconds>(dt), + WIDEN("322:24:54.111222333") ); + verify( hms<nanoseconds>(-dt), + WIDEN("-322:24:54.111222333") ); + + dt += 14000h; + verify( hms<nanoseconds>(dt), + WIDEN("14322:24:54.111222333") ); + verify( hms<nanoseconds>(-dt), + WIDEN("-14322:24:54.111222333") ); +} + +template<typename CharT> +void +test_hh_mm_ss_fp() +{ + duration<double> dt = 22h + 24min + 54s + 111222333ns; + // period controls number of subseconds + verify( hms<nanoseconds>(dt), + WIDEN("22:24:54.111222333") ); + verify( hms<microseconds>(dt), + WIDEN("22:24:54.111222") ); + verify( hms<milliseconds>(dt), + WIDEN("22:24:54.111") ); + verify( hms<deciseconds>(dt), + WIDEN("22:24:54.1") ); + verify( hms<seconds>(dt), + WIDEN("22:24:54") ); + verify( hms<nanoseconds>(-dt), + WIDEN("-22:24:54.111222333") ); + verify( hms<microseconds>(-dt), + WIDEN("-22:24:54.111222") ); + verify( hms<milliseconds>(-dt), + WIDEN("-22:24:54.111") ); + verify( hms<deciseconds>(-dt), + WIDEN("-22:24:54.1") ); + verify( hms<seconds>(-dt), + WIDEN("-22:24:54") ); + + // but hour and minutes are preserved + verify( hms<minutes>(dt), + WIDEN("22:24:54") ); + verify( hms<hours>(dt), + WIDEN("22:24:54") ); + verify( hms<minutes>(-dt), + WIDEN("-22:24:54") ); + verify( hms<hours>(-dt), + WIDEN("-22:24:54") ); +} + +template<typename CharT> +void +test_hh_mm_ss_cust() +{ + const duration<char, deciseconds::period> charRep(123); + verify( hms<deciseconds>(charRep), + WIDEN("00:00:12.3") ); + verify( hms<seconds>(charRep), + WIDEN("00:00:12") ); + + auto dt = 22h + 24min + 54s + 123ms; + // +plus returns long, so formatted as long + const duration<Rep<long>, std::milli> asLong(dt.count()); + verify( hms<milliseconds>(asLong), + WIDEN("22:24:54.123") ); + verify( hms<deciseconds>(asLong), + WIDEN("22:24:54.1") ); + verify( hms<seconds>(asLong), + WIDEN("22:24:54") ); + verify( hms<milliseconds>(-asLong), + WIDEN("-22:24:54.123") ); + verify( hms<deciseconds>(-asLong), + WIDEN("-22:24:54.1") ); + verify( hms<seconds>(-asLong), + WIDEN("-22:24:54") ); + + // +asRep returns Rep<>, so formatted as Rep<> + const duration<Rep<>, std::milli> asRep(dt.count()); + verify( hms<milliseconds>(asRep), + WIDEN("22:24:54.123") ); + verify( hms<deciseconds>(asRep), + WIDEN("22:24:54.1") ); + verify( hms<seconds>(asLong), + WIDEN("22:24:54") ); + verify( hms<milliseconds>(-asLong), + WIDEN("-22:24:54.123") ); + verify( hms<deciseconds>(-asLong), + WIDEN("-22:24:54.1") ); + verify( hms<seconds>(-asLong), + WIDEN("-22:24:54") ); +} + +template<typename CharT> +void +test_durations() +{ + test_duration<CharT>(); + test_duration_fp<CharT>(); + test_duration_cust<CharT>(); + + test_hh_mm_ss<CharT>(); + test_hh_mm_ss_fp<CharT>(); + test_hh_mm_ss_cust<CharT>(); +} + +template<typename CharT> +void +test_day() +{ + verify( day(0), WIDEN("00 is not a valid day") ); + verify( day(1), WIDEN("01") ); + verify( day(10), WIDEN("10") ); + verify( day(32), WIDEN("32 is not a valid day") ); + verify( day(110), WIDEN("110 is not a valid day") ); + verify( day(255), WIDEN("255 is not a valid day") ); +} + +template<typename CharT> +void +test_month() +{ + verify( month(0), WIDEN("0 is not a valid month") ); + verify( month(1), WIDEN("Jan") ); + verify( month(10), WIDEN("Oct") ); + verify( month(32), WIDEN("32 is not a valid month") ); + verify( month(110), WIDEN("110 is not a valid month") ); + verify( month(100), WIDEN("100 is not a valid month") ); + verify( month(110), WIDEN("110 is not a valid month") ); + verify( month(255), WIDEN("255 is not a valid month") ); +} + +template<typename CharT> +void +test_year() +{ + verify( year(-32768), WIDEN("-32768 is not a valid year") ); + verify( year(-32767), WIDEN("-32767") ); + verify( year(-67), WIDEN( "-0067") ); + verify( year(-1), WIDEN( "-0001") ); + verify( year(0), WIDEN( "0000") ); + verify( year(1), WIDEN( "0001") ); + verify( year(123), WIDEN( "0123") ); + verify( year(2025), WIDEN( "2025") ); + verify( year(32767), WIDEN( "32767") ); +} + +template<typename CharT> +void +test_weekday() +{ + verify( weekday(0), WIDEN("Sun") ); + verify( weekday(2), WIDEN("Tue") ); + verify( weekday(6), WIDEN("Sat") ); + verify( weekday(7), WIDEN("Sun") ); + verify( weekday(9), WIDEN("9 is not a valid weekday") ); + verify( weekday(32), WIDEN("32 is not a valid weekday") ); + verify( weekday(110), WIDEN("110 is not a valid weekday") ); + verify( weekday(255), WIDEN("255 is not a valid weekday") ); +} + +template<typename CharT> +void +test_weekday_indexed() +{ + verify( weekday(0)[0], WIDEN("Sun[0 is not a valid index]") ); + verify( weekday(2)[1], WIDEN("Tue[1]") ); + verify( weekday(6)[5], WIDEN("Sat[5]") ); + verify( weekday(7)[6], WIDEN("Sun[6 is not a valid index]") ); + verify( weekday(7)[12], WIDEN("Sun[12 is not a valid index]") ); + verify( weekday(5)[117], WIDEN("Fri[117 is not a valid index]") ); + verify( weekday(7)[255], WIDEN("Sun[255 is not a valid index]") ); + verify( weekday(9)[1], WIDEN("9 is not a valid weekday[1]") ); + verify( weekday(32)[7], WIDEN("32 is not a valid weekday[7 is not a valid index]") ); +} + +template<typename CharT> +void +test_weekday_last() +{ + verify( weekday(0)[last], WIDEN("Sun[last]") ); + verify( weekday(9)[last], WIDEN("9 is not a valid weekday[last]") ); +} + +template<typename CharT> +void +test_month_day() +{ + verify( month(1)/30, WIDEN("Jan/30") ); + verify( month(3)/32, WIDEN("Mar/32 is not a valid day") ); + verify( month(13)/30, WIDEN("13 is not a valid month/30") ); + verify( month(13)/32, WIDEN("13 is not a valid month/32 is not a valid day") ); +} + +template<typename CharT> +void +test_month_day_last() +{ + verify( month(1)/last, WIDEN("Jan/last") ); + verify( month(14)/last, WIDEN("14 is not a valid month/last") ); +} + +template<typename CharT> +void +test_month_weekday() +{ + verify( month(1)/weekday(2)[1], + WIDEN("Jan/Tue[1]") ); + verify( month(3)/weekday(9)[2], + WIDEN("Mar/9 is not a valid weekday[2]") ); + verify( month(13)/weekday(1)[7], + WIDEN("13 is not a valid month/Mon[7 is not a valid index]") ); + verify( month(13)/weekday(10)[3], + WIDEN("13 is not a valid month/10 is not a valid weekday[3]") ); + verify( month(13)/weekday(130)[0], + WIDEN("13 is not a valid month/130 is not a valid weekday[0 is not a valid index]") ); +} + +template<typename CharT> +void +test_month_weekday_last() +{ + verify( month(1)/weekday(2)[last], + WIDEN("Jan/Tue[last]") ); + verify( month(3)/weekday(9)[last], + WIDEN("Mar/9 is not a valid weekday[last]") ); + verify( month(13)/weekday(1)[last], + WIDEN("13 is not a valid month/Mon[last]") ); + verify( month(13)/weekday(10)[last], + WIDEN("13 is not a valid month/10 is not a valid weekday[last]") ); +} + +template<typename CharT> +void +test_year_month() +{ + verify( year(2024)/month(1), + WIDEN("2024/Jan") ); + verify( year(2025)/month(14), + WIDEN("2025/14 is not a valid month") ); + verify( year(-32768)/month(2), + WIDEN("-32768 is not a valid year/Feb") ); + verify( year(-32768)/month(0), + WIDEN("-32768 is not a valid year/0 is not a valid month") ); +} + +template<typename CharT> +void +test_year_month_day() +{ + verify( year(2024)/month(1)/30, + WIDEN("2024-01-30") ); + verify( year(-100)/month(14)/1, + WIDEN("-0100-14-01 is not a valid date") ); + verify( year(2025)/month(11)/100, + WIDEN("2025-11-100 is not a valid date") ); + verify( year(-32768)/month(2)/10, + WIDEN("-32768-02-10 is not a valid date") ); + verify( year(-32768)/month(212)/10, + WIDEN("-32768-212-10 is not a valid date") ); + verify( year(-32768)/month(2)/105, + WIDEN("-32768-02-105 is not a valid date") ); + verify( year(-32768)/month(14)/55, + WIDEN("-32768-14-55 is not a valid date") ); +} + +template<typename CharT> +void +test_year_month_last() +{ + verify( year(2024)/month(1)/last, + WIDEN("2024/Jan/last") ); + verify( year(2025)/month(14)/last, + WIDEN("2025/14 is not a valid month/last") ); + verify( year(-32768)/month(2)/last, + WIDEN("-32768 is not a valid year/Feb/last") ); + verify( year(-32768)/month(0)/last, + WIDEN("-32768 is not a valid year/0 is not a valid month/last") ); +} + +template<typename CharT> +void +test_year_month_weekday() +{ + verify( year(2024)/month(1)/weekday(2)[1], + WIDEN("2024/Jan/Tue[1]") ); + verify( year(-1)/month(3)/weekday(9)[2], + WIDEN("-0001/Mar/9 is not a valid weekday[2]") ); + verify( year(-32768)/month(13)/weekday(1)[7], + WIDEN("-32768 is not a valid year/13 is not a valid month/Mon[7 is not a valid index]") ); + verify( year(-100)/month(13)/weekday(10)[3], + WIDEN("-0100/13 is not a valid month/10 is not a valid weekday[3]") ); + verify( year(-32768)/month(13)/weekday(130)[0], + WIDEN("-32768 is not a valid year/13 is not a valid month/130 is not a valid weekday[0 is not a valid index]") ); +} + +template<typename CharT> +void +test_year_month_weekday_last() +{ + verify( year(2024)/month(1)/weekday(2)[last], + WIDEN("2024/Jan/Tue[last]") ); + verify( year(-1)/month(3)/weekday(9)[last], + WIDEN("-0001/Mar/9 is not a valid weekday[last]") ); + verify( year(-32768)/month(13)/weekday(1)[last], + WIDEN("-32768 is not a valid year/13 is not a valid month/Mon[last]") ); + verify( year(-100)/month(13)/weekday(10)[last], + WIDEN("-0100/13 is not a valid month/10 is not a valid weekday[last]") ); + verify( year(-32768)/month(13)/weekday(130)[last], + WIDEN("-32768 is not a valid year/13 is not a valid month/130 is not a valid weekday[last]") ); +} + +template<typename CharT> +void +test_calendar() +{ + test_day<CharT>(); + test_month<CharT>(); + test_year<CharT>(); + + test_weekday<CharT>(); + test_weekday_indexed<CharT>(); + test_weekday_last<CharT>(); + + test_month_day<CharT>(); + test_month_day_last<CharT>(); + test_month_weekday<CharT>(); + test_month_weekday_last<CharT>(); + + test_year_month<CharT>(); + test_year_month_day<CharT>(); + test_year_month_last<CharT>(); + test_year_month_weekday<CharT>(); + test_year_month_weekday_last<CharT>(); +} + +template<typename Clock, typename Dur, typename Dur2> +constexpr auto +wall_cast(const local_time<Dur2>& tp) +{ + using TP = time_point<Clock, std::common_type_t<Dur, days>>; + if constexpr (std::is_same_v<Clock, utc_clock> || std::is_same_v<Clock, file_clock>) + return clock_cast<Clock>(wall_cast<system_clock, Dur>(tp)); + else if constexpr (std::is_same_v<Clock, tai_clock>) + return TP(floor<Dur>(tp.time_since_epoch()) + days(4383)); + else if constexpr (std::is_same_v<Clock, gps_clock>) + return TP(floor<Dur>(tp.time_since_epoch()) - days(3657)); + else // system_clock, local_t + return time_point<Clock, Dur>(floor<Dur>(tp.time_since_epoch())); +} + +using decadays = duration<days::rep, std::ratio_multiply<std::deca, days::period>>; +using kilodays = duration<days::rep, std::ratio_multiply<std::kilo, days::period>>; + +template<typename CharT, typename Clock, bool CustomizedOstream> +void +test_time_point() +{ + std::basic_string<CharT> res; + + const auto lt = local_days(2024y/March/22) + 13h + 24min + 54s + 111222333ns; + auto strip_time = [](std::basic_string_view<CharT> sv) + { return CustomizedOstream ? sv.substr(0, 10) : sv; }; + + verify( wall_cast<Clock, nanoseconds>(lt), + WIDEN("2024-03-22 13:24:54.111222333") ); + verify( wall_cast<Clock, microseconds>(lt), + WIDEN("2024-03-22 13:24:54.111222") ); + verify( wall_cast<Clock, milliseconds>(lt), + WIDEN("2024-03-22 13:24:54.111") ); + verify( wall_cast<Clock, seconds>(lt), + WIDEN("2024-03-22 13:24:54") ); + verify( wall_cast<Clock, minutes>(lt), + WIDEN("2024-03-22 13:24:00") ); + verify( wall_cast<Clock, hours>(lt), + WIDEN("2024-03-22 13:00:00") ); + verify( wall_cast<Clock, days>(lt), + strip_time(WIDEN("2024-03-22 00:00:00")) ); + verify( wall_cast<Clock, decadays>(lt), + strip_time(WIDEN("2024-03-18 00:00:00")) ); + verify( wall_cast<Clock, kilodays>(lt), + strip_time(WIDEN("2022-01-08 00:00:00")) ); + + if constexpr (!CustomizedOstream) + { + verify( wall_cast<Clock, duration<double>>(lt), + WIDEN("2024-03-22 13:24:54") ); + verify( wall_cast<Clock, years>(lt), + WIDEN("2024-01-01 02:16:48") ); + } + else + { + test_no_empty_spec<CharT, time_point<Clock, duration<double>>>(); + test_no_empty_spec<CharT, time_point<Clock, years>>(); + } +} + +template<typename CharT> +void +test_leap_second() +{ + std::basic_string<CharT> res; + + const auto st = sys_days(2012y/June/30) + 23h + 59min + 59s + 111222333ns; + auto tp = clock_cast<utc_clock>(st); + tp += 1s; + + verify( floor<nanoseconds>(tp), + WIDEN("2012-06-30 23:59:60.111222333") ); + verify( floor<microseconds>(tp), + WIDEN("2012-06-30 23:59:60.111222") ); + verify( floor<milliseconds>(tp), + WIDEN("2012-06-30 23:59:60.111") ); + verify( floor<seconds>(tp), + WIDEN("2012-06-30 23:59:60") ); +} + +#if _GLIBCXX_USE_CXX11_ABI || !_GLIBCXX_USE_DUAL_ABI +template<typename Dur, typename Dur2> +auto +make_zoned(const sys_time<Dur2>& st, const time_zone* tz) +{ return zoned_time<Dur>(tz, floor<Dur>(st)); } + +template<typename CharT> +void +test_zoned_time() +{ + const auto st = sys_days(2024y/March/22) + 13h + 24min + 54s + 111222333ns; + const time_zone* tz = locate_zone("Europe/Sofia"); + VERIFY( tz != nullptr ); + + verify( make_zoned<nanoseconds>(st, tz), + WIDEN("2024-03-22 15:24:54.111222333 EET") ); + verify( make_zoned<microseconds>(st, tz), + WIDEN("2024-03-22 15:24:54.111222 EET") ); + verify( make_zoned<milliseconds>(st, tz), + WIDEN("2024-03-22 15:24:54.111 EET") ); + verify( make_zoned<seconds>(st, tz), + WIDEN("2024-03-22 15:24:54 EET") ); + verify( make_zoned<minutes>(st, tz), + WIDEN("2024-03-22 15:24:00 EET") ); + verify( make_zoned<hours>(st, tz), + WIDEN("2024-03-22 15:00:00 EET") ); + verify( make_zoned<days>(st, tz), + WIDEN("2024-03-22 02:00:00 EET") ); + verify( make_zoned<decadays>(st, tz), + WIDEN("2024-03-18 02:00:00 EET") ); + verify( make_zoned<kilodays>(st, tz), + WIDEN("2022-01-08 02:00:00 EET") ); +} +#endif + +template<typename Dur, typename Dur2> +auto +local_fmt(const local_time<Dur2>& lt, std::string* zone) +{ return local_time_format(floor<Dur>(lt), zone); } + +template<typename CharT> +void +test_local_time_format() +{ + std::basic_string<CharT> res; + + std::string abbrev = "Zone"; + const auto lt = local_days(2024y/March/22) + 13h + 24min + 54s + 111222333ns; + + res = std::format(WIDEN("{}"), local_fmt<nanoseconds>(lt, &abbrev)); + VERIFY( res == WIDEN("2024-03-22 13:24:54.111222333 Zone") ); + res = std::format(WIDEN("{}"), local_fmt<microseconds>(lt, &abbrev)); + VERIFY( res == WIDEN("2024-03-22 13:24:54.111222 Zone") ); + res = std::format(WIDEN("{}"), local_fmt<milliseconds>(lt, &abbrev)); + VERIFY( res == WIDEN("2024-03-22 13:24:54.111 Zone") ); + res = std::format(WIDEN("{}"), local_fmt<seconds>(lt, &abbrev)); + VERIFY( res == WIDEN("2024-03-22 13:24:54 Zone") ); + res = std::format(WIDEN("{}"), local_fmt<minutes>(lt, &abbrev)); + VERIFY( res == WIDEN("2024-03-22 13:24:00 Zone") ); + res = std::format(WIDEN("{}"), local_fmt<hours>(lt, &abbrev)); + VERIFY( res == WIDEN("2024-03-22 13:00:00 Zone") ); + res = std::format(WIDEN("{}"), local_fmt<days>(lt, &abbrev)); + VERIFY( res == WIDEN("2024-03-22 00:00:00 Zone") ); + res = std::format(WIDEN("{}"), local_fmt<decadays>(lt, &abbrev)); + VERIFY( res == WIDEN("2024-03-18 00:00:00 Zone") ); + res = std::format(WIDEN("{}"), local_fmt<kilodays>(lt, &abbrev)); + VERIFY( res == WIDEN("2022-01-08 00:00:00 Zone") ); +} + +template<typename CharT> +void +test_time_points() +{ + test_time_point<CharT, local_t, true>(); + test_time_point<CharT, system_clock, true>(); + test_time_point<CharT, utc_clock, false>(); + test_time_point<CharT, tai_clock, false>(); + test_time_point<CharT, gps_clock, false>(); + test_time_point<CharT, file_clock, false>(); + test_leap_second<CharT>(); +#if _GLIBCXX_USE_CXX11_ABI || !_GLIBCXX_USE_DUAL_ABI + test_zoned_time<CharT>(); +#endif + test_local_time_format<CharT>(); + +} + +#if _GLIBCXX_USE_CXX11_ABI || !_GLIBCXX_USE_DUAL_ABI +template<typename CharT> +void +test_sys_info() +{ + const sys_info si + { + sys_days(2024y/March/22) + 2h, + sys_days(2025y/April/11) + 23h + 15min + 10s, + 2h + 13min + 4s, + 15min, + "Zone" + }; + const std::basic_string_view<CharT> txt + = WIDEN("[2024-03-22 02:00:00,2025-04-11 23:15:10,02:13:04,15min,Zone]"); + + verify( si, txt ); + + std::basic_string<CharT> res; + std::basic_string_view<CharT> sv; + + sv = res = std::format(WIDEN("{:65}"), si); + VERIFY( sv.ends_with(WIDEN(" ")) ); + sv.remove_suffix(4); + VERIFY( sv == txt ); + + sv = res = std::format(WIDEN("{:=^67}"), si); + VERIFY( sv.starts_with(WIDEN("===")) ); + VERIFY( sv.ends_with(WIDEN("===")) ); + sv.remove_prefix(3); + sv.remove_suffix(3); + VERIFY( sv == txt ); +} + +template<typename CharT> +void test_local_info() +{ + using String = std::basic_string<CharT>; + using StringView = std::basic_string_view<CharT>; + + const sys_info s1 + { + sys_days(2015y/September/11) + 2h, + sys_days(2016y/March/13) + 2h, + -5h, + 0h, + "EET" + }; + const sys_info s2 + { + sys_days(2016y/March/13) + 2h, + sys_days(2015y/September/15) + 2h, + -4h, + 1h, + "EDT" + }; + + const StringView single + = WIDEN("[2015-09-11 02:00:00,2016-03-13 02:00:00,-05:00:00,0min,EET]"); + const StringView both + = WIDEN(" local time between " + "[2015-09-11 02:00:00,2016-03-13 02:00:00,-05:00:00,0min,EET]" + " and " + "[2016-03-13 02:00:00,2015-09-15 02:00:00,-04:00:00,60min,EDT]"); + + const local_info l1{local_info::nonexistent, s1, s2}; + auto exp = WIDEN("[nonexistent") + String(both) + WIDEN("]"); + verify( l1, StringView(exp) ); + + const local_info l2{local_info::ambiguous, s1, s2}; + exp = WIDEN("[ambiguous") + String(both) + WIDEN("]"); + verify( l2, StringView(exp) ); + + const local_info l3{local_info::unique, s1, s1}; + exp = WIDEN("[") + String(single) + WIDEN("]"); + verify( l3, StringView(exp) ); + + String res; + StringView sv; + + sv = res = std::format(WIDEN("{:65}"), l3); + VERIFY( sv.ends_with(WIDEN(" ")) ); + sv.remove_suffix(3); + VERIFY( sv == exp ); + + sv = res = std::format(WIDEN("{:=^67}"), l3); + VERIFY( sv.starts_with(WIDEN("==")) ); + VERIFY( sv.ends_with(WIDEN("===")) ); + sv.remove_prefix(2); + sv.remove_suffix(3); + VERIFY( sv == exp ); +} + +template<typename CharT> +void +test_infos() +{ + test_sys_info<CharT>(); + test_local_info<CharT>(); +} +#endif + +template<typename CharT> +void +test_all() +{ + test_padding<CharT>(); + test_durations<CharT>(); + test_calendar<CharT>(); + test_time_points<CharT>(); +#if _GLIBCXX_USE_CXX11_ABI || !_GLIBCXX_USE_DUAL_ABI + test_infos<CharT>(); +#endif +} + +int main() +{ + test_all<char>(); + +#ifdef _GLIBCXX_USE_WCHAR_T + test_all<wchar_t>(); +#endif // _GLIBCXX_USE_WCHAR_T +} diff --git a/libstdc++-v3/testsuite/std/time/format/format.cc b/libstdc++-v3/testsuite/std/time/format/format.cc index d6e3583..d1aee05 100644 --- a/libstdc++-v3/testsuite/std/time/format/format.cc +++ b/libstdc++-v3/testsuite/std/time/format/format.cc @@ -2,6 +2,7 @@ // { dg-timeout-factor 2 } #include <chrono> +#include <vector> #include <testsuite_hooks.h> void @@ -78,6 +79,13 @@ test_bad_format_strings() VERIFY( not is_format_string_for("{:%OOy}", t) ); VERIFY( not is_format_string_for("{:%OEy}", t) ); VERIFY( not is_format_string_for("{:%EOy}", t) ); + + // weekday and month values for which ok() is false + VERIFY( not is_format_string_for("{:%a}", std::chrono::weekday(8)) ); + VERIFY( not is_format_string_for("{:%A}", std::chrono::weekday(8)) ); + VERIFY( not is_format_string_for("{:%b}", std::chrono::month(13)) ); + VERIFY( not is_format_string_for("{:%h}", std::chrono::month(13)) ); + VERIFY( not is_format_string_for("{:%B}", std::chrono::month(13)) ); } template<typename I> diff --git a/libstdc++-v3/testsuite/std/time/format/nonlocking.cc b/libstdc++-v3/testsuite/std/time/format/nonlocking.cc new file mode 100644 index 0000000..f1b57b5 --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/format/nonlocking.cc @@ -0,0 +1,164 @@ +// { dg-do compile { target c++23 } } + +#include <format> +#include <chrono> +#include "custom_rep.h" + +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::day>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::month>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::year>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::weekday>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::weekday_indexed>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::weekday_last>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::month_day>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::month_day_last>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::month_weekday>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::month_weekday_last>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::year_month>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::year_month_day>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::year_month_day_last>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::year_month_weekday>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::year_month_weekday_last>); + +#if _GLIBCXX_USE_CXX11_ABI || !_GLIBCXX_USE_DUAL_ABI +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::local_info>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::sys_info>); +#endif + +template<typename Duration> +using local_time_fmt + = decltype(std::chrono::local_time_format(std::chrono::local_time<Duration>{})); + +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::seconds>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::duration<float>>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::duration<long long, std::mega>>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::local_time<std::chrono::seconds>>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::sys_time<std::chrono::seconds>>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::utc_time<std::chrono::seconds>>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::gps_time<std::chrono::seconds>>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::tai_time<std::chrono::seconds>>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::file_time<std::chrono::seconds>>); +static_assert(std::enable_nonlocking_formatter_optimization< + local_time_fmt<std::chrono::seconds>>); + +using BufferedDuration = std::chrono::duration<Rep<void, int>>; + +static_assert(!std::enable_nonlocking_formatter_optimization< + BufferedDuration>); +static_assert(!std::enable_nonlocking_formatter_optimization< + std::chrono::local_time<BufferedDuration>>); +static_assert(!std::enable_nonlocking_formatter_optimization< + std::chrono::sys_time<BufferedDuration>>); +static_assert(!std::enable_nonlocking_formatter_optimization< + std::chrono::utc_time<BufferedDuration>>); +static_assert(!std::enable_nonlocking_formatter_optimization< + std::chrono::gps_time<BufferedDuration>>); +static_assert(!std::enable_nonlocking_formatter_optimization< + std::chrono::tai_time<BufferedDuration>>); +static_assert(!std::enable_nonlocking_formatter_optimization< + std::chrono::file_time<BufferedDuration>>); +static_assert(!std::enable_nonlocking_formatter_optimization< + local_time_fmt<BufferedDuration>>); + +template<> +inline constexpr bool + std::enable_nonlocking_formatter_optimization<Rep<void, long>> = true; + +using NonBufferedRep = std::chrono::duration<Rep<void, long>>; + +static_assert(!std::enable_nonlocking_formatter_optimization< + NonBufferedRep>); +static_assert(!std::enable_nonlocking_formatter_optimization< + std::chrono::local_time<NonBufferedRep>>); +static_assert(!std::enable_nonlocking_formatter_optimization< + std::chrono::sys_time<NonBufferedRep>>); +static_assert(!std::enable_nonlocking_formatter_optimization< + std::chrono::utc_time<NonBufferedRep>>); +static_assert(!std::enable_nonlocking_formatter_optimization< + std::chrono::gps_time<NonBufferedRep>>); +static_assert(!std::enable_nonlocking_formatter_optimization< + std::chrono::tai_time<NonBufferedRep>>); +static_assert(!std::enable_nonlocking_formatter_optimization< + std::chrono::file_time<NonBufferedRep>>); +static_assert(!std::enable_nonlocking_formatter_optimization< + local_time_fmt<NonBufferedRep>>); + +using NonBufferedDuration = std::chrono::duration<Rep<void, short>>; + +template<> +inline constexpr bool + std::enable_nonlocking_formatter_optimization<NonBufferedDuration> = true; + +static_assert(std::enable_nonlocking_formatter_optimization< + NonBufferedDuration>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::local_time<NonBufferedDuration>>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::sys_time<NonBufferedDuration>>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::utc_time<NonBufferedDuration>>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::gps_time<NonBufferedDuration>>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::tai_time<NonBufferedDuration>>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::file_time<NonBufferedDuration>>); +static_assert(std::enable_nonlocking_formatter_optimization< + local_time_fmt<NonBufferedDuration>>); + +#if _GLIBCXX_USE_CXX11_ABI || !_GLIBCXX_USE_DUAL_ABI +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::zoned_time<std::chrono::seconds>>); +static_assert(!std::enable_nonlocking_formatter_optimization< + std::chrono::zoned_time<BufferedDuration>>); +static_assert(!std::enable_nonlocking_formatter_optimization< + std::chrono::zoned_time<NonBufferedRep>>); +static_assert(std::enable_nonlocking_formatter_optimization< + std::chrono::zoned_time<NonBufferedDuration>>); + +struct MyTimeZone : std::chrono::time_zone +{}; + +template<> +struct std::chrono::zoned_traits<MyTimeZone> +{ + static const MyTimeZone* default_zone(); + static const MyTimeZone* locate_zone(std::string_view name); +}; + +static_assert(!std::enable_nonlocking_formatter_optimization< + std::chrono::zoned_time<std::chrono::seconds, MyTimeZone>>); +static_assert(!std::enable_nonlocking_formatter_optimization< + std::chrono::zoned_time<BufferedDuration, MyTimeZone>>); +static_assert(!std::enable_nonlocking_formatter_optimization< + std::chrono::zoned_time<NonBufferedRep, MyTimeZone>>); +static_assert(!std::enable_nonlocking_formatter_optimization< + std::chrono::zoned_time<NonBufferedDuration, MyTimeZone>>); +#endif + diff --git a/libstdc++-v3/testsuite/std/time/format/pr117214.cc b/libstdc++-v3/testsuite/std/time/format/pr117214.cc index 87c703d..79109c3 100644 --- a/libstdc++-v3/testsuite/std/time/format/pr117214.cc +++ b/libstdc++-v3/testsuite/std/time/format/pr117214.cc @@ -7,10 +7,14 @@ #include <chrono> #include <locale> +#include <span> #include <testsuite_hooks.h> + +template<typename ChronoType> void -test_c() +test_locale_formats(const ChronoType& t, + std::span<const char* const> test_specifiers) { const char *test_locales[] = { "aa_DJ.UTF-8", @@ -19,15 +23,44 @@ test_c() "az_IR.UTF-8", "my_MM.UTF-8", }; - std::chrono::sys_seconds t{std::chrono::seconds{1}}; + auto format_args = std::make_format_args(t); for (auto locale_name : test_locales) { - auto s = std::format(std::locale(locale_name), "{:L%c}", t); - VERIFY( !s.empty() ); + std::locale loc(locale_name); + for (auto specifier : test_specifiers) + { + auto s = std::vformat(loc, specifier, format_args); + VERIFY( !s.empty() ); + } } } +void +test_locale_formats() +{ + using namespace std::chrono; + + const char* test_specifiers[] = { + "{:L%x}", "{:L%Ex}", + "{:L%c}", "{:L%Ec}", + "{:L%X}", "{:L%EX}", + "{:L%r}", + }; + auto date_time_specifiers = std::span(test_specifiers); + auto date_specifiers = date_time_specifiers.subspan(0, 2); + auto time_specifiers = date_time_specifiers.subspan(4); + + auto ymd = 2020y/November/12d; + test_locale_formats(ymd, date_specifiers); + + auto tod = 25h + 10min + 12s; + test_locale_formats(tod, time_specifiers); + + auto tp = sys_days(ymd) + tod; + test_locale_formats(tp, date_time_specifiers); +} + #include <stdlib.h> #include <time.h> @@ -93,7 +126,7 @@ test_c_local() int main() { - test_c(); + test_locale_formats(); test_c_zoned(); test_c_local(); } diff --git a/libstdc++-v3/testsuite/std/time/format/pr117214_custom_timeput.cc b/libstdc++-v3/testsuite/std/time/format/pr117214_custom_timeput.cc new file mode 100644 index 0000000..03b9496 --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/format/pr117214_custom_timeput.cc @@ -0,0 +1,37 @@ +// { dg-do run { target c++20 } } + +#include <chrono> +#include <format> +#include <locale> +#include <testsuite_hooks.h> + +struct custom_time_put : std::time_put<char> +{ + iter_type + do_put(iter_type out, std::ios_base& io, char_type fill, const tm* t, + char format, char modifier) const override + { + using Base = std::time_put<char>; + + switch (format) { + case 'a': case 'A': case 'b': case 'h': case 'B': case 'p': + *out++ = '['; + *out++ = format; + *out++ = ']'; + } + return Base::do_put(out, io, fill, t, format, modifier); + } +}; + +int main() +{ + using namespace std::chrono; + std::locale loc(std::locale::classic(), new custom_time_put); +#define test(t, fmt, exp) VERIFY( std::format(loc, fmt, t) == exp ) + test(Monday, "{:L%a}", "[a]Mon"); + test(Monday, "{:L%A}", "[A]Monday"); + test(January, "{:L%b}", "[b]Jan"); + test(January, "{:L%h}", "[h]Jan"); + test(January, "{:L%B}", "[B]January"); + test(1h, "{:L%p}", "[p]AM"); +} diff --git a/libstdc++-v3/testsuite/std/time/format/pr120114.cc b/libstdc++-v3/testsuite/std/time/format/pr120114.cc new file mode 100644 index 0000000..cdde468 --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/format/pr120114.cc @@ -0,0 +1,125 @@ +// { dg-do run { target c++23 } } +// { dg-options "-fexec-charset=UTF-8" } +// { dg-timeout-factor 2 } + +#include <algorithm> +#include <chrono> +#include <testsuite_hooks.h> + +#define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S) +#define WIDEN(S) WIDEN_(CharT, S) + +template<typename CharT> +void +test_from_format_string() +{ + std::basic_string<CharT> res; + using namespace std::chrono_literals; + auto date = 2025y/std::chrono::May/05d; + + res = std::format(WIDEN("{:+<13%F\U0001f921}"), date); + VERIFY( res == WIDEN("2025-05-05\U0001f921+") ); + + res = std::format(WIDEN("{:->15%F\U0001f921}"), date); + VERIFY( res == WIDEN("---2025-05-05\U0001f921") ); + + res = std::format(WIDEN("{:=^20%F\U0001f921}"), date); + VERIFY( res == WIDEN("====2025-05-05\U0001f921====") ); +} + +template<typename CharT> +void +test_formatted_value() +{ + // Custom time_put facet which returns Ideographic Telegraph Symbol + // for given month for Om. + struct TimePut : std::time_put<CharT> + { + using iter_type = std::time_put<CharT>::iter_type; + using char_type = std::time_put<CharT>::char_type; + + iter_type + do_put(iter_type out, std::ios_base& io, char_type fill, const tm* t, + char format, char modifier) const override + { + if (format != 'm' && modifier != 'm') + return std::time_put<CharT>::do_put(out, io, fill, t, format, modifier); + std::basic_string_view<CharT> str; + switch (t->tm_mon) + { + case 0: + str = WIDEN("\u32C0"); + break; + case 1: + str = WIDEN("\u32C1"); + break; + case 2: + str = WIDEN("\u32C2"); + break; + case 3: + str = WIDEN("\u32C3"); + break; + case 4: + str = WIDEN("\u32C4"); + break; + case 5: + str = WIDEN("\u32C5"); + break; + case 6: + str = WIDEN("\u32C6"); + break; + case 7: + str = WIDEN("\u32C7"); + break; + case 8: + str = WIDEN("\u32C8"); + break; + case 9: + str = WIDEN("\u32C9"); + break; + case 10: + str = WIDEN("\u32CA"); + break; + case 11: + str = WIDEN("\u32CB"); + break; + }; + return std::copy(str.begin(), str.end(), out); + } + }; + const std::locale loc(std::locale::classic(), new TimePut); + + std::basic_string<CharT> res; + + res = std::format(loc, WIDEN("{:<1L%Om}"), std::chrono::January); + VERIFY( res == WIDEN("\u32C0") ); + + res = std::format(loc, WIDEN("{:>2L%Om}"), std::chrono::February); + VERIFY( res == WIDEN("\u32C1") ); + + res = std::format(loc, WIDEN("{:<3L%Om}"), std::chrono::March); + VERIFY( res == WIDEN("\u32C2 ") ); + + res = std::format(loc, WIDEN("{:^4L%Om}"), std::chrono::April); + VERIFY( res == WIDEN(" \u32C3 ") ); + + res = std::format(loc, WIDEN("{:>5L%Om}"), std::chrono::May); + VERIFY( res == WIDEN(" \u32C4") ); + + res = std::format(loc, WIDEN("{:+<6L%Om}"), std::chrono::June); + VERIFY( res == WIDEN("\u32C5++++") ); + + res = std::format(loc, WIDEN("{:=^7L%Om}"), std::chrono::July); + VERIFY( res == WIDEN("==\u32C6===") ); + + res = std::format(loc, WIDEN("{:->8L%Om}"), std::chrono::August); + VERIFY( res == WIDEN("------\u32C7") ); +} + +int main() +{ + test_from_format_string<char>(); + test_from_format_string<wchar_t>(); + test_formatted_value<char>(); + test_formatted_value<wchar_t>(); +} diff --git a/libstdc++-v3/testsuite/std/time/format/pr120481.cc b/libstdc++-v3/testsuite/std/time/format/pr120481.cc new file mode 100644 index 0000000..a748acb --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/format/pr120481.cc @@ -0,0 +1,324 @@ +// { dg-do run { target c++23 } } +// { dg-options "-fexec-charset=UTF-8" } +// { dg-timeout-factor 2 } + +#include <algorithm> +#include <chrono> +#include <testsuite_hooks.h> + +#define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S) +#define WIDEN(S) WIDEN_(CharT, S) + +using namespace std::chrono; + +template<typename CharT> +void +test_year() +{ + std::basic_string<CharT> res; + + res = std::format(WIDEN("{:%Y}"), year(0)); + VERIFY( res == WIDEN("0000") ); + res = std::format(WIDEN("{:%C}"), year(0)); + VERIFY( res == WIDEN("00") ); + res = std::format(WIDEN("{:%y}"), year(0)); + VERIFY( res == WIDEN("00") ); + + res = std::format(WIDEN("{:%Y}"), year(5)); + VERIFY( res == WIDEN("0005") ); + res = std::format(WIDEN("{:%C}"), year(5)); + VERIFY( res == WIDEN("00") ); + res = std::format(WIDEN("{:%y}"), year(5)); + VERIFY( res == WIDEN("05") ); + res = std::format(WIDEN("{:%Y}"), year(-5)); + VERIFY( res == WIDEN("-0005") ); + res = std::format(WIDEN("{:%C}"), year(-5)); + VERIFY( res == WIDEN("-01") ); + res = std::format(WIDEN("{:%y}"), year(-5)); + VERIFY( res == WIDEN("05") ); + + res = std::format(WIDEN("{:%Y}"), year(213)); + VERIFY( res == WIDEN("0213") ); + res = std::format(WIDEN("{:%C}"), year(213)); + VERIFY( res == WIDEN("02") ); + res = std::format(WIDEN("{:%y}"), year(213)); + VERIFY( res == WIDEN("13") ); + res = std::format(WIDEN("{:%Y}"), year(-213)); + VERIFY( res == WIDEN("-0213") ); + res = std::format(WIDEN("{:%C}"), year(-213)); + VERIFY( res == WIDEN("-03") ); + res = std::format(WIDEN("{:%y}"), year(-213)); + VERIFY( res == WIDEN("13") ); + + res = std::format(WIDEN("{:%Y}"), year(7100)); + VERIFY( res == WIDEN("7100") ); + res = std::format(WIDEN("{:%C}"), year(7100)); + VERIFY( res == WIDEN("71") ); + res = std::format(WIDEN("{:%y}"), year(7100)); + VERIFY( res == WIDEN("00") ); + res = std::format(WIDEN("{:%Y}"), year(-7100)); + VERIFY( res == WIDEN("-7100") ); + res = std::format(WIDEN("{:%C}"), year(-7100)); + VERIFY( res == WIDEN("-71") ); + res = std::format(WIDEN("{:%y}"), year(-7100)); + VERIFY( res == WIDEN("00") ); + + res = std::format(WIDEN("{:%Y}"), year(12101)); + VERIFY( res == WIDEN("12101") ); + res = std::format(WIDEN("{:%C}"), year(12101)); + VERIFY( res == WIDEN("121") ); + res = std::format(WIDEN("{:%y}"), year(12101)); + VERIFY( res == WIDEN("01") ); + res = std::format(WIDEN("{:%Y}"), year(-12101)); + VERIFY( res == WIDEN("-12101") ); + res = std::format(WIDEN("{:%C}"), year(-12101)); + VERIFY( res == WIDEN("-122") ); + res = std::format(WIDEN("{:%y}"), year(-12101)); + VERIFY( res == WIDEN("01") ); +} + +template<typename CharT> +void +test_month() +{ + std::basic_string<CharT> res; + + res = std::format(WIDEN("{:%m}"), month(5)); + VERIFY( res == WIDEN("05") ); + res = std::format(WIDEN("{:%m}"), month(50)); + VERIFY( res == WIDEN("50") ); + res = std::format(WIDEN("{:%m}"), month(127)); + VERIFY( res == WIDEN("127") ); + res = std::format(WIDEN("{:%m}"), month(254)); + VERIFY( res == WIDEN("254") ); +} + +template<typename CharT> +void +test_day() +{ + std::basic_string<CharT> res; + + res = std::format(WIDEN("{:%d}"), day(3)); + VERIFY( res == WIDEN("03") ); + res = std::format(WIDEN("{:%d}"), day(22)); + VERIFY( res == WIDEN("22") ); + res = std::format(WIDEN("{:%d}"), day(100)); + VERIFY( res == WIDEN("100") ); + res = std::format(WIDEN("{:%d}"), day(207)); + VERIFY( res == WIDEN("207") ); + + res = std::format(WIDEN("{:%e}"), day(5)); + VERIFY( res == WIDEN(" 5") ); + res = std::format(WIDEN("{:%e}"), day(99)); + VERIFY( res == WIDEN("99") ); + res = std::format(WIDEN("{:%e}"), day(183)); + VERIFY( res == WIDEN("183") ); + res = std::format(WIDEN("{:%e}"), day(214)); + VERIFY( res == WIDEN("214") ); +} + +template<typename CharT> +void +test_date() +{ + std::basic_string<CharT> res; + + res = std::format(WIDEN("{:%F}"), year(-22)/month(10)/day(20)); + VERIFY( res == WIDEN("-0022-10-20") ); + res = std::format(WIDEN("{:%D}"), year(-22)/month(10)/day(20)); + VERIFY( res == WIDEN("10/20/22") ); + + res = std::format(WIDEN("{:%F}"), year(-2020)/month(123)/day(44)); + VERIFY( res == WIDEN("-2020-123-44") ); + res = std::format(WIDEN("{:%D}"), year(-2020)/month(123)/day(44)); + VERIFY( res == WIDEN("123/44/20") ); + + res = std::format(WIDEN("{:%F}"), year(-23404)/month(99)/day(223)); + VERIFY( res == WIDEN("-23404-99-223") ); + res = std::format(WIDEN("{:%D}"), year(-23404)/month(99)/day(223)); + VERIFY( res == WIDEN("99/223/04") ); + + res = std::format(WIDEN("{:%F}"), year(10000)/month(220)/day(100)); + VERIFY( res == WIDEN("10000-220-100") ); + res = std::format(WIDEN("{:%D}"), year(10000)/month(220)/day(100)); + VERIFY( res == WIDEN("220/100/00") ); +} + +template<typename CharT> +void +test_weekday() +{ + std::basic_string<CharT> res; + + res = std::format(WIDEN("{:%w}"), weekday(0)); + VERIFY( res == WIDEN("0") ); + res = std::format(WIDEN("{:%u}"), weekday(0)); + VERIFY( res == WIDEN("7") ); + + res = std::format(WIDEN("{:%w}"), weekday(7)); + VERIFY( res == WIDEN("0") ); + res = std::format(WIDEN("{:%u}"), weekday(7)); + VERIFY( res == WIDEN("7") ); + + res = std::format(WIDEN("{:%w}"), weekday(8)); + VERIFY( res == WIDEN("8") ); + res = std::format(WIDEN("{:%u}"), weekday(8)); + VERIFY( res == WIDEN("8") ); + + res = std::format(WIDEN("{:%w}"), weekday(10)); + VERIFY( res == WIDEN("10") ); + res = std::format(WIDEN("{:%u}"), weekday(10)); + VERIFY( res == WIDEN("10") ); + + res = std::format(WIDEN("{:%w}"), weekday(76)); + VERIFY( res == WIDEN("76") ); + res = std::format(WIDEN("{:%u}"), weekday(76)); + VERIFY( res == WIDEN("76") ); + + res = std::format(WIDEN("{:%w}"), weekday(100)); + VERIFY( res == WIDEN("100") ); + res = std::format(WIDEN("{:%u}"), weekday(100)); + VERIFY( res == WIDEN("100") ); + + res = std::format(WIDEN("{:%w}"), weekday(202)); + VERIFY( res == WIDEN("202") ); + res = std::format(WIDEN("{:%u}"), weekday(202)); + VERIFY( res == WIDEN("202") ); +} + +template<typename CharT> +void +test_hour() +{ + std::basic_string<CharT> res; + + res = std::format(WIDEN("{:%H}"), 0h + 5min + 6s); + VERIFY( res == WIDEN("00") ); + res = std::format(WIDEN("{:%R}"), 0h + 5min + 6s); + VERIFY( res == WIDEN("00:05") ); + res = std::format(WIDEN("{:%T}"), 0h + 5min + 6s); + VERIFY( res == WIDEN("00:05:06") ); + res = std::format(WIDEN("{:%I}"), 0h + 5min + 6s); + VERIFY( res == WIDEN("12") ); + res = std::format(WIDEN("{:%p}"), 0h + 5min + 6s); + VERIFY( res == WIDEN("AM") ); + + res = std::format(WIDEN("{:%H}"), 7h + 15min + 6s); + VERIFY( res == WIDEN("07") ); + res = std::format(WIDEN("{:%R}"), 7h + 15min + 6s); + VERIFY( res == WIDEN("07:15") ); + res = std::format(WIDEN("{:%T}"), 7h + 15min + 6s); + VERIFY( res == WIDEN("07:15:06") ); + res = std::format(WIDEN("{:%I}"), 7h + 15min + 6s); + VERIFY( res == WIDEN("07") ); + res = std::format(WIDEN("{:%p}"), 7h + 15min + 6s); + VERIFY( res == WIDEN("AM") ); + + res = std::format(WIDEN("{:%H}"), 15h + 55min + 26s); + VERIFY( res == WIDEN("15") ); + res = std::format(WIDEN("{:%R}"), 15h + 55min + 26s); + VERIFY( res == WIDEN("15:55") ); + res = std::format(WIDEN("{:%T}"), 15h + 55min + 26s); + VERIFY( res == WIDEN("15:55:26") ); + res = std::format(WIDEN("{:%I}"), 15h + 55min + 26s); + VERIFY( res == WIDEN("03") ); + res = std::format(WIDEN("{:%p}"), 15h + 55min + 26s); + VERIFY( res == WIDEN("PM") ); + + res = std::format(WIDEN("{:%H}"), 50h + 33min + 37s); + VERIFY( res == WIDEN("50") ); + res = std::format(WIDEN("{:%R}"), 50h + 33min + 37s); + VERIFY( res == WIDEN("50:33") ); + res = std::format(WIDEN("{:%T}"), 50h + 33min + 37s); + VERIFY( res == WIDEN("50:33:37") ); + res = std::format(WIDEN("{:%I}"), 50h + 33min + 37s); + VERIFY( res == WIDEN("02") ); + res = std::format(WIDEN("{:%p}"), 50h + 33min + 37s); + VERIFY( res == WIDEN("AM") ); + + res = std::format(WIDEN("{:%H}"), 100h + 21min + 48s); + VERIFY( res == WIDEN("100") ); + res = std::format(WIDEN("{:%R}"), 100h + 21min + 48s); + VERIFY( res == WIDEN("100:21") ); + res = std::format(WIDEN("{:%T}"), 100h + 21min + 48s); + VERIFY( res == WIDEN("100:21:48") ); + res = std::format(WIDEN("{:%I}"), 100h + 21min + 48s); + VERIFY( res == WIDEN("04") ); + res = std::format(WIDEN("{:%p}"), 100h + 21min + 48s); + VERIFY( res == WIDEN("AM") ); + + res = std::format(WIDEN("{:%H}"), 228h + 45min + 33s); + VERIFY( res == WIDEN("228") ); + res = std::format(WIDEN("{:%R}"), 228h + 45min + 33s); + VERIFY( res == WIDEN("228:45") ); + res = std::format(WIDEN("{:%T}"), 228h + 45min + 33s); + VERIFY( res == WIDEN("228:45:33") ); + res = std::format(WIDEN("{:%I}"), 228h + 4min + 33s); + VERIFY( res == WIDEN("12") ); + res = std::format(WIDEN("{:%p}"), 228h + 4min + 33s); + VERIFY( res == WIDEN("PM") ); + + res = std::format(WIDEN("{:%H}"), 1024h + 3min); + VERIFY( res == WIDEN("1024") ); + res = std::format(WIDEN("{:%R}"), 1024h + 3min); + VERIFY( res == WIDEN("1024:03") ); + res = std::format(WIDEN("{:%T}"), 1024h + 3min); + VERIFY( res == WIDEN("1024:03:00") ); + res = std::format(WIDEN("{:%I}"), 1024h + 3min); + VERIFY( res == WIDEN("04") ); + res = std::format(WIDEN("{:%p}"), 1024h + 3min); + VERIFY( res == WIDEN("PM") ); + + res = std::format(WIDEN("{:%H}"), 2039h); + VERIFY( res == WIDEN("2039") ); + res = std::format(WIDEN("{:%R}"), 2039h); + VERIFY( res == WIDEN("2039:00") ); + res = std::format(WIDEN("{:%T}"), 2039h); + VERIFY( res == WIDEN("2039:00:00") ); + res = std::format(WIDEN("{:%I}"), 2039h); + VERIFY( res == WIDEN("11") ); + res = std::format(WIDEN("{:%p}"), 2039h); + VERIFY( res == WIDEN("PM") ); + + res = std::format(WIDEN("{:%H}"), 22111h + 59min + 59s); + VERIFY( res == WIDEN("22111") ); + res = std::format(WIDEN("{:%R}"), 22111h + 59min + 59s); + VERIFY( res == WIDEN("22111:59") ); + res = std::format(WIDEN("{:%T}"), 22111h + 59min + 59s); + VERIFY( res == WIDEN("22111:59:59") ); + res = std::format(WIDEN("{:%I}"), 22111h + 59min + 59s); + VERIFY( res == WIDEN("07") ); + res = std::format(WIDEN("{:%p}"), 22111h + 59min + 59s); + VERIFY( res == WIDEN("AM") ); + + res = std::format(WIDEN("{:%H}"), -22111h - 59min - 59s); + VERIFY( res == WIDEN("-22111") ); + res = std::format(WIDEN("{:%R}"), -22111h - 59min - 59s); + VERIFY( res == WIDEN("-22111:59") ); + res = std::format(WIDEN("{:%T}"), -22111h - 59min - 59s); + VERIFY( res == WIDEN("-22111:59:59") ); + res = std::format(WIDEN("{:%I}"), -22111h - 59min - 59s); + VERIFY( res == WIDEN("-07") ); + res = std::format(WIDEN("{:%p}"), -22111h - 59min - 59s); + VERIFY( res == WIDEN("AM") ); +} + +int main() +{ + test_year<char>(); + test_month<char>(); + test_day<char>(); + test_date<char>(); + test_weekday<char>(); + test_hour<char>(); + +#ifdef _GLIBCXX_USE_WCHAR_T + test_year<wchar_t>(); + test_month<wchar_t>(); + test_day<wchar_t>(); + test_date<wchar_t>(); + test_weekday<wchar_t>(); + test_hour<wchar_t>(); +#endif // _GLIBCXX_USE_WCHAR_T +} diff --git a/libstdc++-v3/testsuite/std/time/format/precision.cc b/libstdc++-v3/testsuite/std/time/format/precision.cc new file mode 100644 index 0000000..aa26615 --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/format/precision.cc @@ -0,0 +1,201 @@ +// { dg-do run { target c++20 } } + +#include <chrono> +#include <ranges> +#include <testsuite_hooks.h> + +using namespace std::chrono; + +#define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S) +#define WIDEN(S) WIDEN_(CharT, S) + +template<typename CharT> +void +test_empty() +{ + std::basic_string<CharT> res; + + const duration<double> d(33.111222); + res = std::format(WIDEN("{:}"), d); + VERIFY( res == WIDEN("33.1112s") ); + res = std::format(WIDEN("{:.0}"), d); + VERIFY( res == WIDEN("33.1112s") ); + res = std::format(WIDEN("{:.3}"), d); + VERIFY( res == WIDEN("33.1112s") ); + res = std::format(WIDEN("{:.6}"), d); + VERIFY( res == WIDEN("33.1112s") ); + res = std::format(WIDEN("{:.9}"), d); + VERIFY( res == WIDEN("33.1112s") ); + + // Uses ostream operator<< + const duration<double, std::nano> nd = d; + res = std::format(WIDEN("{:}"), nd); + VERIFY( res == WIDEN("3.31112e+10ns") ); + res = std::format(WIDEN("{:.0}"), nd); + VERIFY( res == WIDEN("3.31112e+10ns") ); + res = std::format(WIDEN("{:.3}"), nd); + VERIFY( res == WIDEN("3.31112e+10ns") ); + res = std::format(WIDEN("{:.6}"), nd); + VERIFY( res == WIDEN("3.31112e+10ns") ); + res = std::format(WIDEN("{:.9}"), nd); + VERIFY( res == WIDEN("3.31112e+10ns") ); +} + +template<typename CharT> +void +test_Q() +{ + std::basic_string<CharT> res; + + const duration<double> d(7.111222); + res = std::format(WIDEN("{:%Q}"), d); + VERIFY( res == WIDEN("7.111222") ); + res = std::format(WIDEN("{:.0%Q}"), d); + VERIFY( res == WIDEN("7.111222") ); + res = std::format(WIDEN("{:.3%Q}"), d); + VERIFY( res == WIDEN("7.111222") ); + res = std::format(WIDEN("{:.6%Q}"), d); + VERIFY( res == WIDEN("7.111222") ); + res = std::format(WIDEN("{:.9%Q}"), d); + VERIFY( res == WIDEN("7.111222") ); + + duration<double, std::milli> md = d; + res = std::format(WIDEN("{:%Q}"), md); + VERIFY( res == WIDEN("7111.222") ); + res = std::format(WIDEN("{:.0%Q}"), md); + VERIFY( res == WIDEN("7111.222") ); + res = std::format(WIDEN("{:.3%Q}"), md); + VERIFY( res == WIDEN("7111.222") ); + res = std::format(WIDEN("{:.6%Q}"), md); + VERIFY( res == WIDEN("7111.222") ); + res = std::format(WIDEN("{:.9%Q}"), md); + VERIFY( res == WIDEN("7111.222") ); + + const duration<double, std::nano> nd = d; + res = std::format(WIDEN("{:%Q}"), nd); + VERIFY( res == WIDEN("7111222000") ); + res = std::format(WIDEN("{:.0%Q}"), nd); + VERIFY( res == WIDEN("7111222000") ); + res = std::format(WIDEN("{:.3%Q}"), nd); + VERIFY( res == WIDEN("7111222000") ); + res = std::format(WIDEN("{:.6%Q}"), nd); + VERIFY( res == WIDEN("7111222000") ); + res = std::format(WIDEN("{:.9%Q}"), nd); + VERIFY( res == WIDEN("7111222000") ); +} + +template<typename CharT> +void +test_S_fp() +{ + std::basic_string<CharT> res; + + // Precision is ignored, but period affects output + duration<double> d(5.111222); + res = std::format(WIDEN("{:%S}"), d); + VERIFY( res == WIDEN("05") ); + res = std::format(WIDEN("{:.0%S}"), d); + VERIFY( res == WIDEN("05") ); + res = std::format(WIDEN("{:.3%S}"), d); + VERIFY( res == WIDEN("05") ); + res = std::format(WIDEN("{:.6%S}"), d); + VERIFY( res == WIDEN("05") ); + res = std::format(WIDEN("{:.9%S}"), d); + VERIFY( res == WIDEN("05") ); + + duration<double, std::milli> md = d; + res = std::format(WIDEN("{:%S}"), md); + VERIFY( res == WIDEN("05.111") ); + res = std::format(WIDEN("{:.0%S}"), md); + VERIFY( res == WIDEN("05.111") ); + res = std::format(WIDEN("{:.3%S}"), md); + VERIFY( res == WIDEN("05.111") ); + res = std::format(WIDEN("{:.6%S}"), md); + VERIFY( res == WIDEN("05.111") ); + res = std::format(WIDEN("{:.9%S}"), md); + VERIFY( res == WIDEN("05.111") ); + + duration<double, std::micro> ud = d; + res = std::format(WIDEN("{:%S}"), ud); + VERIFY( res == WIDEN("05.111222") ); + res = std::format(WIDEN("{:.0%S}"), ud); + VERIFY( res == WIDEN("05.111222") ); + res = std::format(WIDEN("{:.3%S}"), ud); + VERIFY( res == WIDEN("05.111222") ); + res = std::format(WIDEN("{:.6%S}"), ud); + VERIFY( res == WIDEN("05.111222") ); + res = std::format(WIDEN("{:.9%S}"), ud); + VERIFY( res == WIDEN("05.111222") ); + + duration<double, std::nano> nd = d; + res = std::format(WIDEN("{:%S}"), nd); + VERIFY( res == WIDEN("05.111222000") ); + res = std::format(WIDEN("{:.0%S}"), nd); + VERIFY( res == WIDEN("05.111222000") ); + res = std::format(WIDEN("{:.3%S}"), nd); + VERIFY( res == WIDEN("05.111222000") ); + res = std::format(WIDEN("{:.6%S}"), nd); + VERIFY( res == WIDEN("05.111222000") ); + res = std::format(WIDEN("{:.9%S}"), nd); + VERIFY( res == WIDEN("05.111222000") ); + + duration<double, std::pico> pd = d; + res = std::format(WIDEN("{:%S}"), pd); + VERIFY( res == WIDEN("05.111222000000") ); + res = std::format(WIDEN("{:.0%S}"), pd); + VERIFY( res == WIDEN("05.111222000000") ); + res = std::format(WIDEN("{:.3%S}"), pd); + VERIFY( res == WIDEN("05.111222000000") ); + res = std::format(WIDEN("{:.6%S}"), pd); + VERIFY( res == WIDEN("05.111222000000") ); + res = std::format(WIDEN("{:.9%S}"), pd); + VERIFY( res == WIDEN("05.111222000000") ); +} + +template<typename CharT> +void +test_S_int() +{ + std::basic_string<CharT> res; + const nanoseconds src(7'000'012'345); + + auto d = floor<seconds>(src); + res = std::format(WIDEN("{:%S}"), d); + VERIFY( res == WIDEN("07") ); + + auto md = floor<milliseconds>(src); + res = std::format(WIDEN("{:%S}"), md); + VERIFY( res == WIDEN("07.000") ); + + auto ud = floor<microseconds>(src); + res = std::format(WIDEN("{:%S}"), ud); + VERIFY( res == WIDEN("07.000012") ); + + auto nd = floor<nanoseconds>(src); + res = std::format(WIDEN("{:%S}"), nd); + VERIFY( res == WIDEN("07.000012345") ); + + using picoseconds = duration<unsigned long long, std::pico>; + auto pd = floor<picoseconds>(src); + res = std::format(WIDEN("{:%S}"), pd); + VERIFY( res == WIDEN("07.000012345000") ); +} + +template<typename CharT> +void +test_all() +{ + test_empty<CharT>(); + test_Q<CharT>(); + test_S_int<CharT>(); + test_S_fp<CharT>(); +} + +int main() +{ + test_all<char>(); + +#ifdef _GLIBCXX_USE_WCHAR_T + test_all<wchar_t>(); +#endif // _GLIBCXX_USE_WCHAR_T +} diff --git a/libstdc++-v3/testsuite/std/time/format/whitespace.cc b/libstdc++-v3/testsuite/std/time/format/whitespace.cc new file mode 100644 index 0000000..debda08 --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/format/whitespace.cc @@ -0,0 +1,56 @@ +// { dg-do run { target c++20 } } + +#include <chrono> +#include <testsuite_hooks.h> + +using namespace std::chrono; + +#define WIDEN_(C, S) ::std::__format::_Widen<C>(S, L##S) +#define WIDEN(S) WIDEN_(_CharT, S) + +template<typename _CharT, typename ChronoType> +void +test(const ChronoType& ct) +{ + std::basic_string<_CharT> res; + + res = std::format(WIDEN("{:%% %t %n more text}"), ct); + VERIFY( res == WIDEN("% \t \n more text") ); + + res = std::format(WIDEN("{:7%% %t %n}"), ct); + VERIFY( res == WIDEN("% \t \n ") ); + + res = std::format(WIDEN("{:>6%% %t %n}"), ct); + VERIFY( res == WIDEN(" % \t \n") ); + + res = std::format(WIDEN("{:+>7%% %t %n}"), ct); + VERIFY( res == WIDEN("++% \t \n") ); + + res = std::format(WIDEN("{:=^7%% %t %n}"), ct); + VERIFY( res == WIDEN("=% \t \n=") ); +} + +template<typename CharT> +void +test_all() +{ + test<CharT>(20s); + test<CharT>(10d); + test<CharT>(Monday); + test<CharT>(2020y/January/8); + test<CharT>(local_days(2020y/January/8)); + test<CharT>(sys_days(2020y/January/8) + 13h + 10min + 5s); +#if _GLIBCXX_USE_CXX11_ABI || ! _GLIBCXX_USE_DUAL_ABI + test<CharT>(sys_info()); + test<CharT>(local_info()); +#endif +} + +int main() +{ + test_all<char>(); + +#ifdef _GLIBCXX_USE_WCHAR_T + test_all<wchar_t>(); +#endif // _GLIBCXX_USE_WCHAR_T +} diff --git a/libstdc++-v3/testsuite/std/time/hash.cc b/libstdc++-v3/testsuite/std/time/hash.cc new file mode 100644 index 0000000..ed9139b --- /dev/null +++ b/libstdc++-v3/testsuite/std/time/hash.cc @@ -0,0 +1,280 @@ +// { dg-do run { target c++26 } } + +#include <chrono> +#include <unordered_set> +#include <limits.h> +#include <testsuite_hooks.h> + +#if _GLIBCXX_USE_CXX11_ABI +# if !defined(__cpp_lib_chrono) +# error "__cpp_lib_chrono not defined" +# elif __cpp_lib_chrono < 202306L +# error "Wrong value for __cpp_lib_chrono" +# endif +#endif + +template <typename T> +struct arithmetic_wrapper +{ + arithmetic_wrapper() = default; + arithmetic_wrapper(T t) : t(t) {} + friend bool operator==(arithmetic_wrapper, arithmetic_wrapper) = default; + T t; +}; + +template <typename T> +struct std::hash<arithmetic_wrapper<T>> +{ + size_t operator()(arithmetic_wrapper<T> val) const noexcept + { return std::hash<T>{}(val.t); } +}; + +template <typename T> +struct non_hashable_arithmetic_wrapper +{ + non_hashable_arithmetic_wrapper() = default; + non_hashable_arithmetic_wrapper(T t) : t(t) {} + friend bool operator==(non_hashable_arithmetic_wrapper, non_hashable_arithmetic_wrapper) = default; + T t; +}; + +template <typename T> +constexpr bool is_hash_poisoned = !std::is_default_constructible_v<std::hash<T>>; + +template <typename T> +void test_unordered_set(const T& t) +{ + std::unordered_set<T> set; + + set.insert(t); + VERIFY(set.size() == 1); + VERIFY(set.contains(t)); + + set.erase(t); + VERIFY(set.size() == 0); + VERIFY(!set.contains(t)); +} + +template <typename T> +void test_hash(const T& t) +{ + static_assert(noexcept(std::hash<T>{}(t))); + static_assert(std::__is_fast_hash<T>::value); + test_unordered_set(t); +} + +void test01() +{ + using namespace std::chrono; + using namespace std::literals::chrono_literals; + + // duration + test_hash(-999s); + test_hash(1234ms); +#if defined __SIZEOF_INT128__ + test_hash(duration<__int128>(123456)); +#endif + test_hash(duration<double>(123.45)); + using AWint = arithmetic_wrapper<int>; + test_hash(duration<AWint>(AWint(1234))); + using AWdouble = arithmetic_wrapper<double>; + test_hash(duration<AWdouble>(AWdouble(123.45))); + + // time_point + test_hash(sys_seconds(1234s)); +#if defined __SIZEOF_INT128__ + test_hash(sys_time<duration<__int128>>(duration<__int128>(123456))); +#endif + test_hash(sys_time<duration<double>>(duration<double>(123.45))); + test_hash(utc_seconds(1234s)); + test_hash(local_days(days(1234))); + test_hash(system_clock::now()); + test_hash(steady_clock::now()); + test_hash(utc_clock::now()); + test_hash(gps_clock::now()); + + // day + test_hash(1d); + test_hash(0d); + test_hash(255d); + test_hash(1234d); + test_hash(day(UINT_MAX)); + + // month + test_hash(January); + test_hash(September); + test_hash(month(0u)); + test_hash(month(255u)); + test_hash(month(1234u)); + test_hash(month(UINT_MAX)); + + // year + test_hash(2024y); + test_hash(0y); + test_hash(year::min()); + test_hash(year::max()); + test_hash(year(INT_MAX)); + test_hash(year(INT_MIN)); + + // weekday + test_hash(Monday); + test_hash(Thursday); + test_hash(weekday(255u)); + test_hash(weekday(UINT_MAX)); + + // weekday_indexed + test_hash(Monday[0u]); + test_hash(Monday[7u]); + test_hash(Monday[1234u]); + test_hash(weekday(1234u)[0u]); + + // weekday_last + test_hash(Monday[last]); + test_hash(Friday[last]); + test_hash(weekday(1234u)[last]); + + // month_day + test_hash(March / 3); + test_hash(March / 31); + test_hash(February / 31); + test_hash(February / 1234); + test_hash(month(1234u) / 1); + + // month_day_last + test_hash(March / last); + test_hash(month(1234u) / last); + + // month_weekday + test_hash(March / Tuesday[2u]); + test_hash(month(1234u) / Tuesday[2u]); + test_hash(March / weekday(1234u)[2u]); + test_hash(March / Tuesday[1234u]); + + // month_weekday_last + test_hash(April / Sunday[last]); + test_hash(month(1234u) / Tuesday[last]); + test_hash(April / weekday(1234u)[last]); + + // year_month + test_hash(2024y / August); + test_hash(1'000'000y / August); + test_hash(2024y / month(1234u)); + + // year_month_day + test_hash(2024y / August / 31); + test_hash(-10y / March / 5); + test_hash(2024y / February / 31); + test_hash(1'000'000y / March / 5); + test_hash(2024y / month(1234u) / 5); + test_hash(2024y / March / 1234); + + // year_month_day_last + test_hash(2024y / August / last); + test_hash(1'000'000y / August / last); + test_hash(2024y / month(1234u) / last); + + // year_month_weekday + test_hash(2024y / August / Tuesday[2u]); + test_hash(-10y / August / Tuesday[2u]); + test_hash(1'000'000y / August / Tuesday[2u]); + test_hash(2024y / month(1234u) / Tuesday[2u]); + test_hash(2024y / August / weekday(1234u)[2u]); + test_hash(2024y / August / Tuesday[1234u]); + + // year_month_weekday_last + test_hash(2024y / August / Tuesday[last]); + test_hash(-10y / August / Tuesday[last]); + test_hash(1'000'000y / August / Tuesday[last]); + test_hash(2024y / month(1234u) / Tuesday[last]); + test_hash(2024y / August / weekday(1234u)[last]); + +#if _GLIBCXX_USE_CXX11_ABI + // zoned_time + test_hash(zoned_seconds("Europe/Rome", sys_seconds(1234s))); + test_hash(zoned_time("Europe/Rome", system_clock::now())); + + // leap_second + for (leap_second l : get_tzdb().leap_seconds) + test_hash(l); +#endif +} + +void test02() +{ + using namespace std::chrono; + using namespace std::literals::chrono_literals; + + { + std::unordered_set<milliseconds> set; + set.insert(2000ms); + VERIFY(set.contains(2000ms)); + VERIFY(set.contains(2s)); + VERIFY(!set.contains(1234ms)); + VERIFY(!set.contains(1234s)); + } + { + using TP = sys_time<milliseconds>; + std::unordered_set<TP> set; + set.insert(TP(2000ms)); + VERIFY(set.contains(TP(2000ms))); + VERIFY(set.contains(sys_seconds(2s))); + VERIFY(!set.contains(TP(1234ms))); + VERIFY(!set.contains(sys_seconds(1234s))); + } +} + +void test03() +{ + using namespace std::chrono; + + static constexpr + auto test_hash = []<typename T>(const T& t) + { + static_assert(noexcept(std::hash<T>{}(t))); + }; + + static constexpr + auto test = []<typename D>(const D& d) + { + test_hash(d); + test_hash(sys_time<D>(d)); +#if _GLIBCXX_USE_CXX11_ABI + test_hash(zoned_time<D>(sys_time<D>(d))); +#endif + }; + + test(duration<int>(123)); + test(duration<int, std::ratio<1, 1000>>(123)); + test(duration<int, std::ratio<1000, 1>>(123)); + test(duration<double>(123.456)); + test(duration<arithmetic_wrapper<int>>(arithmetic_wrapper<int>(123))); +} + +void test04() +{ + using namespace std::chrono; + + static_assert(!is_hash_poisoned<duration<int>>); + static_assert(!is_hash_poisoned<duration<double>>); + static_assert(!is_hash_poisoned<duration<arithmetic_wrapper<int>>>); + static_assert(!is_hash_poisoned<duration<arithmetic_wrapper<double>>>); + static_assert(is_hash_poisoned<duration<non_hashable_arithmetic_wrapper<int>>>); + static_assert(is_hash_poisoned<duration<non_hashable_arithmetic_wrapper<double>>>); + +#if _GLIBCXX_USE_CXX11_ABI + static_assert(!is_hash_poisoned<zoned_time<duration<int>>>); + static_assert(!is_hash_poisoned<zoned_time<duration<double>>>); + static_assert(!is_hash_poisoned<zoned_time<duration<arithmetic_wrapper<int>>>>); + static_assert(!is_hash_poisoned<zoned_time<duration<arithmetic_wrapper<double>>>>); + static_assert(is_hash_poisoned<zoned_time<duration<non_hashable_arithmetic_wrapper<int>>>>); + static_assert(is_hash_poisoned<zoned_time<duration<non_hashable_arithmetic_wrapper<double>>>>); +#endif +} + +int main() +{ + test01(); + test02(); + test03(); + test04(); +} diff --git a/libstdc++-v3/testsuite/std/time/month/io.cc b/libstdc++-v3/testsuite/std/time/month/io.cc index 99ec073..edfa196 100644 --- a/libstdc++-v3/testsuite/std/time/month/io.cc +++ b/libstdc++-v3/testsuite/std/time/month/io.cc @@ -24,6 +24,9 @@ test_ostream() ss.imbue(std::locale(ISO_8859(15,fr_FR))); ss << month(1); VERIFY( ss.str() == "janv." ); + ss.str(""); + ss << month(0) << '|' << month(13); + VERIFY( ss.str() == "0 is not a valid month|13 is not a valid month" ); } void @@ -66,6 +69,10 @@ test_format() VERIFY( s == "Jan" ); s = std::format(loc_fr, "{:L%b}", month(1)); VERIFY( s == "janv." ); + s = std::format(loc_fr, "{:L}", month(0)); + VERIFY( s == "0 is not a valid month" ); + s = std::format(loc_fr, "{:L}", month(13)); + VERIFY( s == "13 is not a valid month" ); std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ"; std::string_view my_specs = "bBhm"; diff --git a/libstdc++-v3/testsuite/std/time/month_day/io.cc b/libstdc++-v3/testsuite/std/time/month_day/io.cc index 30aa588..c3ae180 100644 --- a/libstdc++-v3/testsuite/std/time/month_day/io.cc +++ b/libstdc++-v3/testsuite/std/time/month_day/io.cc @@ -23,6 +23,45 @@ test_ostream() } void +test_format() +{ + using namespace std::chrono; + std::locale loc_fr(ISO_8859(15,fr_FR)); + + auto s = std::format("{:%b%%%B%t%m%n %d%%%e}", month(1)/day(3)); + VERIFY( s == "Jan%January\t01\n 03% 3" ); + s = std::format(loc_fr, "{:L%b%%%B%t%m%n %d%%%e}", month(1)/day(3)); + VERIFY( s == "janv.%janvier\t01\n 03% 3"); + + s = std::format("{0:%m/%d} {0}", month(10)/day(13)); + VERIFY( s == "10/13 Oct/13" ); + s = std::format("{0:%m/%d} {0}", month(13)/day(34)); + VERIFY( s == "13/34 13 is not a valid month/34 is not a valid day" ); + + std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ"; + std::string_view my_specs = "bBdehm"; + for (char c : specs) + { + char fmt[] = { '{', ':', '%', c, '}' }; + try + { + auto md = month(1)/day(10); + (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(md)); + // The call above should throw for any conversion-spec not in my_specs: + VERIFY(my_specs.find(c) != my_specs.npos); + } + catch (const std::format_error& e) + { + VERIFY(my_specs.find(c) == my_specs.npos); + std::string_view s = e.what(); + // Libstdc++-specific message: + VERIFY(s.find("format argument does not contain the information " + "required by the chrono-specs") != s.npos); + } + } +} + +void test_parse() { using namespace std::chrono; @@ -102,6 +141,6 @@ test_parse() int main() { test_ostream(); - // TODO: test_format(); + test_format(); test_parse(); } diff --git a/libstdc++-v3/testsuite/std/time/month_day_last/io.cc b/libstdc++-v3/testsuite/std/time/month_day_last/io.cc index c12cee8..484a8d8 100644 --- a/libstdc++-v3/testsuite/std/time/month_day_last/io.cc +++ b/libstdc++-v3/testsuite/std/time/month_day_last/io.cc @@ -22,9 +22,47 @@ test_ostream() VERIFY( ss.str() == "juil./last" ); } +void +test_format() +{ + using namespace std::chrono; + std::locale loc_fr(ISO_8859(15,fr_FR)); + + auto s = std::format("{:%b%%%B%t%m%n}", month(3)/last); + VERIFY( s == "Mar%March\t03\n" ); + s = std::format(loc_fr, "{:L%b%%%B%t%m%n}", month(3)/last); + VERIFY( s == "mars%mars\t03\n"); + + s = std::format("{0:%m/last} {0}", month(4)/last); + VERIFY( s == "04/last Apr/last" ); + s = std::format("{0:%m/last} {0}", month(0)/last); + VERIFY( s == "00/last 0 is not a valid month/last" ); + + std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ"; + std::string_view my_specs = "bBhm"; + for (char c : specs) + { + char fmt[] = { '{', ':', '%', c, '}' }; + try + { + auto mdl = month(1)/last; + (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(mdl)); + // The call above should throw for any conversion-spec not in my_specs: + VERIFY(my_specs.find(c) != my_specs.npos); + } + catch (const std::format_error& e) + { + VERIFY(my_specs.find(c) == my_specs.npos); + std::string_view s = e.what(); + // Libstdc++-specific message: + VERIFY(s.find("format argument does not contain the information " + "required by the chrono-specs") != s.npos); + } + } +} + int main() { test_ostream(); - // TODO: test_format(); - // TODO: test_parse(); + test_format(); } diff --git a/libstdc++-v3/testsuite/std/time/month_weekday/io.cc b/libstdc++-v3/testsuite/std/time/month_weekday/io.cc index 82cac64..0c2dcaf 100644 --- a/libstdc++-v3/testsuite/std/time/month_weekday/io.cc +++ b/libstdc++-v3/testsuite/std/time/month_weekday/io.cc @@ -23,9 +23,47 @@ test_ostream() VERIFY( ss.str() == "juil./jeu.[4]" ); } +void +test_format() +{ + using namespace std::chrono; + std::locale loc_fr(ISO_8859(15,fr_FR)); + + auto s = std::format("{:%b%%%B%t%m%n %a%%%A%t%u%n%w}", month(5)/weekday(1)[2]); + VERIFY( s == "May%May\t05\n Mon%Monday\t1\n1" ); + s = std::format(loc_fr, "{:L%b%%%B%t%m%n %a%%%A%t%u%n%w}", month(5)/weekday(1)[2]); + VERIFY( s == "mai%mai\t05\n lun.%lundi\t1\n1"); + + s = std::format("{0:%m/%u[]} {0}", month(9)/weekday(0)[2]); + VERIFY( s == "09/7[] Sep/Sun[2]" ); + s = std::format("{0:%m/%u[]} {0}", month(111)/weekday(8)[0]); + VERIFY( s == "111/8[] 111 is not a valid month/8 is not a valid weekday[0 is not a valid index]" ); + + std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ"; + std::string_view my_specs = "aAbBhmuw"; + for (char c : specs) + { + char fmt[] = { '{', ':', '%', c, '}' }; + try + { + auto mwi = month(1)/weekday(1)[1]; + (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(mwi)); + // The call above should throw for any conversion-spec not in my_specs: + VERIFY(my_specs.find(c) != my_specs.npos); + } + catch (const std::format_error& e) + { + VERIFY(my_specs.find(c) == my_specs.npos); + std::string_view s = e.what(); + // Libstdc++-specific message: + VERIFY(s.find("format argument does not contain the information " + "required by the chrono-specs") != s.npos); + } + } +} + int main() { test_ostream(); - // TODO: test_format(); - // TODO: test_parse(); + test_format(); } diff --git a/libstdc++-v3/testsuite/std/time/month_weekday_last/io.cc b/libstdc++-v3/testsuite/std/time/month_weekday_last/io.cc index 47968d0..2c29258 100644 --- a/libstdc++-v3/testsuite/std/time/month_weekday_last/io.cc +++ b/libstdc++-v3/testsuite/std/time/month_weekday_last/io.cc @@ -23,9 +23,47 @@ test_ostream() VERIFY( ss.str() == "juil./jeu.[last]" ); } +void +test_format() +{ + using namespace std::chrono; + std::locale loc_fr(ISO_8859(15,fr_FR)); + + auto s = std::format("{:%b%%%B%t%m%n %a%%%A%t%u%n%w}", month(6)/weekday(2)[last]); + VERIFY( s == "Jun%June\t06\n Tue%Tuesday\t2\n2" ); + s = std::format(loc_fr, "{:L%b%%%B%t%m%n %a%%%A%t%u%n%w}", month(6)/weekday(2)[last]); + VERIFY( s == "juin%juin\t06\n mar.%mardi\t2\n2"); + + s = std::format("{0:%m/%w[last]} {0}", month(8)/weekday(7)[last]); + VERIFY( s == "08/0[last] Aug/Sun[last]" ); + s = std::format("{0:%m/%w[last]} {0}", month(70)/weekday(9)[last]); + VERIFY( s == "70/9[last] 70 is not a valid month/9 is not a valid weekday[last]" ); + + std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ"; + std::string_view my_specs = "aAbBhmuw"; + for (char c : specs) + { + char fmt[] = { '{', ':', '%', c, '}' }; + try + { + auto mwl = month(1)/weekday(1)[last]; + (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(mwl)); + // The call above should throw for any conversion-spec not in my_specs: + VERIFY(my_specs.find(c) != my_specs.npos); + } + catch (const std::format_error& e) + { + VERIFY(my_specs.find(c) == my_specs.npos); + std::string_view s = e.what(); + // Libstdc++-specific message: + VERIFY(s.find("format argument does not contain the information " + "required by the chrono-specs") != s.npos); + } + } +} + int main() { test_ostream(); - // TODO: test_format(); - // TODO: test_parse(); + test_format(); } diff --git a/libstdc++-v3/testsuite/std/time/parse/parse.cc b/libstdc++-v3/testsuite/std/time/parse/parse.cc index 8bb0fd0..78c761c 100644 --- a/libstdc++-v3/testsuite/std/time/parse/parse.cc +++ b/libstdc++-v3/testsuite/std/time/parse/parse.cc @@ -309,7 +309,7 @@ test_modifiers() is >> parse("%5M", min); VERIFY( is.eof() && is.fail() ); - std::chrono::seconds s; + std::chrono::seconds s{}; is.clear(); is.str("000000000012345"); is >> parse("%12S", s); // Read more than 10 digits to check overflow logic. diff --git a/libstdc++-v3/testsuite/std/time/weekday/io.cc b/libstdc++-v3/testsuite/std/time/weekday/io.cc index a56cdae..90a9bcb 100644 --- a/libstdc++-v3/testsuite/std/time/weekday/io.cc +++ b/libstdc++-v3/testsuite/std/time/weekday/io.cc @@ -69,6 +69,8 @@ test_format() VERIFY( s == "Mon" ); s = std::format(loc_fr, "{:L%a}", weekday(1)); VERIFY( s == "lun." ); + s = std::format(loc_fr, "{:L}", weekday(25)); + VERIFY( s == "25 is not a valid weekday" ); std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ"; std::string_view my_specs = "aAuw"; diff --git a/libstdc++-v3/testsuite/std/time/weekday_indexed/io.cc b/libstdc++-v3/testsuite/std/time/weekday_indexed/io.cc index 29255ad..ae86419 100644 --- a/libstdc++-v3/testsuite/std/time/weekday_indexed/io.cc +++ b/libstdc++-v3/testsuite/std/time/weekday_indexed/io.cc @@ -22,9 +22,47 @@ test_ostream() VERIFY( ss.str() == "sam.[1]" ); } +void +test_format() +{ + using namespace std::chrono; + std::locale loc_fr(ISO_8859(15,fr_FR)); + + auto s = std::format("{:%a%%%A%t%u%n%w}", weekday(7)[3]); + VERIFY( s == "Sun%Sunday\t7\n0" ); + s = std::format(loc_fr, "{:L%a%%%A%t%u%n%w}", weekday(7)[3]); + VERIFY( s == "dim.%dimanche\t7\n0"); + + s = std::format("{0:%w[]} {0}", weekday(4)[4]); + VERIFY( s == "4[] Thu[4]" ); + s = std::format("{0:%w[]} {0}", weekday(10)[7]); + VERIFY( s == "10[] 10 is not a valid weekday[7 is not a valid index]" ); + + std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ"; + std::string_view my_specs = "aAuw"; + for (char c : specs) + { + char fmt[] = { '{', ':', '%', c, '}' }; + try + { + auto wi = weekday(1)[1]; + (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(wi)); + // The call above should throw for any conversion-spec not in my_specs: + VERIFY(my_specs.find(c) != my_specs.npos); + } + catch (const std::format_error& e) + { + VERIFY(my_specs.find(c) == my_specs.npos); + std::string_view s = e.what(); + // Libstdc++-specific message: + VERIFY(s.find("format argument does not contain the information " + "required by the chrono-specs") != s.npos); + } + } +} + int main() { test_ostream(); - // TODO: test_format(); - // TODO: test_parse(); + test_format(); } diff --git a/libstdc++-v3/testsuite/std/time/weekday_last/io.cc b/libstdc++-v3/testsuite/std/time/weekday_last/io.cc index 6f76922..49cf0d5 100644 --- a/libstdc++-v3/testsuite/std/time/weekday_last/io.cc +++ b/libstdc++-v3/testsuite/std/time/weekday_last/io.cc @@ -22,9 +22,47 @@ test_ostream() VERIFY( ss.str() == "sam.[last]" ); } +void +test_format() +{ + using namespace std::chrono; + std::locale loc_fr(ISO_8859(15,fr_FR)); + + auto s = std::format("{:%a%%%A%t%u%n%w}", weekday(5)[last]); + VERIFY( s == "Fri%Friday\t5\n5" ); + s = std::format(loc_fr, "{:L%a%%%A%t%u%n%w}", weekday(5)[last]); + VERIFY( s == "ven.%vendredi\t5\n5"); + + s = std::format("{0:%w[last]} {0}", weekday(6)[last]); + VERIFY( s == "6[last] Sat[last]" ); + s = std::format("{0:%w[last]} {0}", weekday(9)[last]); + VERIFY( s == "9[last] 9 is not a valid weekday[last]" ); + + std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ"; + std::string_view my_specs = "aAuw"; + for (char c : specs) + { + char fmt[] = { '{', ':', '%', c, '}' }; + try + { + auto wl = weekday(1)[last]; + (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(wl)); + // The call above should throw for any conversion-spec not in my_specs: + VERIFY(my_specs.find(c) != my_specs.npos); + } + catch (const std::format_error& e) + { + VERIFY(my_specs.find(c) == my_specs.npos); + std::string_view s = e.what(); + // Libstdc++-specific message: + VERIFY(s.find("format argument does not contain the information " + "required by the chrono-specs") != s.npos); + } + } +} + int main() { test_ostream(); - // TODO: test_format(); - // TODO: test_parse(); + test_format(); } diff --git a/libstdc++-v3/testsuite/std/time/year_month/io.cc b/libstdc++-v3/testsuite/std/time/year_month/io.cc index 7bb3442..3392eb3 100644 --- a/libstdc++-v3/testsuite/std/time/year_month/io.cc +++ b/libstdc++-v3/testsuite/std/time/year_month/io.cc @@ -23,6 +23,45 @@ test_ostream() } void +test_format() +{ + using namespace std::chrono; + std::locale loc_fr(ISO_8859(15,fr_FR)); + + auto s = std::format("{:%C%%%y\t%Y %b%%%B%t%m%n}", year(2019)/month(4)); + VERIFY( s == "20%19\t2019 Apr%April\t04\n" ); + s = std::format(loc_fr, "{:L%C%%%y\t%Y %b%%%B%t%m%n}", year(2019)/month(4)); + VERIFY( s == "20%19\t2019 avril%avril\t04\n"); + + s = std::format("{0:%Y/%m} {0}", year(2018)/month(2)); + VERIFY( s == "2018/02 2018/Feb" ); + s = std::format("{0:%Y/%m} {0}", year(-32768)/month(15)); + VERIFY( s == "-32768/15 -32768 is not a valid year/15 is not a valid month" ); + + std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ"; + std::string_view my_specs = "CbBhmyY"; + for (char c : specs) + { + char fmt[] = { '{', ':', '%', c, '}' }; + try + { + auto ym = year(2013)/month(1); + (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(ym)); + // The call above should throw for any conversion-spec not in my_specs: + VERIFY(my_specs.find(c) != my_specs.npos); + } + catch (const std::format_error& e) + { + VERIFY(my_specs.find(c) == my_specs.npos); + std::string_view s = e.what(); + // Libstdc++-specific message: + VERIFY(s.find("format argument does not contain the information " + "required by the chrono-specs") != s.npos); + } + } +} + +void test_parse() { using namespace std::chrono; @@ -73,6 +112,6 @@ test_parse() int main() { test_ostream(); - // TODO: test_format(); + test_format(); test_parse(); } diff --git a/libstdc++-v3/testsuite/std/time/year_month_day/io.cc b/libstdc++-v3/testsuite/std/time/year_month_day/io.cc index 632b7a0..5ce2794 100644 --- a/libstdc++-v3/testsuite/std/time/year_month_day/io.cc +++ b/libstdc++-v3/testsuite/std/time/year_month_day/io.cc @@ -43,38 +43,87 @@ test_format() s = std::format("{:%G-W%V-%u}", 2022y/January/3); VERIFY( s == "2022-W01-1" ); + std::chrono::month Quindecember(17); // %U: Week number for weeks starting on Sunday s = std::format("Day {:%w (%a) of Week %U of %Y}", 2022y/January/1); VERIFY( s == "Day 6 (Sat) of Week 00 of 2022" ); s = std::format("Day {:%w (%a) of Week %U of %Y}", 2022y/January/2); VERIFY( s == "Day 0 (Sun) of Week 01 of 2022" ); + s = std::format("Day {:%w (%a) of Week %U of %Y}", 2024y/January/1); + VERIFY( s == "Day 1 (Mon) of Week 00 of 2024" ); + s = std::format("Day {:%w (%a) of Week %U of %Y}", 2024y/January/7); + VERIFY( s == "Day 0 (Sun) of Week 01 of 2024" ); + s = std::format("Day {:%w (%a) of Week %U of %Y}", 2024y/January/8); + VERIFY( s == "Day 1 (Mon) of Week 01 of 2024" ); + s = std::format("Day {:%w (%a) of Week %U of %Y}", 2022y/Quindecember/20); + VERIFY( s == "Day 1 (Mon) of Week 73 of 2022" ); // %W: Week number for weeks starting on Monday s = std::format("Day {:%u (%a) of Week %W of %Y}", 2022y/January/2); VERIFY( s == "Day 7 (Sun) of Week 00 of 2022" ); s = std::format("Day {:%u (%a) of Week %W of %Y}", 2022y/January/3); VERIFY( s == "Day 1 (Mon) of Week 01 of 2022" ); - + s = std::format("Day {:%w (%a) of Week %W of %Y}", 2019y/January/1); + VERIFY( s == "Day 2 (Tue) of Week 00 of 2019" ); + s = std::format("Day {:%w (%a) of Week %W of %Y}", 2019y/January/7); + VERIFY( s == "Day 1 (Mon) of Week 01 of 2019" ); + s = std::format("Day {:%w (%a) of Week %W of %Y}", 2019y/January/8); + VERIFY( s == "Day 2 (Tue) of Week 01 of 2019" ); + s = std::format("Day {:%w (%a) of Week %W of %Y}", 2022y/Quindecember/20); + VERIFY( s == "Day 1 (Mon) of Week 73 of 2022" ); + + // %G: ISO week-calendar year (ISO 8601) // %V: ISO week number (ISO 8601). - s = std::format("W{:%V}", 1977y/1/1); - VERIFY( s == "W53" ); - s = std::format("W{:%V}", 1977y/1/2); - VERIFY( s == "W53" ); - s = std::format("W{:%V}", 1977y/12/31); - VERIFY( s == "W52" ); - s = std::format("W{:%V}", 1978y/1/1); - VERIFY( s == "W52" ); - s = std::format("W{:%V}", 1978y/1/2); - VERIFY( s == "W01" ); - s = std::format("W{:%V}", 1978y/12/31); - VERIFY( s == "W52" ); - s = std::format("W{:%V}", 1979y/1/1); - VERIFY( s == "W01" ); - s = std::format("W{:%V}", 1979y/12/30); - VERIFY( s == "W52" ); - s = std::format("W{:%V}", 1979y/12/31); - VERIFY( s == "W01" ); - s = std::format("W{:%V}", 1980y/1/1); - VERIFY( s == "W01" ); + s = std::format("{:%G-W%V}", 1977y/1/1); + VERIFY( s == "1976-W53" ); + s = std::format("{:%G-W%V}", 1977y/1/2); + VERIFY( s == "1976-W53" ); + s = std::format("{:%G-W%V}", 1977y/1/3); + VERIFY( s == "1977-W01" ); + s = std::format("{:%G-W%V}", 1977y/12/31); + VERIFY( s == "1977-W52" ); + s = std::format("{:%G-W%V}", 1978y/1/1); + VERIFY( s == "1977-W52" ); + s = std::format("{:%G-W%V}", 1978y/1/2); + VERIFY( s == "1978-W01" ); + s = std::format("{:%G-W%V}", 1978y/12/31); + VERIFY( s == "1978-W52" ); + s = std::format("{:%G-W%V}", 1979y/1/1); + VERIFY( s == "1979-W01" ); + s = std::format("{:%G-W%V}", 1979y/12/30); + VERIFY( s == "1979-W52" ); + s = std::format("{:%G-W%V}", 1979y/12/31); + VERIFY( s == "1980-W01" ); + s = std::format("{:%G-W%V}", 1980y/1/1); + VERIFY( s == "1980-W01" ); + s = std::format("{:%G-W%V}", 1980y/18/20); + VERIFY( s == "1981-W26" ); + + // "Leap weak" on year starting on Thursday + s = std::format("{:%G-W%V}", 2009y/12/31); + VERIFY( s == "2009-W53" ); + s = std::format("{:%G-W%V}", 2010y/1/1); + VERIFY( s == "2009-W53" ); + s = std::format("{:%G-W%V}", 2010y/1/3); + VERIFY( s == "2009-W53" ); + s = std::format("{:%G-W%V}", 2010y/1/4); + VERIFY( s == "2010-W01" ); + + // "Leap weak" on leap year stating on Wednesday + // 2020/Dec/31 is Thurday, thus 366 day of year + s = std::format("{:%G-W%V}", 2020y/12/30); + VERIFY( s == "2020-W53" ); + s = std::format("{:%G-W%V}", 2020y/12/31); + VERIFY( s == "2020-W53" ); + s = std::format("{:%G-W%V}", 2021y/1/1); + VERIFY( s == "2020-W53" ); + s = std::format("{:%G-W%V}", 2021y/1/3); + VERIFY( s == "2020-W53" ); + s = std::format("{:%G-W%V}", 2021y/1/4); + VERIFY( s == "2021-W01" ); + s = std::format("{:%G-W%V}", 2021y/1/7); + VERIFY( s == "2021-W01" ); + s = std::format("{:%G-W%V}", 2021y/1/8); + VERIFY( s == "2021-W01" ); s = std::format("{:%x}", 2022y/December/19); VERIFY( s == "12/19/22" ); diff --git a/libstdc++-v3/testsuite/std/time/year_month_day_last/io.cc b/libstdc++-v3/testsuite/std/time/year_month_day_last/io.cc index 3241536..cb937c1 100644 --- a/libstdc++-v3/testsuite/std/time/year_month_day_last/io.cc +++ b/libstdc++-v3/testsuite/std/time/year_month_day_last/io.cc @@ -22,9 +22,87 @@ test_ostream() VERIFY( ss.str() == "2023/juil./last" ); } +void +test_format() +{ + using namespace std::chrono; + + std::string s = std::format("{:%Y%t%C%%%y%n%j %b %a}", 2024y/January/last); + VERIFY( s == "2024\t20%24\n031 Jan Wed" ); + std::wstring ws = std::format(L"{:%Y%t%C%%%y%n%j %b %a}", 2024y/December/last); + VERIFY( ws == L"2024\t20%24\n366 Dec Tue" ); + + s = std::format("{0:%Y-%m-%d} {0}", 2023y/May/last); + VERIFY( s == "2023-05-31 2023/May/last" ); + s = std::format("{0:%Y-%m-%d} {0}", 2023y/month(13)/last); + VERIFY( s == "2023-13-30 2023/13 is not a valid month/last" ); + + s = std::format("{:%Y-%m-%d %j}", 2024y/February/last); + VERIFY( s == "2024-02-29 060" ); + s = std::format("{:%Y-%m-%d %j}", 2023y/February/last); + VERIFY( s == "2023-02-28 059" ); + s = std::format("{:%Y-%m-%d %j}", 2024y/September/last); + VERIFY( s == "2024-09-30 274" ); + + // %U: Week number for weeks starting on Sunday + s = std::format("{:%Y-U%U}", 2023y/January/last); + VERIFY( s == "2023-U05" ); + s = std::format("{:%Y-U%U}", 2023y/December/last); + VERIFY( s == "2023-U53" ); + // %W: Week number for weeks starting on Monday + s = std::format("{:%Y-W%W}", 2023y/January/last); + VERIFY( s == "2023-W05" ); + s = std::format("{:%Y-W%W}", 2023y/December/last); + VERIFY( s == "2023-W52" ); + + // %G: ISO week-calendar year (ISO 8601) + // %V: ISO week number (ISO 8601). + s = std::format("{:%G-V%V}", 2019y/December/last); + VERIFY( s == "2020-V01" ); + s = std::format("{:%G-V%V}", 2023y/January/last); + VERIFY( s == "2023-V05" ); + s = std::format("{:%G-V%V}", 2023y/December/last); + VERIFY( s == "2023-V52" ); + + s = std::format("{:%F}", 2023y/July/last); + VERIFY( s == "2023-07-31" ); + s = std::format("{:%x}", 2023y/July/last); + VERIFY( s == "07/31/23" ); + s = std::format("{:L%x}", 2023y/July/last); + VERIFY( s == "07/31/23" ); + std::locale loc_fr(ISO_8859(15,fr_FR)); + s = std::format(loc_fr, "{:%x}", 2023y/July/last); + VERIFY( s == "07/31/23" ); + s = std::format(loc_fr, "{:L%x}", 2023y/July/last); + VERIFY( s == "31/07/2023" || s == "31.07.2023" ); // depends on locale defs + s = std::format(loc_fr, "{:L}", 2023y/July/last); + VERIFY( s == "2023/juil./last" ); + + std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ"; + std::string_view my_specs = "aAbBCdDeFgGhjmuUVwWxyY"; + for (char c : specs) + { + char fmt[] = { '{', ':', '%', c, '}' }; + try + { + year_month_weekday ymw = 2023y/July/Thursday[2]; + (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(ymw)); + // The call above should throw for any conversion-spec not in my_specs: + VERIFY(my_specs.find(c) != my_specs.npos); + } + catch (const std::format_error& e) + { + VERIFY(my_specs.find(c) == my_specs.npos); + std::string_view s = e.what(); + // Libstdc++-specific message: + VERIFY(s.find("format argument does not contain the information " + "required by the chrono-specs") != s.npos); + } + } +} + int main() { test_ostream(); - // TODO: test_format(); - // TODO: test_parse(); + test_format(); } diff --git a/libstdc++-v3/testsuite/std/time/year_month_weekday/io.cc b/libstdc++-v3/testsuite/std/time/year_month_weekday/io.cc index 65baf1d..e01484c 100644 --- a/libstdc++-v3/testsuite/std/time/year_month_weekday/io.cc +++ b/libstdc++-v3/testsuite/std/time/year_month_weekday/io.cc @@ -34,9 +34,153 @@ test_ostream() VERIFY( ss.str() == "2023/juil./jeu.[2]" ); } +void +test_format() +{ + using namespace std::chrono; + + std::string s = std::format("{:%Y%t%C%%%y%n%j %b %a}", 2024y/January/Friday[2]); + VERIFY( s == "2024\t20%24\n012 Jan Fri" ); + std::wstring ws = std::format(L"{:%Y%t%C%%%y%n%j %b %a}", 2024y/December/Monday[1]); + VERIFY( ws == L"2024\t20%24\n337 Dec Mon" ); + + s = std::format("{0:%Y-%m-%d} {0}", 2023y/May/Monday[1]); + VERIFY( s == "2023-05-01 2023/May/Mon[1]" ); + s = std::format("{0:%Y-%m-%d} {0}", 2023y/month(13)/Monday[1]); + VERIFY( s == "2023-13-01 2023/13 is not a valid month/Mon[1]" ); + + s = std::format("{:%u %w}", Monday[1]); + VERIFY( s == "1 1" ); + s = std::format("{:%u %w}", Sunday[2]); + VERIFY( s == "7 0" ); + // 0 and 7 are both Sundays + s = std::format("{:%u %w}", weekday(0)[1]); + VERIFY( s == "7 0" ); + s = std::format("{:%u %w}", weekday(7)[1]); + VERIFY( s == "7 0" ); + s = std::format("{:%u %w}", weekday(9)[1]); + VERIFY( s == "9 9" ); + + s = std::format("{:%Y-%m-%d %j}", 2024y/February/Thursday[5]); + VERIFY( s == "2024-02-29 060" ); + s = std::format("{:%Y-%m-%d %j}", 2023y/February/Tuesday[4]); + VERIFY( s == "2023-02-28 059" ); + s = std::format("{:%Y-%m-%d %j}", 2024y/September/Sunday[1]); + VERIFY( s == "2024-09-01 245" ); + s = std::format("{:%Y-%m-%d %j}", 2024y/September/Sunday[5]); + VERIFY( s == "2024-09-29 273" ); + // first weeks of next month + s = std::format("{:%Y-%m-%d %j}", 2024y/September/Sunday[6]); + VERIFY( s == "2024-09-36 280" ); + s = std::format("{:%Y-%m-%d %j}", 2024y/September/Sunday[7]); + VERIFY( s == "2024-09-43 287" ); + // last week on previous month + s = std::format("{:%Y-%m-%d %j}", 2024y/September/Saturday[0]); + VERIFY( s == "2024-09-00 244" ); + s = std::format("{:%Y-%m-%d %j}", 2024y/September/Sunday[0]); + VERIFY( s == "2024-09-00 238" ); // day is de-facto -6 + + // %U: Week number for weeks starting on Sunday + s = std::format("{:%Y-U%U}", 2023y/January/Sunday[0]); + VERIFY( s == "2023-U00" ); + s = std::format("{:%Y-U%U}", 2023y/January/Sunday[1]); + VERIFY( s == "2023-U01" ); + s = std::format("{:%Y-U%U}", 2023y/January/Sunday[4]); + VERIFY( s == "2023-U04" ); + s = std::format("{:%Y-U%U}", 2023y/January/Sunday[7]); + VERIFY( s == "2023-U07" ); + s = std::format("{:%Y-U%U}", 2023y/December/Sunday[0]); + VERIFY( s == "2023-U48" ); + s = std::format("{:%Y-U%U}", 2023y/December/Sunday[1]); + VERIFY( s == "2023-U49" ); + s = std::format("{:%Y-U%U}", 2023y/December/Sunday[4]); + VERIFY( s == "2023-U52" ); + s = std::format("{:%Y-U%U}", 2023y/December/Sunday[7]); + VERIFY( s == "2023-U55" ); + // %W: Week number for weeks starting on Monday + s = std::format("{:%Y-W%W}", 2023y/January/Monday[0]); + VERIFY( s == "2023-W00" ); + s = std::format("{:%Y-W%W}", 2023y/January/Monday[1]); + VERIFY( s == "2023-W01" ); + s = std::format("{:%Y-W%W}", 2023y/January/Monday[4]); + VERIFY( s == "2023-W04" ); + s = std::format("{:%Y-W%W}", 2023y/January/Monday[7]); + VERIFY( s == "2023-W07" ); + s = std::format("{:%Y-W%W}", 2023y/December/Monday[0]); + VERIFY( s == "2023-W48" ); + s = std::format("{:%Y-W%W}", 2023y/December/Monday[1]); + VERIFY( s == "2023-W49" ); + s = std::format("{:%Y-W%W}", 2023y/December/Monday[4]); + VERIFY( s == "2023-W52" ); + s = std::format("{:%Y-W%W}", 2023y/December/Monday[7]); + VERIFY( s == "2023-W55" ); + // First Sunday precedes first Monday, so happens on + // last week of previous month and thus year + s = std::format("{:%Y-W%W}", 2023y/January/Sunday[1]); + VERIFY( s == "2023-W00" ); + // Last Sunday of pevious year happens on second to + // last week on previous year + s = std::format("{:%Y-W%W}", 2023y/January/Sunday[0]); + VERIFY( s == "2023-W99" ); + + // %G: ISO week-calendar year (ISO 8601) + // %V: ISO week number (ISO 8601). + s = std::format("{:%G-V%V}", 2023y/January/Monday[0]); + VERIFY( s == "2022-V52" ); + s = std::format("{:%G-V%V}", 2023y/January/Monday[1]); + VERIFY( s == "2023-V01" ); + s = std::format("{:%G-V%V}", 2023y/January/Monday[4]); + VERIFY( s == "2023-V04" ); + s = std::format("{:%G-V%V}", 2023y/January/Monday[7]); + VERIFY( s == "2023-V07" ); + s = std::format("{:%G-V%V}", 2023y/December/Friday[0]); + VERIFY( s == "2023-V47" ); + s = std::format("{:%G-V%V}", 2023y/December/Friday[1]); + VERIFY( s == "2023-V48" ); + s = std::format("{:%G-V%V}", 2023y/December/Friday[5]); + VERIFY( s == "2023-V52" ); + s = std::format("{:%G-V%V}", 2023y/December/Friday[6]); + VERIFY( s == "2024-V01" ); + + s = std::format("{:%F}", 2023y/July/Thursday[2]); + VERIFY( s == "2023-07-13" ); + s = std::format("{:%x}", 2023y/July/Thursday[2]); + VERIFY( s == "07/13/23" ); + s = std::format("{:L%x}", 2023y/July/Thursday[2]); + VERIFY( s == "07/13/23" ); + std::locale loc_fr(ISO_8859(15,fr_FR)); + s = std::format(loc_fr, "{:%x}", 2023y/July/Thursday[2]); + VERIFY( s == "07/13/23" ); + s = std::format(loc_fr, "{:L%x}", 2023y/July/Thursday[2]); + VERIFY( s == "13/07/2023" || s == "13.07.2023" ); // depends on locale defs + s = std::format(loc_fr, "{:L}", 2023y/July/Thursday[2]); + VERIFY( s == "2023/juil./jeu.[2]" ); + + std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ"; + std::string_view my_specs = "aAbBCdDeFgGhjmuUVwWxyY"; + for (char c : specs) + { + char fmt[] = { '{', ':', '%', c, '}' }; + try + { + year_month_weekday ymw = 2023y/July/Thursday[2]; + (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(ymw)); + // The call above should throw for any conversion-spec not in my_specs: + VERIFY(my_specs.find(c) != my_specs.npos); + } + catch (const std::format_error& e) + { + VERIFY(my_specs.find(c) == my_specs.npos); + std::string_view s = e.what(); + // Libstdc++-specific message: + VERIFY(s.find("format argument does not contain the information " + "required by the chrono-specs") != s.npos); + } + } +} + int main() { test_ostream(); - // TODO: test_format(); - // TODO: test_parse(); + test_format(); } diff --git a/libstdc++-v3/testsuite/std/time/year_month_weekday_last/io.cc b/libstdc++-v3/testsuite/std/time/year_month_weekday_last/io.cc index 17f2244..27bbe94 100644 --- a/libstdc++-v3/testsuite/std/time/year_month_weekday_last/io.cc +++ b/libstdc++-v3/testsuite/std/time/year_month_weekday_last/io.cc @@ -30,9 +30,103 @@ test_ostream() VERIFY( ss.str() == "2023/juil./jeu.[last]" ); } +void +test_format() +{ + using namespace std::chrono; + + std::string s = std::format("{:%Y%t%C%%%y%n%j %b %a}", 2024y/January/Friday[last]); + VERIFY( s == "2024\t20%24\n026 Jan Fri" ); + std::wstring ws = std::format(L"{:%Y%t%C%%%y%n%j %b %a}", 2024y/December/Monday[last]); + VERIFY( ws == L"2024\t20%24\n365 Dec Mon" ); + + s = std::format("{0:%Y-%m-%d} {0}", 2023y/May/Monday[last]); + VERIFY( s == "2023-05-29 2023/May/Mon[last]" ); + s = std::format("{0:%Y-%m-%d} {0}", 2023y/month(13)/Monday[last]); + VERIFY( s == "2023-13-29 2023/13 is not a valid month/Mon[last]" ); + + s = std::format("{:%u %w}", Monday[last]); + VERIFY( s == "1 1" ); + s = std::format("{:%u %w}", Sunday[2]); + VERIFY( s == "7 0" ); + // 0 and 7 are both Sundays + s = std::format("{:%u %w}", weekday(0)[last]); + VERIFY( s == "7 0" ); + s = std::format("{:%u %w}", weekday(7)[last]); + VERIFY( s == "7 0" ); + s = std::format("{:%u %w}", weekday(9)[last]); + VERIFY( s == "9 9" ); + + s = std::format("{:%Y-%m-%d %j}", 2024y/February/Thursday[last]); + VERIFY( s == "2024-02-29 060" ); + s = std::format("{:%Y-%m-%d %j}", 2023y/February/Tuesday[last]); + VERIFY( s == "2023-02-28 059" ); + s = std::format("{:%Y-%m-%d %j}", 2024y/September/Sunday[last]); + VERIFY( s == "2024-09-29 273" ); + + // %U: Week number for weeks starting on Sunday + s = std::format("{:%Y-U%U}", 2023y/January/Sunday[last]); + VERIFY( s == "2023-U05" ); + s = std::format("{:%Y-U%U}", 2023y/December/Sunday[last]); + VERIFY( s == "2023-U53" ); + // %W: Week number for weeks starting on Monday + s = std::format("{:%Y-W%W}", 2023y/January/Monday[last]); + VERIFY( s == "2023-W05" ); + s = std::format("{:%Y-W%W}", 2023y/December/Monday[last]); + VERIFY( s == "2023-W52" ); + s = std::format("{:%Y-W%W}", 2023y/January/Sunday[last]); + VERIFY( s == "2023-W04" ); + s = std::format("{:%Y-W%W}", 2023y/December/Sunday[last]); + VERIFY( s == "2023-W52" ); + + // %G: ISO week-calendar year (ISO 8601) + // %V: ISO week number (ISO 8601). + s = std::format("{:%G-V%V}", 2019y/December/Tuesday[last]); + VERIFY( s == "2020-V01" ); + s = std::format("{:%G-V%V}", 2023y/January/Monday[last]); + VERIFY( s == "2023-V05" ); + s = std::format("{:%G-V%V}", 2023y/December/Friday[last]); + VERIFY( s == "2023-V52" ); + + s = std::format("{:%F}", 2023y/July/Thursday[last]); + VERIFY( s == "2023-07-27" ); + s = std::format("{:%x}", 2023y/July/Thursday[last]); + VERIFY( s == "07/27/23" ); + s = std::format("{:L%x}", 2023y/July/Thursday[last]); + VERIFY( s == "07/27/23" ); + std::locale loc_fr(ISO_8859(15,fr_FR)); + s = std::format(loc_fr, "{:%x}", 2023y/July/Thursday[last]); + VERIFY( s == "07/27/23" ); + s = std::format(loc_fr, "{:L%x}", 2023y/July/Thursday[last]); + VERIFY( s == "27/07/2023" || s == "27.07.2023" ); // depends on locale defs + s = std::format(loc_fr, "{:L}", 2023y/July/Thursday[last]); + VERIFY( s == "2023/juil./jeu.[last]" ); + + std::string_view specs = "aAbBcCdDeFgGhHIjmMpqQrRSTuUVwWxXyYzZ"; + std::string_view my_specs = "aAbBCdDeFgGhjmuUVwWxyY"; + for (char c : specs) + { + char fmt[] = { '{', ':', '%', c, '}' }; + try + { + year_month_weekday ymw = 2023y/July/Thursday[2]; + (void) std::vformat(std::string_view(fmt, 5), std::make_format_args(ymw)); + // The call above should throw for any conversion-spec not in my_specs: + VERIFY(my_specs.find(c) != my_specs.npos); + } + catch (const std::format_error& e) + { + VERIFY(my_specs.find(c) == my_specs.npos); + std::string_view s = e.what(); + // Libstdc++-specific message: + VERIFY(s.find("format argument does not contain the information " + "required by the chrono-specs") != s.npos); + } + } +} + int main() { test_ostream(); - // TODO: test_format(); - // TODO: test_parse(); + test_format(); } diff --git a/libstdc++-v3/testsuite/tr1/2_general_utilities/shared_ptr/cons/43820_neg.cc b/libstdc++-v3/testsuite/tr1/2_general_utilities/shared_ptr/cons/43820_neg.cc index 7b5ede4..03fad14 100644 --- a/libstdc++-v3/testsuite/tr1/2_general_utilities/shared_ptr/cons/43820_neg.cc +++ b/libstdc++-v3/testsuite/tr1/2_general_utilities/shared_ptr/cons/43820_neg.cc @@ -39,9 +39,6 @@ void test01() // { dg-error "incomplete" "" { target *-*-* } 600 } } -// { dg-error "-Wdelete-incomplete" "" { target c++26 } 283 } -// { dg-error "-Wdelete-incomplete" "" { target c++26 } 305 } - // Ignore additional diagnostic given with -Wsystem-headers: // { dg-prune-output "has incomplete type" } // { dg-prune-output "possible problem detected" } diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/01_assoc_laguerre/check_value.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/01_assoc_laguerre/check_value.cc index 1ae8371..77e7648 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/01_assoc_laguerre/check_value.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/01_assoc_laguerre/check_value.cc @@ -2217,7 +2217,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_assoc_laguerre<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/02_assoc_legendre/check_value.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/02_assoc_legendre/check_value.cc index 023f09d..a30700b 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/02_assoc_legendre/check_value.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/02_assoc_legendre/check_value.cc @@ -1985,7 +1985,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_assoc_legendre<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/02_assoc_legendre/pr86655.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/02_assoc_legendre/pr86655.cc index 7f7e3a4..ec99b60 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/02_assoc_legendre/pr86655.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/02_assoc_legendre/pr86655.cc @@ -36,7 +36,6 @@ template<typename _Tp> void test_m_gt_l() { - bool test __attribute__((unused)) = true; unsigned int larr[4] = {0u, 1u, 2u, 5u}; for (unsigned int l = 0; l < 4; ++l) for (unsigned int m = larr[l] + 1u; m <= larr[l] + 2u; ++m) diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/03_beta/check_value.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/03_beta/check_value.cc index f61438d..2dcdcbc 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/03_beta/check_value.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/03_beta/check_value.cc @@ -261,7 +261,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_beta<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/04_comp_ellint_1/check_value.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/04_comp_ellint_1/check_value.cc index 76240db..7faffe8 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/04_comp_ellint_1/check_value.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/04_comp_ellint_1/check_value.cc @@ -73,7 +73,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_comp_ellint_1<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/05_comp_ellint_2/check_value.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/05_comp_ellint_2/check_value.cc index b46a140..60adcb1 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/05_comp_ellint_2/check_value.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/05_comp_ellint_2/check_value.cc @@ -73,7 +73,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_comp_ellint_2<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/06_comp_ellint_3/check_value.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/06_comp_ellint_3/check_value.cc index aba5284..34f5956 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/06_comp_ellint_3/check_value.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/06_comp_ellint_3/check_value.cc @@ -459,7 +459,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_comp_ellint_3<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/07_conf_hyperg/check_value.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/07_conf_hyperg/check_value.cc index 5b10ea4..1cd2733 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/07_conf_hyperg/check_value.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/07_conf_hyperg/check_value.cc @@ -3819,7 +3819,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_conf_hyperg<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/08_cyl_bessel_i/check_value.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/08_cyl_bessel_i/check_value.cc index cb4b3960..02df6ba 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/08_cyl_bessel_i/check_value.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/08_cyl_bessel_i/check_value.cc @@ -702,7 +702,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_cyl_bessel_i<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/09_cyl_bessel_j/check_value.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/09_cyl_bessel_j/check_value.cc index 689880d..f3b7fa6 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/09_cyl_bessel_j/check_value.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/09_cyl_bessel_j/check_value.cc @@ -735,7 +735,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_cyl_bessel_j<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/10_cyl_bessel_k/check_value.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/10_cyl_bessel_k/check_value.cc index a408c3f..10b8092 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/10_cyl_bessel_k/check_value.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/10_cyl_bessel_k/check_value.cc @@ -746,7 +746,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_cyl_bessel_k<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/11_cyl_neumann/check_value.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/11_cyl_neumann/check_value.cc index 868d278..c365bb5 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/11_cyl_neumann/check_value.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/11_cyl_neumann/check_value.cc @@ -779,7 +779,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_cyl_neumann<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/12_ellint_1/check_value.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/12_ellint_1/check_value.cc index 2ca367f..acb217d 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/12_ellint_1/check_value.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/12_ellint_1/check_value.cc @@ -459,7 +459,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_ellint_1<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/13_ellint_2/check_value.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/13_ellint_2/check_value.cc index d9e0f7f..7bb33b5 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/13_ellint_2/check_value.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/13_ellint_2/check_value.cc @@ -459,7 +459,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_ellint_2<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/14_ellint_3/check_value.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/14_ellint_3/check_value.cc index 2da68ab..df624a0 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/14_ellint_3/check_value.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/14_ellint_3/check_value.cc @@ -6121,7 +6121,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_ellint_3<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/15_expint/check_value_neg.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/15_expint/check_value_neg.cc index 33d95c9..64f7685 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/15_expint/check_value_neg.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/15_expint/check_value_neg.cc @@ -168,7 +168,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_expint<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/16_hermite/check_value.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/16_hermite/check_value.cc index 8afbcdd..0676f62 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/16_hermite/check_value.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/16_hermite/check_value.cc @@ -1904,7 +1904,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_hermite<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/17_hyperg/check_value.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/17_hyperg/check_value.cc index c77dc3d..b83994e 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/17_hyperg/check_value.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/17_hyperg/check_value.cc @@ -12292,7 +12292,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_hyperg<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/18_laguerre/check_value.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/18_laguerre/check_value.cc index 60d6af0..2fee8e4 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/18_laguerre/check_value.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/18_laguerre/check_value.cc @@ -305,7 +305,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_laguerre<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/19_legendre/check_value.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/19_legendre/check_value.cc index 19f096d..325ae0d 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/19_legendre/check_value.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/19_legendre/check_value.cc @@ -305,7 +305,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_legendre<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/20_riemann_zeta/check_value_neg.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/20_riemann_zeta/check_value_neg.cc index 658faa7..cbbccdf 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/20_riemann_zeta/check_value_neg.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/20_riemann_zeta/check_value_neg.cc @@ -273,7 +273,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_riemann_zeta<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/21_sph_bessel/check_value.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/21_sph_bessel/check_value.cc index 5863f9a..f2ef94b 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/21_sph_bessel/check_value.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/21_sph_bessel/check_value.cc @@ -504,7 +504,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_sph_bessel<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/22_sph_legendre/check_value.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/22_sph_legendre/check_value.cc index 910485f..1e178a6 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/22_sph_legendre/check_value.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/22_sph_legendre/check_value.cc @@ -1985,7 +1985,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_sph_legendre<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/22_sph_legendre/pr86655.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/22_sph_legendre/pr86655.cc index 11c9935..87f63ce 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/22_sph_legendre/pr86655.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/22_sph_legendre/pr86655.cc @@ -36,7 +36,6 @@ template<typename _Tp> void test_m_gt_l() { - bool test __attribute__((unused)) = true; unsigned int larr[4] = {0u, 1u, 2u, 5u}; for (unsigned int l = 0; l < 4; ++l) for (unsigned int m = larr[l] + 1u; m <= larr[l] + 2u; ++m) diff --git a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/23_sph_neumann/check_value.cc b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/23_sph_neumann/check_value.cc index 8134e7f..64cfce9 100644 --- a/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/23_sph_neumann/check_value.cc +++ b/libstdc++-v3/testsuite/tr1/5_numerical_facilities/special_functions/23_sph_neumann/check_value.cc @@ -554,7 +554,6 @@ template<typename Ret, unsigned int Num> void test(const testcase_sph_neumann<Ret> (&data)[Num], Ret toler) { - bool test __attribute__((unused)) = true; const Ret eps = std::numeric_limits<Ret>::epsilon(); Ret max_abs_diff = -Ret(1); Ret max_abs_frac = -Ret(1); diff --git a/libstdc++-v3/testsuite/util/debug/checks.h b/libstdc++-v3/testsuite/util/debug/checks.h index 938cdda..528c021 100644 --- a/libstdc++-v3/testsuite/util/debug/checks.h +++ b/libstdc++-v3/testsuite/util/debug/checks.h @@ -19,10 +19,12 @@ #include <vector> #include <deque> #include <list> +#include <inplace_vector> #ifndef _GLIBCXX_DEBUG # include <debug/vector> # include <debug/deque> # include <debug/list> +# include <debug/inplace_vector> #endif #include <testsuite_hooks.h> @@ -88,10 +90,11 @@ namespace __gnu_test void check_assign1() { + using namespace std; typedef _Tp cont_type; typedef typename cont_type::value_type cont_val_type; typedef typename CopyableValueType<cont_val_type>::value_type val_type; - typedef std::vector<val_type> vector_type; + typedef _GLIBCXX_STD_C::vector<val_type> vector_type; generate_unique<val_type> gu; @@ -116,10 +119,11 @@ namespace __gnu_test void check_assign2() { + using namespace std; typedef _Tp cont_type; typedef typename cont_type::value_type cont_val_type; typedef typename CopyableValueType<cont_val_type>::value_type val_type; - typedef std::vector<val_type> vector_type; + typedef _GLIBCXX_STD_C::vector<val_type> vector_type; generate_unique<val_type> gu; @@ -170,10 +174,11 @@ namespace __gnu_test void check_construct1() { + using namespace std; typedef _Tp cont_type; typedef typename cont_type::value_type cont_val_type; typedef typename CopyableValueType<cont_val_type>::value_type val_type; - typedef std::vector<val_type> vector_type; + typedef _GLIBCXX_STD_C::vector<val_type> vector_type; generate_unique<val_type> gu; @@ -193,10 +198,11 @@ namespace __gnu_test void check_construct2() { + using namespace std; typedef _Tp cont_type; typedef typename cont_type::value_type cont_val_type; typedef typename CopyableValueType<cont_val_type>::value_type val_type; - typedef std::vector<val_type> vector_type; + typedef _GLIBCXX_STD_C::vector<val_type> vector_type; generate_unique<val_type> gu; @@ -267,6 +273,13 @@ namespace __gnu_test : InsertRangeHelperAux<std::list<_Tp1, _Tp2> > { }; +#ifdef __glibcxx_inplace_vector // C++ >= 26 + template<typename _Tp, size_t _Nm> + struct InsertRangeHelper<std::inplace_vector<_Tp, _Nm> > + : InsertRangeHelperAux<std::inplace_vector<_Tp, _Nm> > + { }; +#endif + #ifndef _GLIBCXX_DEBUG template <typename _Tp1, typename _Tp2> struct InsertRangeHelper<__gnu_debug::vector<_Tp1, _Tp2> > @@ -282,16 +295,24 @@ namespace __gnu_test struct InsertRangeHelper<__gnu_debug::list<_Tp1, _Tp2> > : InsertRangeHelperAux<__gnu_debug::list<_Tp1, _Tp2> > { }; + +# ifdef __glibcxx_inplace_vector // C++ >= 26 + template<typename _Tp, size_t _Nm> + struct InsertRangeHelper<__gnu_debug::inplace_vector<_Tp, _Nm> > + : InsertRangeHelperAux<__gnu_debug::inplace_vector<_Tp, _Nm> > + { }; +# endif #endif template<typename _Tp> void check_insert1() { + using namespace std; typedef _Tp cont_type; typedef typename cont_type::value_type cont_val_type; typedef typename CopyableValueType<cont_val_type>::value_type val_type; - typedef std::vector<val_type> vector_type; + typedef _GLIBCXX_STD_C::vector<val_type> vector_type; generate_unique<val_type> gu; @@ -315,10 +336,11 @@ namespace __gnu_test void check_insert2() { + using namespace std; typedef _Tp cont_type; typedef typename cont_type::value_type cont_val_type; typedef typename CopyableValueType<cont_val_type>::value_type val_type; - typedef std::vector<val_type> vector_type; + typedef _GLIBCXX_STD_C::vector<val_type> vector_type; generate_unique<val_type> gu; @@ -369,10 +391,11 @@ namespace __gnu_test void check_insert4() { + using namespace std; typedef _Tp cont_type; typedef typename cont_type::value_type cont_val_type; typedef typename CopyableValueType<cont_val_type>::value_type val_type; - typedef std::list<val_type> list_type; + typedef _GLIBCXX_STD_C::list<val_type> list_type; generate_unique<val_type> gu; diff --git a/libstdc++-v3/testsuite/util/exception/safety.h b/libstdc++-v3/testsuite/util/exception/safety.h index 8226c17..c62394c 100644 --- a/libstdc++-v3/testsuite/util/exception/safety.h +++ b/libstdc++-v3/testsuite/util/exception/safety.h @@ -24,6 +24,7 @@ #include <ext/throw_allocator.h> #include <cstdlib> // getenv, atoi #include <cstdio> // printf, fflush +#include <cassert> // assert // Container requirement testing. namespace __gnu_test diff --git a/libstdc++-v3/testsuite/util/pstl/test_utils.h b/libstdc++-v3/testsuite/util/pstl/test_utils.h index 55b5100..9c61a714 100644 --- a/libstdc++-v3/testsuite/util/pstl/test_utils.h +++ b/libstdc++-v3/testsuite/util/pstl/test_utils.h @@ -154,7 +154,7 @@ class ForwardIterator explicit ForwardIterator(Iterator i) : my_iterator(i) {} reference operator*() const { return *my_iterator; } Iterator operator->() const { return my_iterator; } - ForwardIterator + ForwardIterator& operator++() { ++my_iterator; @@ -194,13 +194,13 @@ class BidirectionalIterator : public ForwardIterator<Iterator, IteratorTag> explicit BidirectionalIterator(Iterator i) : base_type(i) {} BidirectionalIterator(const base_type& i) : base_type(i.iterator()) {} - BidirectionalIterator + BidirectionalIterator& operator++() { ++base_type::my_iterator; return *this; } - BidirectionalIterator + BidirectionalIterator& operator--() { --base_type::my_iterator; diff --git a/libstdc++-v3/testsuite/util/replacement_memory_operators.h b/libstdc++-v3/testsuite/util/replacement_memory_operators.h index 2516cd2..69afa77 100644 --- a/libstdc++-v3/testsuite/util/replacement_memory_operators.h +++ b/libstdc++-v3/testsuite/util/replacement_memory_operators.h @@ -36,8 +36,12 @@ namespace __gnu_test ~counter() THROW (counter_error) { +#if __cpp_exceptions if (_M_throw && _M_count != 0) throw counter_error(); +#else + VERIFY( !_M_throw || _M_count == 0 ); +#endif } static void @@ -133,8 +137,12 @@ void* operator new(std::size_t size) THROW(std::bad_alloc) { std::printf("operator new is called \n"); void* p = std::malloc(size); +#if __cpp_exceptions if (!p) throw std::bad_alloc(); +#else + VERIFY( p ); +#endif __gnu_test::counter::increment(); return p; } diff --git a/libstdc++-v3/testsuite/util/testsuite_abi.cc b/libstdc++-v3/testsuite/util/testsuite_abi.cc index 1b4044c..4b01023 100644 --- a/libstdc++-v3/testsuite/util/testsuite_abi.cc +++ b/libstdc++-v3/testsuite/util/testsuite_abi.cc @@ -216,6 +216,7 @@ check_version(symbol& test, bool added) known_versions.push_back("GLIBCXX_3.4.32"); known_versions.push_back("GLIBCXX_3.4.33"); known_versions.push_back("GLIBCXX_3.4.34"); + known_versions.push_back("GLIBCXX_3.4.35"); known_versions.push_back("GLIBCXX_LDBL_3.4.31"); known_versions.push_back("GLIBCXX_IEEE128_3.4.29"); known_versions.push_back("GLIBCXX_IEEE128_3.4.30"); @@ -240,6 +241,7 @@ check_version(symbol& test, bool added) #ifdef __riscv known_versions.push_back("CXXABI_1.3.16"); #endif + known_versions.push_back("CXXABI_1.3.17"); known_versions.push_back("CXXABI_IEEE128_1.3.13"); known_versions.push_back("CXXABI_TM_1"); known_versions.push_back("CXXABI_FLOAT128"); @@ -258,8 +260,8 @@ check_version(symbol& test, bool added) test.version_status = symbol::incompatible; // Check that added symbols are added in the latest pre-release version. - bool latestp = (test.version_name == "GLIBCXX_3.4.34" - || test.version_name == "CXXABI_1.3.16" + bool latestp = (test.version_name == "GLIBCXX_3.4.35" + || test.version_name == "CXXABI_1.3.17" || test.version_name == "CXXABI_FLOAT128" || test.version_name == "CXXABI_TM_1"); if (added && !latestp) diff --git a/libstdc++-v3/testsuite/util/testsuite_allocator.h b/libstdc++-v3/testsuite/util/testsuite_allocator.h index be596bf..3367b1b 100644 --- a/libstdc++-v3/testsuite/util/testsuite_allocator.h +++ b/libstdc++-v3/testsuite/util/testsuite_allocator.h @@ -26,14 +26,14 @@ #ifndef _GLIBCXX_TESTSUITE_ALLOCATOR_H #define _GLIBCXX_TESTSUITE_ALLOCATOR_H -#include <bits/move.h> -#include <ext/pointer.h> -#include <ext/alloc_traits.h> -#include <testsuite_hooks.h> #if __cplusplus >= 201703L # include <memory_resource> # include <new> #endif +#include <bits/move.h> +#include <ext/pointer.h> +#include <ext/alloc_traits.h> +#include <testsuite_hooks.h> #if __cplusplus >= 201103L # include <unordered_map> @@ -216,7 +216,7 @@ namespace __gnu_test } // Implement swap for underlying allocators that might need it. - friend inline void + friend void swap(tracker_allocator& a, tracker_allocator& b) { using std::swap; @@ -310,10 +310,6 @@ namespace __gnu_test { typedef __gnu_cxx::__alloc_traits<Alloc> AllocTraits; - Alloc& base() { return *this; } - const Alloc& base() const { return *this; } - void swap_base(Alloc& b) { using std::swap; swap(b, this->base()); } - public: typedef typename check_consistent_alloc_value_type<Tp, Alloc>::value_type value_type; @@ -332,9 +328,11 @@ namespace __gnu_test typename AllocTraits::template rebind<Tp1>::other> other; }; + _GLIBCXX_CONSTEXPR uneq_allocator() _GLIBCXX_USE_NOEXCEPT : personality(0) { } + _GLIBCXX_CONSTEXPR uneq_allocator(int person) _GLIBCXX_USE_NOEXCEPT : personality(person) { } @@ -344,21 +342,23 @@ namespace __gnu_test #endif template<typename Tp1> + _GLIBCXX_CONSTEXPR uneq_allocator(const uneq_allocator<Tp1, typename AllocTraits::template rebind<Tp1>::other>& b) _GLIBCXX_USE_NOEXCEPT : personality(b.get_personality()) { } - ~uneq_allocator() _GLIBCXX_USE_NOEXCEPT - { } - - int get_personality() const { return personality; } + _GLIBCXX_CONSTEXPR int get_personality() const { return personality; } + _GLIBCXX20_CONSTEXPR pointer allocate(size_type n, const void* = 0) { pointer p = AllocTraits::allocate(*this, n); + if (std::__is_constant_evaluated()) + return p; + try { get_map().insert(map_type::value_type(reinterpret_cast<void*>(p), @@ -373,19 +373,24 @@ namespace __gnu_test return p; } + _GLIBCXX14_CONSTEXPR void deallocate(pointer p, size_type n) { VERIFY( p ); - map_type::iterator it = get_map().find(reinterpret_cast<void*>(p)); - VERIFY( it != get_map().end() ); + if (!std::__is_constant_evaluated()) + { + map_type::iterator it = get_map().find(reinterpret_cast<void*>(p)); + VERIFY( it != get_map().end() ); + + // Enforce requirements in Table 32 about deallocation vs + // allocator equality. + VERIFY( it->second == personality ); - // Enforce requirements in Table 32 about deallocation vs - // allocator equality. - VERIFY( it->second == personality ); + get_map().erase(it); + } - get_map().erase(it); AllocTraits::deallocate(*this, p, n); } @@ -406,22 +411,23 @@ namespace __gnu_test private: // ... yet swappable! - friend inline void + friend _GLIBCXX_CONSTEXPR void swap(uneq_allocator& a, uneq_allocator& b) { std::swap(a.personality, b.personality); - a.swap_base(b); + using std::swap; + swap(static_cast<Alloc&>(a), static_cast<Alloc&>(b)); } template<typename Tp1> - friend inline bool + friend _GLIBCXX_CONSTEXPR bool operator==(const uneq_allocator& a, const uneq_allocator<Tp1, typename AllocTraits::template rebind<Tp1>::other>& b) { return a.personality == b.get_personality(); } template<typename Tp1> - friend inline bool + friend _GLIBCXX_CONSTEXPR bool operator!=(const uneq_allocator& a, const uneq_allocator<Tp1, typename AllocTraits::template rebind<Tp1>::other>& b) @@ -438,9 +444,12 @@ namespace __gnu_test typedef __gnu_cxx::__alloc_traits<Alloc> AllocTraits; typedef uneq_allocator<Tp, Alloc> base_alloc; - base_alloc& base() { return *this; } - const base_alloc& base() const { return *this; } - void swap_base(base_alloc& b) { swap(b, this->base()); } + _GLIBCXX14_CONSTEXPR void + swap_base(base_alloc& b) + { + using std::swap; + swap(b, static_cast<base_alloc&>(*this)); + } typedef std::integral_constant<bool, Propagate> trait_type; @@ -454,11 +463,13 @@ namespace __gnu_test typename AllocTraits::template rebind<Up>::other> other; }; + constexpr propagating_allocator(int i) noexcept : base_alloc(i) { } template<typename Up> + constexpr propagating_allocator(const propagating_allocator<Up, Propagate, typename AllocTraits::template rebind<Up>::other>& a) noexcept @@ -469,6 +480,7 @@ namespace __gnu_test propagating_allocator(const propagating_allocator&) noexcept = default; + _GLIBCXX14_CONSTEXPR propagating_allocator& operator=(const propagating_allocator& a) noexcept { @@ -478,6 +490,7 @@ namespace __gnu_test } template<bool P2> + _GLIBCXX14_CONSTEXPR propagating_allocator& operator=(const propagating_allocator<Tp, P2, Alloc>& a) noexcept { @@ -487,11 +500,13 @@ namespace __gnu_test } // postcondition: LWG2593 a.get_personality() un-changed. + constexpr propagating_allocator(propagating_allocator&& a) noexcept - : base_alloc(std::move(a.base())) + : base_alloc(static_cast<base_alloc&&>(a)) { } // postcondition: LWG2593 a.get_personality() un-changed + _GLIBCXX14_CONSTEXPR propagating_allocator& operator=(propagating_allocator&& a) noexcept { @@ -503,7 +518,8 @@ namespace __gnu_test typedef trait_type propagate_on_container_move_assignment; typedef trait_type propagate_on_container_swap; - propagating_allocator select_on_container_copy_construction() const + constexpr propagating_allocator + select_on_container_copy_construction() const { return Propagate ? *this : propagating_allocator(); } }; @@ -517,19 +533,24 @@ namespace __gnu_test constexpr SimpleAllocator() noexcept { } template <class T> + constexpr SimpleAllocator(const SimpleAllocator<T>&) { } + _GLIBCXX20_CONSTEXPR Tp *allocate(std::size_t n) { return std::allocator<Tp>().allocate(n); } + _GLIBCXX20_CONSTEXPR void deallocate(Tp *p, std::size_t n) { std::allocator<Tp>().deallocate(p, n); } }; template <class T, class U> + constexpr bool operator==(const SimpleAllocator<T>&, const SimpleAllocator<U>&) { return true; } template <class T, class U> + constexpr bool operator!=(const SimpleAllocator<T>&, const SimpleAllocator<U>&) { return false; } @@ -541,15 +562,16 @@ namespace __gnu_test default_init_allocator() = default; template<typename U> + constexpr default_init_allocator(const default_init_allocator<U>& a) : state(a.state) { } - T* + _GLIBCXX14_CONSTEXPR T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); } - void + _GLIBCXX14_CONSTEXPR void deallocate(T* p, std::size_t n) { std::allocator<T>().deallocate(p, n); } @@ -557,15 +579,17 @@ namespace __gnu_test }; template<typename T, typename U> - bool operator==(const default_init_allocator<T>& t, - const default_init_allocator<U>& u) + constexpr bool + operator==(const default_init_allocator<T>& t, + const default_init_allocator<U>& u) { return t.state == u.state; } template<typename T, typename U> - bool operator!=(const default_init_allocator<T>& t, - const default_init_allocator<U>& u) + constexpr bool + operator!=(const default_init_allocator<T>& t, + const default_init_allocator<U>& u) { return !(t == u); } -#endif +#endif // C++11 template<typename Tp> struct ExplicitConsAlloc : std::allocator<Tp> @@ -573,7 +597,7 @@ namespace __gnu_test ExplicitConsAlloc() { } template<typename Up> - explicit + explicit _GLIBCXX_CONSTEXPR ExplicitConsAlloc(const ExplicitConsAlloc<Up>&) { } template<typename Up> @@ -592,6 +616,7 @@ namespace __gnu_test CustomPointerAlloc() = default; template<typename Up> + constexpr CustomPointerAlloc(const CustomPointerAlloc<Up>&) { } template<typename Up> @@ -603,9 +628,11 @@ namespace __gnu_test typedef Ptr<void> void_pointer; typedef Ptr<const void> const_void_pointer; + _GLIBCXX14_CONSTEXPR pointer allocate(std::size_t n, const_void_pointer = {}) { return pointer(std::allocator<Tp>::allocate(n)); } + _GLIBCXX14_CONSTEXPR void deallocate(pointer p, std::size_t n) { std::allocator<Tp>::deallocate(std::addressof(*p), n); } }; @@ -623,16 +650,16 @@ namespace __gnu_test explicit operator bool() const noexcept { return value != nullptr; } - friend inline bool + friend constexpr bool operator==(NullablePointer lhs, NullablePointer rhs) noexcept { return lhs.value == rhs.value; } - friend inline bool + friend constexpr bool operator!=(NullablePointer lhs, NullablePointer rhs) noexcept { return lhs.value != rhs.value; } protected: - explicit NullablePointer(Ptr p) noexcept : value(p) { } + constexpr explicit NullablePointer(Ptr p) noexcept : value(p) { } Ptr value; }; @@ -641,16 +668,16 @@ namespace __gnu_test struct NullablePointer<void> { NullablePointer() = default; - NullablePointer(std::nullptr_t) noexcept { } - explicit NullablePointer(const volatile void*) noexcept { } + constexpr NullablePointer(std::nullptr_t) noexcept { } + constexpr explicit NullablePointer(const volatile void*) noexcept { } - explicit operator bool() const noexcept { return false; } + constexpr explicit operator bool() const noexcept { return false; } - friend inline bool + friend constexpr bool operator==(NullablePointer, NullablePointer) noexcept { return true; } - friend inline bool + friend constexpr bool operator!=(NullablePointer, NullablePointer) noexcept { return false; } }; diff --git a/libstdc++-v3/testsuite/util/testsuite_common_types.h b/libstdc++-v3/testsuite/util/testsuite_common_types.h index cd36a20..98c0f53 100644 --- a/libstdc++-v3/testsuite/util/testsuite_common_types.h +++ b/libstdc++-v3/testsuite/util/testsuite_common_types.h @@ -22,14 +22,6 @@ #ifndef _TESTSUITE_COMMON_TYPES_H #define _TESTSUITE_COMMON_TYPES_H 1 -#include <ext/typelist.h> - -#include <ext/new_allocator.h> -#include <ext/malloc_allocator.h> -#include <ext/mt_allocator.h> -#include <ext/bitmap_allocator.h> -#include <ext/pool_allocator.h> - #include <algorithm> #include <vector> @@ -53,6 +45,14 @@ namespace unord = std; namespace unord = std::tr1; #endif +#include <ext/typelist.h> + +#include <ext/new_allocator.h> +#include <ext/malloc_allocator.h> +#include <ext/mt_allocator.h> +#include <ext/bitmap_allocator.h> +#include <ext/pool_allocator.h> + namespace __gnu_test { using __gnu_cxx::typelist::null_type; diff --git a/libstdc++-v3/testsuite/util/testsuite_containers.h b/libstdc++-v3/testsuite/util/testsuite_containers.h index 37491a4..4021585 100644 --- a/libstdc++-v3/testsuite/util/testsuite_containers.h +++ b/libstdc++-v3/testsuite/util/testsuite_containers.h @@ -20,10 +20,10 @@ #ifndef _GLIBCXX_TESTSUITE_CONTAINERS_H #define _GLIBCXX_TESTSUITE_CONTAINERS_H -#include <bits/boost_concept_check.h> #include <cassert> -#include <testsuite_container_traits.h> #include <utility> // for rel_ops. +#include <bits/boost_concept_check.h> +#include <testsuite_container_traits.h> // Container requirement testing. namespace __gnu_test @@ -210,6 +210,9 @@ namespace __gnu_test clit = container.cbegin(bn); assert( ++clit == container.cend(bn) ); + clit = container.begin(bn); + assert( ++clit == container.cend(bn) ); + assert( container.begin(bn) != container.cend(bn) ); } }; @@ -304,6 +307,9 @@ namespace __gnu_test assert( container.cbegin() != container.cend() ); assert( container.cbegin() != container.end() ); assert( container.begin() != container.cend() ); + + auto cit = container.begin(); + assert( cit == container.cbegin() ); } }; diff --git a/libstdc++-v3/testsuite/util/testsuite_error.h b/libstdc++-v3/testsuite/util/testsuite_error.h index 5295d60b..03b09ee 100644 --- a/libstdc++-v3/testsuite/util/testsuite_error.h +++ b/libstdc++-v3/testsuite/util/testsuite_error.h @@ -19,12 +19,13 @@ // <http://www.gnu.org/licenses/>. // -#include <string> -#include <testsuite_hooks.h> - #ifndef _TESTSUITE_ERROR_H #define _TESTSUITE_ERROR_H 1 +#include <string> +#include <system_error> +#include <testsuite_hooks.h> + namespace __gnu_test { struct test_category : public std::error_category diff --git a/libstdc++-v3/testsuite/util/testsuite_greedy_ops.h b/libstdc++-v3/testsuite/util/testsuite_greedy_ops.h index 3545848..9103fcb 100644 --- a/libstdc++-v3/testsuite/util/testsuite_greedy_ops.h +++ b/libstdc++-v3/testsuite/util/testsuite_greedy_ops.h @@ -15,6 +15,8 @@ // with this library; see the file COPYING3. If not see // <http://www.gnu.org/licenses/>. +#include <cstddef> + namespace greedy_ops { struct X diff --git a/libstdc++-v3/testsuite/util/testsuite_hooks.h b/libstdc++-v3/testsuite/util/testsuite_hooks.h index faa01ba..bf34fd1 100644 --- a/libstdc++-v3/testsuite/util/testsuite_hooks.h +++ b/libstdc++-v3/testsuite/util/testsuite_hooks.h @@ -58,16 +58,13 @@ # define _VERIFY_PRINT(S, F, L, P, C) __builtin_printf(S, F, L, P, C) #endif -#define VERIFY(fn) \ - do \ - { \ - if (! (fn)) \ - { \ - _VERIFY_PRINT("%s:%d: %s: Assertion '%s' failed.\n", \ - __FILE__, __LINE__, __PRETTY_FUNCTION__, #fn); \ - __builtin_abort(); \ - } \ - } while (false) +#define VERIFY(...) \ + ((void)((__VA_ARGS__) \ + ? (void)(true ? true : bool(__VA_ARGS__)) \ + : (_VERIFY_PRINT("%s:%d: %s: Assertion '%s' failed.\n", \ + __FILE__, __LINE__, __PRETTY_FUNCTION__, \ + #__VA_ARGS__), \ + __builtin_abort()))) #ifdef _GLIBCXX_HAVE_UNISTD_H # include <unistd.h> diff --git a/libstdc++-v3/testsuite/util/testsuite_iterators.h b/libstdc++-v3/testsuite/util/testsuite_iterators.h index 74a8739..70383b8 100644 --- a/libstdc++-v3/testsuite/util/testsuite_iterators.h +++ b/libstdc++-v3/testsuite/util/testsuite_iterators.h @@ -31,7 +31,7 @@ #include <bits/stl_iterator_base_types.h> #if __cplusplus >= 201103L -#include <bits/move.h> +#include <utility> #endif #if __cplusplus > 201703L @@ -61,10 +61,12 @@ namespace __gnu_test T* first; T* last; + _GLIBCXX_CONSTEXPR BoundsContainer(T* _first, T* _last) : first(_first), last(_last) { } - std::size_t size() const { return last - first; } + _GLIBCXX_CONSTEXPR std::size_t + size() const { return last - first; } }; // Simple container for holding state of a set of output iterators. @@ -74,11 +76,13 @@ namespace __gnu_test T* incrementedto; bool* writtento; + _GLIBCXX20_CONSTEXPR OutputContainer(T* _first, T* _last) : BoundsContainer<T>(_first, _last), incrementedto(_first), writtento(new bool[this->size()]()) { } + _GLIBCXX20_CONSTEXPR ~OutputContainer() { delete[] writtento; } }; @@ -92,12 +96,14 @@ namespace __gnu_test public: OutputContainer<T>* SharedInfo; + _GLIBCXX_CONSTEXPR WritableObject(T* ptr_in, OutputContainer<T>* SharedInfo_in): ptr(ptr_in), SharedInfo(SharedInfo_in) { } #if __cplusplus >= 201103L template<class U> + _GLIBCXX14_CONSTEXPR typename std::enable_if<std::is_assignable<T&, U>::value>::type operator=(U&& new_val) const { @@ -107,6 +113,7 @@ namespace __gnu_test } #else template<class U> + _GLIBCXX14_CONSTEXPR void operator=(const U& new_val) { @@ -128,6 +135,7 @@ namespace __gnu_test struct output_iterator_wrapper { protected: + _GLIBCXX_CONSTEXPR output_iterator_wrapper() : ptr(0), SharedInfo(0) { } @@ -142,6 +150,7 @@ namespace __gnu_test T* ptr; ContainerType* SharedInfo; + _GLIBCXX14_CONSTEXPR output_iterator_wrapper(T* _ptr, ContainerType* SharedInfo_in) : ptr(_ptr), SharedInfo(SharedInfo_in) { @@ -155,6 +164,7 @@ namespace __gnu_test operator=(const output_iterator_wrapper&) = default; #endif + _GLIBCXX14_CONSTEXPR WritableObject<T> operator*() const { @@ -163,6 +173,7 @@ namespace __gnu_test return WritableObject<T>(ptr, SharedInfo); } + _GLIBCXX14_CONSTEXPR output_iterator_wrapper& operator++() { @@ -173,6 +184,7 @@ namespace __gnu_test return *this; } + _GLIBCXX14_CONSTEXPR output_iterator_wrapper operator++(int) { @@ -224,13 +236,19 @@ namespace __gnu_test struct deref_proxy { T* ptr; - operator const T&() const { return *ptr; } + + _GLIBCXX_CONSTEXPR + operator const T&() const + { return *ptr; } } p; - deref_proxy operator*() const { return p; } + _GLIBCXX_CONSTEXPR + deref_proxy operator*() const + { return p; } }; protected: + _GLIBCXX_CONSTEXPR input_iterator_wrapper() : ptr(0), SharedInfo(0) { } @@ -245,6 +263,7 @@ namespace __gnu_test T* ptr; ContainerType* SharedInfo; + _GLIBCXX14_CONSTEXPR input_iterator_wrapper(T* _ptr, ContainerType* SharedInfo_in) : ptr(_ptr), SharedInfo(SharedInfo_in) { ITERATOR_VERIFY(ptr >= SharedInfo->first && ptr <= SharedInfo->last); } @@ -256,6 +275,7 @@ namespace __gnu_test operator=(const input_iterator_wrapper&) = default; #endif + _GLIBCXX14_CONSTEXPR bool operator==(const input_iterator_wrapper& in) const { @@ -264,26 +284,34 @@ namespace __gnu_test return ptr == in.ptr; } + _GLIBCXX14_CONSTEXPR bool operator!=(const input_iterator_wrapper& in) const { return !(*this == in); } - T& - operator*() const + _GLIBCXX_CONSTEXPR + T* base() const + { + return ptr; + } + + _GLIBCXX14_CONSTEXPR + T& operator*() const { ITERATOR_VERIFY(SharedInfo && ptr < SharedInfo->last); ITERATOR_VERIFY(ptr >= SharedInfo->first); return *ptr; } - T* - operator->() const + _GLIBCXX14_CONSTEXPR + T* operator->() const { return &**this; } + _GLIBCXX14_CONSTEXPR input_iterator_wrapper& operator++() { @@ -294,6 +322,7 @@ namespace __gnu_test return *this; } + _GLIBCXX14_CONSTEXPR post_inc_proxy operator++(int) { @@ -334,10 +363,12 @@ namespace __gnu_test typedef BoundsContainer<T> ContainerType; typedef std::forward_iterator_tag iterator_category; + _GLIBCXX14_CONSTEXPR forward_iterator_wrapper(T* _ptr, ContainerType* SharedInfo_in) : input_iterator_wrapper<T>(_ptr, SharedInfo_in) { } + _GLIBCXX14_CONSTEXPR forward_iterator_wrapper() { } @@ -348,17 +379,18 @@ namespace __gnu_test operator=(const forward_iterator_wrapper&) = default; #endif - T& - operator*() const + _GLIBCXX14_CONSTEXPR + T& operator*() const { ITERATOR_VERIFY(this->SharedInfo && this->ptr < this->SharedInfo->last); return *(this->ptr); } - T* - operator->() const + _GLIBCXX14_CONSTEXPR + T* operator->() const { return &**this; } + _GLIBCXX14_CONSTEXPR forward_iterator_wrapper& operator++() { @@ -367,6 +399,7 @@ namespace __gnu_test return *this; } + _GLIBCXX14_CONSTEXPR forward_iterator_wrapper operator++(int) { @@ -376,8 +409,8 @@ namespace __gnu_test } #if __cplusplus >= 201402L - bool - operator==(const forward_iterator_wrapper& it) const noexcept + constexpr + bool operator==(const forward_iterator_wrapper& it) const noexcept { // Since C++14 value-initialized forward iterators are comparable. if (this->SharedInfo == nullptr || it.SharedInfo == nullptr) @@ -388,8 +421,8 @@ namespace __gnu_test return base_this == base_that; } - bool - operator!=(const forward_iterator_wrapper& it) const noexcept + constexpr + bool operator!=(const forward_iterator_wrapper& it) const noexcept { return !(*this == it); } @@ -409,10 +442,12 @@ namespace __gnu_test typedef BoundsContainer<T> ContainerType; typedef std::bidirectional_iterator_tag iterator_category; + _GLIBCXX14_CONSTEXPR bidirectional_iterator_wrapper(T* _ptr, ContainerType* SharedInfo_in) : forward_iterator_wrapper<T>(_ptr, SharedInfo_in) { } + _GLIBCXX14_CONSTEXPR bidirectional_iterator_wrapper() : forward_iterator_wrapper<T>() { } @@ -425,6 +460,7 @@ namespace __gnu_test operator=(const bidirectional_iterator_wrapper&) = default; #endif + _GLIBCXX14_CONSTEXPR bidirectional_iterator_wrapper& operator++() { @@ -433,6 +469,7 @@ namespace __gnu_test return *this; } + _GLIBCXX14_CONSTEXPR bidirectional_iterator_wrapper operator++(int) { @@ -441,6 +478,7 @@ namespace __gnu_test return tmp; } + _GLIBCXX14_CONSTEXPR bidirectional_iterator_wrapper& operator--() { @@ -449,6 +487,7 @@ namespace __gnu_test return *this; } + _GLIBCXX14_CONSTEXPR bidirectional_iterator_wrapper operator--(int) { @@ -472,10 +511,12 @@ namespace __gnu_test typedef BoundsContainer<T> ContainerType; typedef std::random_access_iterator_tag iterator_category; + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper(T* _ptr, ContainerType* SharedInfo_in) : bidirectional_iterator_wrapper<T>(_ptr, SharedInfo_in) { } + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper() : bidirectional_iterator_wrapper<T>() { } @@ -488,6 +529,7 @@ namespace __gnu_test operator=(const random_access_iterator_wrapper&) = default; #endif + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper& operator++() { @@ -496,6 +538,7 @@ namespace __gnu_test return *this; } + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper operator++(int) { @@ -504,6 +547,7 @@ namespace __gnu_test return tmp; } + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper& operator--() { @@ -512,6 +556,7 @@ namespace __gnu_test return *this; } + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper operator--(int) { @@ -520,6 +565,7 @@ namespace __gnu_test return tmp; } + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper& operator+=(std::ptrdiff_t n) { @@ -536,10 +582,12 @@ namespace __gnu_test return *this; } + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper& operator-=(std::ptrdiff_t n) { return *this += -n; } + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper operator-(std::ptrdiff_t n) const { @@ -547,6 +595,7 @@ namespace __gnu_test return tmp -= n; } + _GLIBCXX14_CONSTEXPR std::ptrdiff_t operator-(const random_access_iterator_wrapper<T>& in) const { @@ -554,46 +603,66 @@ namespace __gnu_test return this->ptr - in.ptr; } - T& - operator[](std::ptrdiff_t n) const + _GLIBCXX14_CONSTEXPR + T& operator[](std::ptrdiff_t n) const { return *(*this + n); } - bool - operator<(const random_access_iterator_wrapper<T>& in) const +#if __cplusplus >= 201103L + // Ensure that the iterator's difference_type is always used. + template<typename D> void operator+=(D) = delete; + template<typename D> void operator-=(D) = delete; + template<typename D> void operator[](D) const = delete; + template<typename D> + typename std::enable_if<std::is_integral<D>::value>::type + operator-(D) const = delete; +#endif + + _GLIBCXX14_CONSTEXPR + bool operator<(const random_access_iterator_wrapper<T>& in) const { ITERATOR_VERIFY(this->SharedInfo == in.SharedInfo); return this->ptr < in.ptr; } - bool - operator>(const random_access_iterator_wrapper<T>& in) const + _GLIBCXX14_CONSTEXPR + bool operator>(const random_access_iterator_wrapper<T>& in) const { return in < *this; } - bool - operator>=(const random_access_iterator_wrapper<T>& in) const + _GLIBCXX14_CONSTEXPR + bool operator>=(const random_access_iterator_wrapper<T>& in) const { return !(*this < in); } - bool - operator<=(const random_access_iterator_wrapper<T>& in) const + _GLIBCXX14_CONSTEXPR + bool operator<=(const random_access_iterator_wrapper<T>& in) const { return !(*this > in); } }; template<typename T> + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper<T> operator+(random_access_iterator_wrapper<T> it, std::ptrdiff_t n) { return it += n; } template<typename T> + _GLIBCXX14_CONSTEXPR random_access_iterator_wrapper<T> operator+(std::ptrdiff_t n, random_access_iterator_wrapper<T> it) { return it += n; } +#if __cplusplus >= 201103L + // Ensure that the iterator's difference_type is always used. + template<typename T, typename D> + void operator+(random_access_iterator_wrapper<T>, D) = delete; + template<typename T, typename D> + void operator+(D, random_access_iterator_wrapper<T>) = delete; +#endif + /** * @brief A container-type class for holding iterator wrappers @@ -605,16 +674,22 @@ namespace __gnu_test template <class T, template<class TT> class ItType> struct test_container { + typedef ItType<T> iterator; + typedef typename iterator::value_type value_type; + typename ItType<T>::ContainerType bounds; + _GLIBCXX_CONSTEXPR test_container(T* _first, T* _last) : bounds(_first, _last) { } template<std::size_t N> explicit + _GLIBCXX_CONSTEXPR test_container(T (&arr)[N]) : bounds(arr, arr+N) { } + _GLIBCXX14_CONSTEXPR ItType<T> it(int pos) { @@ -622,6 +697,7 @@ namespace __gnu_test return ItType<T>(bounds.first + pos, &bounds); } + _GLIBCXX14_CONSTEXPR ItType<T> it(T* pos) { @@ -629,18 +705,22 @@ namespace __gnu_test return ItType<T>(pos, &bounds); } + _GLIBCXX_CONSTEXPR const T& val(int pos) { return (bounds.first)[pos]; } + _GLIBCXX14_CONSTEXPR ItType<T> begin() { return it(bounds.first); } + _GLIBCXX14_CONSTEXPR ItType<T> end() { return it(bounds.last); } + _GLIBCXX_CONSTEXPR std::size_t size() const { return bounds.size(); } @@ -680,6 +760,7 @@ namespace __gnu_test // Use an integer-class type to try and break the library code. using difference_type = std::ranges::__detail::__max_diff_type; + constexpr contiguous_iterator_wrapper& operator++() { @@ -687,6 +768,7 @@ namespace __gnu_test return *this; } + constexpr contiguous_iterator_wrapper& operator--() { @@ -694,6 +776,7 @@ namespace __gnu_test return *this; } + constexpr contiguous_iterator_wrapper operator++(int) { @@ -702,6 +785,7 @@ namespace __gnu_test return tmp; } + constexpr contiguous_iterator_wrapper operator--(int) { @@ -710,6 +794,7 @@ namespace __gnu_test return tmp; } + constexpr contiguous_iterator_wrapper& operator+=(difference_type n) { @@ -718,23 +803,28 @@ namespace __gnu_test return *this; } - friend contiguous_iterator_wrapper + friend constexpr + contiguous_iterator_wrapper operator+(contiguous_iterator_wrapper iter, difference_type n) { return iter += n; } - friend contiguous_iterator_wrapper + friend constexpr + contiguous_iterator_wrapper operator+(difference_type n, contiguous_iterator_wrapper iter) { return iter += n; } + constexpr contiguous_iterator_wrapper& operator-=(difference_type n) { return *this += -n; } - friend contiguous_iterator_wrapper + friend constexpr + contiguous_iterator_wrapper operator-(contiguous_iterator_wrapper iter, difference_type n) { return iter -= n; } - friend difference_type + friend constexpr + difference_type operator-(contiguous_iterator_wrapper l, contiguous_iterator_wrapper r) { const random_access_iterator_wrapper<T>& lbase = l; @@ -742,6 +832,7 @@ namespace __gnu_test return static_cast<difference_type>(lbase - rbase); } + constexpr decltype(auto) operator[](difference_type n) const { auto d = static_cast<std::ptrdiff_t>(n); @@ -759,6 +850,7 @@ namespace __gnu_test { using input_iterator_wrapper<T>::input_iterator_wrapper; + constexpr input_iterator_wrapper_nocopy() : input_iterator_wrapper<T>(nullptr, nullptr) { } @@ -773,6 +865,7 @@ namespace __gnu_test using input_iterator_wrapper<T>::operator++; + constexpr input_iterator_wrapper_nocopy& operator++() { @@ -789,6 +882,7 @@ namespace __gnu_test using input_iterator_wrapper<T>::operator++; + constexpr input_iterator_wrapper_rval& operator++() { @@ -796,8 +890,8 @@ namespace __gnu_test return *this; } - T&& - operator*() const + constexpr + T&& operator*() const { return std::move(input_iterator_wrapper<T>::operator*()); } }; @@ -815,7 +909,9 @@ namespace __gnu_test using Iter<T>::operator++; - iterator& operator++() { Iter<T>::operator++(); return *this; } + constexpr + iterator& operator++() + { Iter<T>::operator++(); return *this; } }; template<typename I> @@ -823,21 +919,24 @@ namespace __gnu_test { T* end; - friend bool operator==(const sentinel& s, const I& i) noexcept + friend constexpr bool + operator==(const sentinel& s, const I& i) noexcept { return s.end == i.ptr; } - friend auto operator-(const sentinel& s, const I& i) noexcept + friend constexpr + auto operator-(const sentinel& s, const I& i) noexcept requires std::random_access_iterator<I> { return std::iter_difference_t<I>(s.end - i.ptr); } - friend auto operator-(const I& i, const sentinel& s) noexcept + friend constexpr auto + operator-(const I& i, const sentinel& s) noexcept requires std::random_access_iterator<I> { return std::iter_difference_t<I>(i.ptr - s.end); } }; protected: - auto - get_iterator(T* p) + constexpr + auto get_iterator(T* p) { if constexpr (std::default_initializable<Iter<T>>) return Iter<T>(p, &bounds); @@ -846,17 +945,19 @@ namespace __gnu_test } public: + constexpr test_range(T* first, T* last) : bounds(first, last) { } template<std::size_t N> - explicit + explicit constexpr test_range(T (&arr)[N]) : test_range(arr, arr+N) { } - auto begin() & { return get_iterator(bounds.first); } + constexpr auto begin() & + { return get_iterator(bounds.first); } - auto end() & + constexpr auto end() & { using I = decltype(get_iterator(bounds.last)); return sentinel<I>{bounds.last}; @@ -869,7 +970,9 @@ namespace __gnu_test template<typename T, template<typename> class Iter> struct test_range_nocopy : test_range<T, Iter> { - test_range_nocopy(T* first, T* last) : test_range<T, Iter>(first, last) + constexpr + test_range_nocopy(T* first, T* last) + : test_range<T, Iter>(first, last) {} test_range_nocopy(test_range_nocopy&&) = default; @@ -904,6 +1007,7 @@ namespace __gnu_test { using test_range<T, Iter>::test_range; + constexpr std::size_t size() const noexcept { return this->bounds.size(); } }; @@ -939,18 +1043,22 @@ namespace __gnu_test { T* end; - friend bool operator==(const sentinel& s, const I& i) noexcept + friend constexpr + bool operator==(const sentinel& s, const I& i) noexcept { return s.end == i.ptr; } - friend std::iter_difference_t<I> + friend constexpr + std::iter_difference_t<I> operator-(const sentinel& s, const I& i) noexcept { return std::iter_difference_t<I>(s.end - i.ptr); } - friend std::iter_difference_t<I> + friend constexpr + std::iter_difference_t<I> operator-(const I& i, const sentinel& s) noexcept { return std::iter_difference_t<I>(i.ptr - s.end); } }; + constexpr auto end() & { using I = decltype(this->get_iterator(this->bounds.last)); diff --git a/libstdc++-v3/testsuite/util/testsuite_new_operators.h b/libstdc++-v3/testsuite/util/testsuite_new_operators.h index bf0dab8..3e372b8 100644 --- a/libstdc++-v3/testsuite/util/testsuite_new_operators.h +++ b/libstdc++-v3/testsuite/util/testsuite_new_operators.h @@ -23,6 +23,7 @@ #define _GLIBCXX_TESTSUITE_NEW_OPERATORS_H #include <new> +#include <cstdlib> #include <testsuite_hooks.h> namespace __gnu_test diff --git a/libstdc++-v3/testsuite/util/testsuite_random.h b/libstdc++-v3/testsuite/util/testsuite_random.h index 533be4f..68968ae 100644 --- a/libstdc++-v3/testsuite/util/testsuite_random.h +++ b/libstdc++-v3/testsuite/util/testsuite_random.h @@ -27,6 +27,7 @@ #include <cmath> #include <initializer_list> #include <system_error> +#include <random> #include <testsuite_hooks.h> namespace __gnu_test |
