[libcxx-commits] [libcxx] de41a35 - [libc++] Implement LWG2991: variant copy constructor missing noexcept(see below) (#230241)
via libcxx-commits
libcxx-commits at lists.llvm.org
Sun Oct 11 01:27:12 PDT 2026
Author: Karolishp
Date: 2026-10-11T16:27:07+08:00
New Revision: de41a359c9b645aa0279cb26e7e98e25f0159cca
URL: https://github.com/llvm/llvm-project/commit/de41a359c9b645aa0279cb26e7e98e25f0159cca
DIFF: https://github.com/llvm/llvm-project/commit/de41a359c9b645aa0279cb26e7e98e25f0159cca.diff
LOG: [libc++] Implement LWG2991: variant copy constructor missing noexcept(see below) (#230241)
LWG2991 adds a conditional `noexcept` to the variant's copy constructor.
A small example to illustrate the issue:
```cpp
static_assert(!std::is_trivially_copy_constructible_v<std::shared_ptr<int>>);
static_assert(std::is_copy_constructible_v<std::shared_ptr<int>>);
static_assert(std::is_nothrow_copy_constructible_v<std::shared_ptr<int>>);
static_assert(!std::is_trivially_move_constructible_v<std::shared_ptr<int>>);
static_assert(std::is_move_constructible_v<std::shared_ptr<int>>);
static_assert(std::is_nothrow_move_constructible_v<std::shared_ptr<int>>);
using Variant = std::variant<int,std::shared_ptr<int>>;
static_assert(std::is_nothrow_copy_constructible_v<Variant>); // false, should be true
static_assert(std::is_nothrow_move_constructible_v<Variant>);
```
The change mirrors the move constructor which was already correctly
conditionally noexcept. I added tests to cover the different
combinations of (`_TriviallyAvailable`/`_Available`) and
(`noexcept(true)`/`noexcept(false)`).
Fixes #171301
## AI Disclosure
AI was used to review the changes before putting this PR up.
Added:
Modified:
libcxx/docs/Status/Cxx26Issues.csv
libcxx/include/variant
libcxx/test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp
Removed:
################################################################################
diff --git a/libcxx/docs/Status/Cxx26Issues.csv b/libcxx/docs/Status/Cxx26Issues.csv
index 9f2a8edbde8c9..b46b90167298e 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 393aea461af9f..5b01f09ed2575 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,10 @@ _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 820ff9e0d1a9d..7165e7962929d 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>
@@ -51,6 +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 {
+ NoThrowNTCopy(const NoThrowNTCopy&) noexcept;
+};
+
+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) {}
@@ -60,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) {}
@@ -99,6 +109,11 @@ void makeEmpty(Variant& v) {
}
#endif // TEST_HAS_NO_EXCEPTIONS
+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>>);
+
constexpr void test_copy_ctor_sfinae() {
{
using V = std::variant<int, long>;
More information about the libcxx-commits
mailing list