[libcxx-commits] [libcxx] [libc++] Implement LWG2991: variant copy constructor missing noexcept(see below) (PR #230241)
via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Oct 9 04:23:05 PDT 2026
https://github.com/KaroliShp updated https://github.com/llvm/llvm-project/pull/230241
>From 74fc538e4de750fbea458bcf02e46eb36f8f210b Mon Sep 17 00:00:00 2001
From: Karolis <karolis.spukas at gmail.com>
Date: Thu, 8 Oct 2026 19:03:50 +0100
Subject: [PATCH 1/2] [libc++] Implement LWG2991: variant copy constructor
missing noexcept(see below)
---
libcxx/docs/Status/Cxx26Issues.csv | 2 +-
libcxx/include/variant | 8 ++---
.../variant.ctor/copy.pass.cpp | 35 ++++++++++++++++++-
3 files changed, 39 insertions(+), 6 deletions(-)
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 7cb257a49c30f0..7a0be30b70986f 100644
--- a/libcxx/docs/Status/Cxx26Issues.csv
+++ b/libcxx/docs/Status/Cxx26Issues.csv
@@ -146,7 +146,7 @@
"`LWG4245 <https://wg21.link/LWG4245>`__","Operators that interact with ``counted_iterator`` and ``default_sentinel_t`` should be ``noexcept``","2025-06 (Sofia)","","","`#148226 <https://github.com/llvm/llvm-project/issues/148226>`__",""
"`LWG4247 <https://wg21.link/LWG4247>`__","Header ``<stdbit.h>`` is not yet freestanding","2025-06 (Sofia)","","","`#148227 <https://github.com/llvm/llvm-project/issues/148227>`__",""
"","","","","","",""
-"`LWG2991 <https://wg21.link/LWG2991>`__","``variant copy`` constructor missing ``noexcept(see below)``","2025-11 (Kona)","","","`#171301 <https://github.com/llvm/llvm-project/issues/171301>`__",""
+"`LWG2991 <https://wg21.link/LWG2991>`__","``variant copy`` constructor missing ``noexcept(see below)``","2025-11 (Kona)","|Complete|","24","`#171301 <https://github.com/llvm/llvm-project/issues/171301>`__",""
"`LWG3090 <https://wg21.link/LWG3090>`__","What is ยง[time.duration.cons]p4's ""no overflow is induced in the conversion"" intended to mean?","2025-11 (Kona)","","","`#171302 <https://github.com/llvm/llvm-project/issues/171302>`__",""
"`LWG3343 <https://wg21.link/LWG3343>`__","Ordering of calls to ``unlock()`` and ``notify_all()`` in Effects element of ``notify_all_at_thread_exit()`` should be reversed","2025-11 (Kona)","|Complete|","16","`#105356 <https://github.com/llvm/llvm-project/issues/105356>`__",""
"`LWG3454 <https://wg21.link/LWG3454>`__","``pointer_traits::pointer_to`` should be ``constexpr``","2025-11 (Kona)","|Complete|","16","`#171307 <https://github.com/llvm/llvm-project/issues/171307>`__",""
diff --git a/libcxx/include/variant b/libcxx/include/variant
index 393aea461af9f7..765035db8117a4 100644
--- a/libcxx/include/variant
+++ b/libcxx/include/variant
@@ -22,7 +22,7 @@ namespace std {
// 20.7.2.1, constructors
constexpr variant() noexcept(see below);
- constexpr variant(const variant&);
+ constexpr variant(const variant&) noexcept(see below);
constexpr variant(variant&&) noexcept(see below);
template <class T> constexpr variant(T&&) noexcept(see below);
@@ -904,9 +904,9 @@ _LIBCPP_VARIANT_COPY_CONSTRUCTOR(
_LIBCPP_VARIANT_COPY_CONSTRUCTOR(
_Trait::_Available,
- _LIBCPP_HIDE_FROM_ABI
- _LIBCPP_CONSTEXPR_SINCE_CXX20 __copy_constructor(const __copy_constructor& __that) : __copy_constructor(
- __valueless_t{}) { this->__generic_construct(*this, __that); } _LIBCPP_EAT_SEMICOLON);
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __copy_constructor(const __copy_constructor& __that) noexcept(
+ __all<is_nothrow_copy_constructible_v<_Types>...>::value)
+ : __copy_constructor(__valueless_t{}) { this->__generic_construct(*this, __that); } _LIBCPP_EAT_SEMICOLON);
_LIBCPP_VARIANT_COPY_CONSTRUCTOR(
_Trait::_Unavailable,
diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp
index 820ff9e0d1a9d0..8a422a40f70936 100644
--- a/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp
+++ b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp
@@ -12,7 +12,7 @@
// template <class ...Types> class variant;
-// constexpr variant(variant const&);
+// constexpr variant(variant const&) noexcept(see below);
#include <cassert>
#include <type_traits>
@@ -52,6 +52,16 @@ struct NTCopy {
static_assert(!std::is_trivially_copy_constructible<NTCopy>::value, "");
static_assert(std::is_copy_constructible<NTCopy>::value, "");
+struct NoThrowNTCopy {
+ constexpr NoThrowNTCopy(int v) : value(v) {}
+ NoThrowNTCopy(const NoThrowNTCopy& that) noexcept : value(that.value) {}
+ NoThrowNTCopy(NoThrowNTCopy&&) = delete;
+ int value;
+};
+
+static_assert(!std::is_trivially_copy_constructible<NoThrowNTCopy>::value, "");
+static_assert(std::is_copy_constructible<NoThrowNTCopy>::value, "");
+
struct TCopy {
constexpr TCopy(int v) : value(v) {}
TCopy(TCopy const&) = default;
@@ -99,6 +109,28 @@ void makeEmpty(Variant& v) {
}
#endif // TEST_HAS_NO_EXCEPTIONS
+constexpr void test_copy_noexcept() {
+ {
+ using V = std::variant<int, long>;
+ static_assert(std::is_nothrow_copy_constructible<V>::value, "");
+ }
+ {
+ using V = std::variant<int, TCopy>;
+ static_assert(std::is_nothrow_copy_constructible<TCopy>::value, "");
+ static_assert(std::is_nothrow_copy_constructible<V>::value, "");
+ }
+ {
+ using V = std::variant<int, NTCopy>;
+ static_assert(!std::is_nothrow_copy_constructible<NTCopy>::value, "");
+ static_assert(!std::is_nothrow_copy_constructible<V>::value, "");
+ }
+ {
+ using V = std::variant<int, NoThrowNTCopy>;
+ static_assert(std::is_nothrow_copy_constructible<NoThrowNTCopy>::value, "");
+ static_assert(std::is_nothrow_copy_constructible<V>::value, "");
+ }
+}
+
constexpr void test_copy_ctor_sfinae() {
{
using V = std::variant<int, long>;
@@ -268,6 +300,7 @@ TEST_CONSTEXPR_CXX20 void test_constexpr_copy_ctor_non_trivial() {
void non_constexpr_test() { test_copy_ctor_valueless_by_exception(); }
constexpr bool cxx17_constexpr_test() {
+ test_copy_noexcept();
test_copy_ctor_sfinae();
test_constexpr_copy_ctor_trivial();
>From 75947ed91219b65345d2042260dbed0614d73315 Mon Sep 17 00:00:00 2001
From: Karolis <karolis.spukas at gmail.com>
Date: Fri, 9 Oct 2026 12:22:50 +0100
Subject: [PATCH 2/2] Address review comments
---
.../variant.ctor/copy.pass.cpp | 40 +++++--------------
1 file changed, 11 insertions(+), 29 deletions(-)
diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp
index 8a422a40f70936..55641593571b1e 100644
--- a/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp
+++ b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp
@@ -51,16 +51,15 @@ struct NTCopy {
static_assert(!std::is_trivially_copy_constructible<NTCopy>::value, "");
static_assert(std::is_copy_constructible<NTCopy>::value, "");
+static_assert(!std::is_nothrow_copy_constructible_v<NTCopy>);
struct NoThrowNTCopy {
- constexpr NoThrowNTCopy(int v) : value(v) {}
- NoThrowNTCopy(const NoThrowNTCopy& that) noexcept : value(that.value) {}
- NoThrowNTCopy(NoThrowNTCopy&&) = delete;
- int value;
+ NoThrowNTCopy(const NoThrowNTCopy&) noexcept;
};
-static_assert(!std::is_trivially_copy_constructible<NoThrowNTCopy>::value, "");
-static_assert(std::is_copy_constructible<NoThrowNTCopy>::value, "");
+static_assert(!std::is_trivially_copy_constructible_v<NoThrowNTCopy>);
+static_assert(std::is_copy_constructible_v<NoThrowNTCopy>);
+static_assert(std::is_nothrow_copy_constructible_v<NoThrowNTCopy>);
struct TCopy {
constexpr TCopy(int v) : value(v) {}
@@ -70,6 +69,7 @@ struct TCopy {
};
static_assert(std::is_trivially_copy_constructible<TCopy>::value, "");
+static_assert(std::is_nothrow_copy_constructible_v<TCopy>);
struct TCopyNTMove {
constexpr TCopyNTMove(int v) : value(v) {}
@@ -80,6 +80,11 @@ struct TCopyNTMove {
static_assert(std::is_trivially_copy_constructible<TCopyNTMove>::value, "");
+static_assert(std::is_nothrow_copy_constructible_v<std::variant<int, long>>);
+static_assert(std::is_nothrow_copy_constructible_v<std::variant<int, TCopy>>);
+static_assert(!std::is_nothrow_copy_constructible_v<std::variant<int, NTCopy>>);
+static_assert(std::is_nothrow_copy_constructible_v<std::variant<int, NoThrowNTCopy>>);
+
#ifndef TEST_HAS_NO_EXCEPTIONS
struct MakeEmptyT {
static int alive;
@@ -109,28 +114,6 @@ void makeEmpty(Variant& v) {
}
#endif // TEST_HAS_NO_EXCEPTIONS
-constexpr void test_copy_noexcept() {
- {
- using V = std::variant<int, long>;
- static_assert(std::is_nothrow_copy_constructible<V>::value, "");
- }
- {
- using V = std::variant<int, TCopy>;
- static_assert(std::is_nothrow_copy_constructible<TCopy>::value, "");
- static_assert(std::is_nothrow_copy_constructible<V>::value, "");
- }
- {
- using V = std::variant<int, NTCopy>;
- static_assert(!std::is_nothrow_copy_constructible<NTCopy>::value, "");
- static_assert(!std::is_nothrow_copy_constructible<V>::value, "");
- }
- {
- using V = std::variant<int, NoThrowNTCopy>;
- static_assert(std::is_nothrow_copy_constructible<NoThrowNTCopy>::value, "");
- static_assert(std::is_nothrow_copy_constructible<V>::value, "");
- }
-}
-
constexpr void test_copy_ctor_sfinae() {
{
using V = std::variant<int, long>;
@@ -300,7 +283,6 @@ TEST_CONSTEXPR_CXX20 void test_constexpr_copy_ctor_non_trivial() {
void non_constexpr_test() { test_copy_ctor_valueless_by_exception(); }
constexpr bool cxx17_constexpr_test() {
- test_copy_noexcept();
test_copy_ctor_sfinae();
test_constexpr_copy_ctor_trivial();
More information about the libcxx-commits
mailing list