[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