aboutsummaryrefslogtreecommitdiff
path: root/libstdc++-v3/testsuite
diff options
context:
space:
mode:
authorLuc Grosheintz <luc.grosheintz@gmail.com>2025-09-04 14:20:28 +0200
committerTomasz Kamiński <tkaminsk@redhat.com>2025-09-08 09:38:53 +0200
commitc440b585ba374b6348ef223e4891a717a1f6660c (patch)
tree3ad8da30efbb30d17276f54ca2d95fc21ddb27bb /libstdc++-v3/testsuite
parent565d9a3617fcc167f190e04445a1d7952b4d9bba (diff)
downloadgcc-c440b585ba374b6348ef223e4891a717a1f6660c.zip
gcc-c440b585ba374b6348ef223e4891a717a1f6660c.tar.gz
gcc-c440b585ba374b6348ef223e4891a717a1f6660c.tar.bz2
libstdc++: Implement constant_wrapper, cw from P2781R9.
This is a partial implementation of P2781R9. It adds std::cw and std::constant_wrapper, but doesn't modify __integral_constant_like for span/mdspan. libstdc++-v3/ChangeLog: * include/bits/version.def (constant_wrapper): Add. * include/bits/version.h: Regenerate. * include/std/type_traits (_CwFixedValue): New class. (_IndexSequence): New struct. (_BuildIndexSequence): New struct. (_ConstExprParam): New concept. (_CwOperators): New struct. (constant_wrapper): New struct. (cw): New global constant. * src/c++23/std.cc.in (constant_wrapper): Add. (cw): Add. * testsuite/20_util/constant_wrapper/adl.cc: New test. * testsuite/20_util/constant_wrapper/ex.cc: New test. * testsuite/20_util/constant_wrapper/generic.cc: New test. * testsuite/20_util/constant_wrapper/instantiate.cc: New test. * testsuite/20_util/constant_wrapper/op_comma_neg.cc: New test. * testsuite/20_util/constant_wrapper/version.cc: New test. Reviewed-by: Jonathan Wakely <jwakely@redhat.com> Co-authored-by: Tomasz Kamiński <tkaminsk@redhat.com> Signed-off-by: Luc Grosheintz <luc.grosheintz@gmail.com> Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
Diffstat (limited to 'libstdc++-v3/testsuite')
-rw-r--r--libstdc++-v3/testsuite/20_util/constant_wrapper/adl.cc86
-rw-r--r--libstdc++-v3/testsuite/20_util/constant_wrapper/ex.cc39
-rw-r--r--libstdc++-v3/testsuite/20_util/constant_wrapper/generic.cc391
-rw-r--r--libstdc++-v3/testsuite/20_util/constant_wrapper/instantiate.cc575
-rw-r--r--libstdc++-v3/testsuite/20_util/constant_wrapper/op_comma_neg.cc14
-rw-r--r--libstdc++-v3/testsuite/20_util/constant_wrapper/other_wrappers.cc75
-rw-r--r--libstdc++-v3/testsuite/20_util/constant_wrapper/version.cc11
7 files changed, 1191 insertions, 0 deletions
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