[libcxx-commits] [libcxx] d1f65cd - [libc++] Resolve LWG4308, correct `iterator` availability for `optional<T&>` (#173948)
via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Dec 30 21:39:05 PST 2025
Author: William Tran-Viet
Date: 2025-12-31T13:39:00+08:00
New Revision: d1f65cd584c5bd14f826e805bc54732ebb4488cb
URL: https://github.com/llvm/llvm-project/commit/d1f65cd584c5bd14f826e805bc54732ebb4488cb
DIFF: https://github.com/llvm/llvm-project/commit/d1f65cd584c5bd14f826e805bc54732ebb4488cb.diff
LOG: [libc++] Resolve LWG4308, correct `iterator` availability for `optional<T&>` (#173948)
Resolves #171345
Implements [proposed resolution for
LWG4308](https://cplusplus.github.io/LWG/issue4308) and removes
`const_iterator` from `optional<T&>`, which was missed.
- 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
- Add a partial specialization for `__optional_iterator` for `T&`, which
only has the `iterator` type.
- Correct a static assert message as a drive-by
- Move the libcxx specific iterator test into the standard test because
the standard now specifies when the iterator should be available
Added:
Modified:
libcxx/docs/Status/Cxx2cIssues.csv
libcxx/include/optional
libcxx/test/std/utilities/optional/optional.iterator/iterator.pass.cpp
Removed:
libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp
################################################################################
diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index 76015ea53d0ba..32f5117a0198a 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -184,7 +184,7 @@
"`LWG4302 <https://wg21.link/LWG4302>`__","Problematic ``vector_sum_of_squares`` wording","2025-11 (Kona)","","","`#171342 <https://github.com/llvm/llvm-project/issues/171342>`__",""
"`LWG4304 <https://wg21.link/LWG4304>`__","``std::optional<NonReturnable&>`` is ill-formed due to ``value_or``","2025-11 (Kona)","","","`#171343 <https://github.com/llvm/llvm-project/issues/171343>`__",""
"`LWG4305 <https://wg21.link/LWG4305>`__","Missing user requirements on ``type_order`` template","2025-11 (Kona)","","","`#171344 <https://github.com/llvm/llvm-project/issues/171344>`__",""
-"`LWG4308 <https://wg21.link/LWG4308>`__","``std::optional<T&>::iterator`` can't be a contiguous iterator for some ``T``","2025-11 (Kona)","","","`#171345 <https://github.com/llvm/llvm-project/issues/171345>`__",""
+"`LWG4308 <https://wg21.link/LWG4308>`__","``std::optional<T&>::iterator`` can't be a contiguous iterator for some ``T``","2025-11 (Kona)","|Complete|","22","`#171345 <https://github.com/llvm/llvm-project/issues/171345>`__",""
"`LWG4312 <https://wg21.link/LWG4312>`__","Const and value category mismatch for ``allocator_arg_t``/``allocator_arg`` in the description of uses-allocator construction","2025-11 (Kona)","","","`#171346 <https://github.com/llvm/llvm-project/issues/171346>`__",""
"`LWG4315 <https://wg21.link/LWG4315>`__","Insufficient specification of ``vector_two_norm`` and ``matrix_frob_norm``","2025-11 (Kona)","","","`#171347 <https://github.com/llvm/llvm-project/issues/171347>`__",""
"`LWG4316 <https://wg21.link/LWG4316>`__","``{can_}substitute`` specification is ill-formed","2025-11 (Kona)","","","`#171348 <https://github.com/llvm/llvm-project/issues/171348>`__",""
diff --git a/libcxx/include/optional b/libcxx/include/optional
index 440fdf73a4310..bea7474d49298 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");
}
@@ -681,17 +681,15 @@ struct __is_std_optional<optional<_Tp>> : true_type {};
template <class _Tp, class = void>
struct __optional_iterator {};
+# if _LIBCPP_STD_VER >= 26
+
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>>)> > {
+struct __optional_iterator<_Tp, enable_if_t<!is_lvalue_reference_v<_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>>;
+ using __pointer _LIBCPP_NODEBUG = add_pointer_t<_Tp>;
+ using __const_pointer _LIBCPP_NODEBUG = add_pointer_t<const _Tp>;
public:
-# if _LIBCPP_STD_VER >= 26
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
using iterator = __bounded_iter<__wrap_iter<__pointer>>;
using const_iterator = __bounded_iter<__wrap_iter<__const_pointer>>;
@@ -703,12 +701,7 @@ public:
// [optional.iterators], iterator support
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
auto& __derived_self = static_cast<optional<_Tp>&>(*this);
- auto __ptr = [&__derived_self]() {
- if constexpr (is_lvalue_reference_v<_Tp>) {
- return __derived_self.has_value() ? std::addressof(__derived_self.__get()) : nullptr;
- }
- return std::addressof(__derived_self.__get());
- }();
+ auto* __ptr = std::addressof(__derived_self.__get());
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
return std::__make_bounded_iter(
@@ -722,12 +715,7 @@ public:
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
auto& __derived_self = static_cast<const optional<_Tp>&>(*this);
- auto* __ptr = [&__derived_self]() {
- if constexpr (is_lvalue_reference_v<_Tp>) {
- return __derived_self.has_value() ? std::addressof(__derived_self.__get()) : nullptr;
- }
- return std::addressof(__derived_self.__get());
- }();
+ auto* __ptr = std::addressof(__derived_self.__get());
# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
return std::__make_bounded_iter(
@@ -745,9 +733,43 @@ public:
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept {
return begin() + (static_cast<const optional<_Tp>&>(*this).has_value() ? 1 : 0);
}
-# endif
};
+template <class _Tp>
+struct __optional_iterator<_Tp&, enable_if_t<is_object_v<_Tp> && !__is_unbounded_array_v<_Tp> >> {
+private:
+ using __pointer _LIBCPP_NODEBUG = add_pointer_t<_Tp>;
+
+public:
+# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+ using iterator = __bounded_iter<__wrap_iter<__pointer>>;
+# else
+ using iterator = __wrap_iter<__pointer>;
+# endif
+
+ // [optional.ref.iterators], iterator support
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const noexcept {
+ auto& __derived_self = static_cast<const optional<_Tp&>&>(*this);
+ auto* __ptr = __derived_self.has_value() ? std::addressof(__derived_self.__get()) : nullptr;
+
+# ifdef _LIBCPP_ABI_BOUNDED_ITERATORS_IN_OPTIONAL
+ return std::__make_bounded_iter(
+ __wrap_iter<__pointer>(__ptr),
+ __wrap_iter<__pointer>(__ptr),
+ __wrap_iter<__pointer>(__ptr) + (__derived_self.has_value() ? 1 : 0));
+# else
+ return iterator(__ptr);
+# endif
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const noexcept {
+ return begin() + (static_cast<const optional<_Tp&>&>(*this).has_value() ? 1 : 0);
+ }
+};
+
+# endif // _LIBCPP_STD_VER >= 26
+
template <class _Tp>
class _LIBCPP_DECLSPEC_EMPTY_BASES optional
: private __optional_move_assign_base<_Tp>,
diff --git a/libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp b/libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp
deleted file mode 100644
index b604579e43557..0000000000000
--- a/libcxx/test/libcxx/utilities/optional/optional.iterator/iterator.compile.pass.cpp
+++ /dev/null
@@ -1,29 +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 (&)()>>);
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..58a989767c961 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,21 @@
#include <type_traits>
#include <utility>
+template <typename T>
+concept has_iterator = requires { typename T::iterator; };
+
+template <typename T>
+concept has_const_iterator = requires { typename T::const_iterator; };
+
+template <typename T>
+concept has_both_iterators = has_iterator<T> && has_const_iterator<T>;
+
+template <typename T>
+concept only_has_iterator = has_iterator<T> && !has_const_iterator<T>;
+
+template <typename T>
+concept has_no_iterators = !has_iterator<T> && !has_const_iterator<T>;
+
template <typename T>
constexpr bool test_range_concept() {
return std::ranges::range<std::optional<T>>;
@@ -56,7 +71,11 @@ constexpr bool test() {
assert((std::is_same_v<typename decltype(it)::value_type, std::remove_cvref_t<T>>));
assert((std::is_same_v<typename decltype(it)::reference, std::remove_reference_t<T>&>));
assert((std::is_same_v<typename decltype(it2)::value_type, std::remove_cvref_t<T>>));
- assert((std::is_same_v<typename decltype(it2)::reference, const std::remove_reference_t<T>&>));
+
+ // for optional<T&>, there is no const_iterator
+ if (!std::is_lvalue_reference_v<T>) {
+ assert((std::is_same_v<typename decltype(it2)::reference, const std::remove_reference_t<T>&>));
+ }
}
{ // std::ranges::size for an engaged optional<T> == 1, disengaged optional<T> == 0
@@ -90,6 +109,17 @@ constexpr bool test() {
}
constexpr bool tests() {
+ // Verify that iterator and const_iterator are present for object type T, but for T&,
+ // that only iterator is available iff T is an object type and is not an unbounded array.
+
+ static_assert(has_both_iterators<std::optional<int>>);
+ static_assert(has_both_iterators<std::optional<const int>>);
+ static_assert(only_has_iterator<std::optional<int&>>);
+ static_assert(only_has_iterator<std::optional<const int&>>);
+ static_assert(only_has_iterator<std::optional<int (&)[1]>>);
+ static_assert(has_no_iterators<std::optional<int (&)[]>>);
+ static_assert(has_no_iterators<std::optional<int (&)()>>);
+
assert((test<int, 1>()));
assert((test<char, 'a'>()));
assert((test<bool, true>()));
@@ -103,7 +133,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