[libcxx-commits] [libcxx] [libc++] Implement LWG2991: variant copy constructor missing noexcept(see below) (PR #230241)
via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Oct 8 11:45:58 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: Karolishp (KaroliShp)
<details>
<summary>Changes</summary>
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/230241.diff
3 Files Affected:
- (modified) libcxx/docs/Status/Cxx26Issues.csv (+1-1)
- (modified) libcxx/include/variant (+4-4)
- (modified) libcxx/test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp (+34-1)
``````````diff
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();
``````````
</details>
https://github.com/llvm/llvm-project/pull/230241
More information about the libcxx-commits
mailing list