[libcxx-commits] [libcxx] [libc++] Resolve LWG4308: LWG4308: std::optional<T&>::iterator can't be a contiguous iterator for some T (PR #173948)
William Tran-Viet via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Dec 29 19:38:49 PST 2025
https://github.com/smallp-o-p created https://github.com/llvm/llvm-project/pull/173948
Resolves #171345
- Constrains iterator to only be available if T is not an lvalue reference, or if it is T&, that T is an object type and is not an unbounded array
- Correct a static assert message as a drive-by
- Move the libcxx specific iterator test into the standard test
>From 08cee47ee327a20cc10eecea41b482694278bf8e Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Mon, 29 Dec 2025 21:27:40 -0500
Subject: [PATCH 1/3] Implement LWG4308
---
libcxx/include/optional | 5 +++--
.../optional/optional.iterator/iterator.compile.pass.cpp | 3 ++-
2 files changed, 5 insertions(+), 3 deletions(-)
rename libcxx/test/{libcxx => std}/utilities/optional/optional.iterator/iterator.compile.pass.cpp (88%)
diff --git a/libcxx/include/optional b/libcxx/include/optional
index 440fdf73a4310..ab58b845141d9 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -681,11 +681,12 @@ struct __is_std_optional<optional<_Tp>> : true_type {};
template <class _Tp, class = void>
struct __optional_iterator {};
+// LWG4308 optional<T&>: Constraint: T is an object type other than an array of unknown bound.
template <class _Tp>
struct __optional_iterator<
_Tp,
- enable_if_t<!(is_lvalue_reference_v<_Tp> && is_function_v<__libcpp_remove_reference_t<_Tp>>) &&
- !(is_lvalue_reference_v<_Tp> && is_array_v<__libcpp_remove_reference_t<_Tp>>)> > {
+ enable_if_t<!is_lvalue_reference_v<_Tp> || (is_object_v<__libcpp_remove_reference_t<_Tp>> &&
+ !__is_unbounded_array_v<__libcpp_remove_reference_t<_Tp>>)>> {
private:
using __pointer _LIBCPP_NODEBUG = add_pointer_t<remove_reference_t<_Tp>>;
using __const_pointer _LIBCPP_NODEBUG = add_pointer_t<const remove_reference_t<_Tp>>;
diff --git a/libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/iterator.compile.pass.cpp
similarity index 88%
rename from libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp
rename to libcxx/test/std/utilities/optional/optional.iterator/iterator.compile.pass.cpp
index b604579e43557..f6ee65783c8ce 100644
--- a/libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.iterator/iterator.compile.pass.cpp
@@ -25,5 +25,6 @@ static_assert(has_iterator_aliases<std::optional<int>>);
static_assert(has_iterator_aliases<std::optional<const int>>);
static_assert(has_iterator_aliases<std::optional<int&>>);
static_assert(has_iterator_aliases<std::optional<const int&>>);
-static_assert(!has_iterator_aliases<std::optional<int (&)[1]>>);
+static_assert(has_iterator_aliases<std::optional<int (&)[1]>>);
+static_assert(!has_iterator_aliases<std::optional<int (&)[]>>);
static_assert(!has_iterator_aliases<std::optional<int (&)()>>);
>From 5633f4b7c8790055adcc88000b5c7947c78d02b2 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Mon, 29 Dec 2025 21:52:37 -0500
Subject: [PATCH 2/3] Drive-by: Fix static assert message when constructing a
optional<T&> from a temporary
---
libcxx/include/optional | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/include/optional b/libcxx/include/optional
index ab58b845141d9..388a47487d512 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -499,7 +499,7 @@ struct __optional_storage_base<_Tp, true> {
_LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_storage_base(in_place_t, _UArg&& __uarg)
: __value_(std::addressof(__uarg)) {
static_assert(!__reference_constructs_from_temporary_v<_Tp, _UArg>,
- "Attempted to construct a reference element in tuple from a "
+ "Attempted to construct a reference element in optional from a "
"possible temporary");
}
>From dffc95cfb674147e698c3377db06c4841535d27c Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Mon, 29 Dec 2025 22:24:44 -0500
Subject: [PATCH 3/3] Move compile test into standard test
---
.../iterator.compile.pass.cpp | 30 -------------------
.../optional.iterator/iterator.pass.cpp | 19 +++++++++++-
2 files changed, 18 insertions(+), 31 deletions(-)
delete mode 100644 libcxx/test/std/utilities/optional/optional.iterator/iterator.compile.pass.cpp
diff --git a/libcxx/test/std/utilities/optional/optional.iterator/iterator.compile.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/iterator.compile.pass.cpp
deleted file mode 100644
index f6ee65783c8ce..0000000000000
--- a/libcxx/test/std/utilities/optional/optional.iterator/iterator.compile.pass.cpp
+++ /dev/null
@@ -1,30 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-// REQUIRES: std-at-least-c++26
-
-// <optional>
-
-// template <class T> class optional::iterator;
-// template <class T> class optional::const_iterator;
-
-#include <optional>
-
-template <typename T>
-concept has_iterator_aliases = requires {
- typename T::iterator;
- typename T::const_iterator;
-};
-
-static_assert(has_iterator_aliases<std::optional<int>>);
-static_assert(has_iterator_aliases<std::optional<const int>>);
-static_assert(has_iterator_aliases<std::optional<int&>>);
-static_assert(has_iterator_aliases<std::optional<const int&>>);
-static_assert(has_iterator_aliases<std::optional<int (&)[1]>>);
-static_assert(!has_iterator_aliases<std::optional<int (&)[]>>);
-static_assert(!has_iterator_aliases<std::optional<int (&)()>>);
diff --git a/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp b/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp
index 671fac35e732a..909969144063d 100644
--- a/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp
@@ -19,6 +19,12 @@
#include <type_traits>
#include <utility>
+template <typename T>
+concept has_iterator = requires {
+ typename T::iterator;
+ typename T::const_iterator;
+};
+
template <typename T>
constexpr bool test_range_concept() {
return std::ranges::range<std::optional<T>>;
@@ -90,6 +96,17 @@ constexpr bool test() {
}
constexpr bool tests() {
+ // Verify that iterator is present for object type T, but for T&, that iterator is available only if T is
+ // an object type and is not an unbounded array.
+
+ static_assert(has_iterator<std::optional<int>>);
+ static_assert(has_iterator<std::optional<const int>>);
+ static_assert(has_iterator<std::optional<int&>>);
+ static_assert(has_iterator<std::optional<const int&>>);
+ static_assert(has_iterator<std::optional<int (&)[1]>>);
+ static_assert(!has_iterator<std::optional<int (&)[]>>);
+ static_assert(!has_iterator<std::optional<int (&)()>>);
+
assert((test<int, 1>()));
assert((test<char, 'a'>()));
assert((test<bool, true>()));
@@ -103,7 +120,7 @@ constexpr bool tests() {
assert(!test_range_concept<int (&)()>());
assert(!test_range_concept<int (&)[]>());
- assert(!test_range_concept<int (&)[42]>());
+ assert(test_range_concept<int (&)[42]>());
return true;
}
More information about the libcxx-commits
mailing list