https://gcc.gnu.org/g:774ae8734f0e199a8c6d29dd8c186b893385470b

commit r16-1487-g774ae8734f0e199a8c6d29dd8c186b893385470b
Author: Giuseppe D'Angelo <giuseppe.dang...@kdab.com>
Date:   Mon Jun 9 23:13:21 2025 +0200

    libstdc++: add range support to std::optional (P3168)
    
    This commit implements P3168 ("Give std::optional Range Support"), added
    for C++26. Both begin() and end() are straightforward, implemented using
    normal_iterator over a raw pointer.
    
    std::optional is also a view, so specialize enable_view for it.
    
    We also need to disable automatic formatting a std::optional as a range
    by specializing format_kind. In order to avoid dragging <format> when
    including <optional>, I've isolated format_kind and some supporting code
    into <bits/formatfwd.h> so that I can use that (comparatively) lighter
    header.
    
    libstdc++-v3/ChangeLog:
    
            * include/bits/formatfwd.h (format_kind): Move the definition
            (and some supporting code) from <format>.
            * include/std/format (format_kind): Likewise.
            * include/bits/version.def (optional_range_support): Add
            the feature-testing macro.
            * include/bits/version.h: Regenerate.
            * include/std/optional (iterator, const_iterator, begin, end):
            Add range support.
            (enable_view): Specialize for std::optional.
            (format_kind): Specialize for std::optional.
            * testsuite/20_util/optional/range.cc: New test.
            * testsuite/20_util/optional/version.cc: Test the new
            feature-testing macro.

Diff:
---
 libstdc++-v3/include/bits/formatfwd.h              |  26 ++++
 libstdc++-v3/include/bits/version.def              |   8 +
 libstdc++-v3/include/bits/version.h                |  10 ++
 libstdc++-v3/include/std/format                    |  26 ----
 libstdc++-v3/include/std/optional                  |  47 ++++++
 libstdc++-v3/testsuite/20_util/optional/range.cc   | 163 +++++++++++++++++++++
 libstdc++-v3/testsuite/20_util/optional/version.cc |  18 +++
 7 files changed, 272 insertions(+), 26 deletions(-)

diff --git a/libstdc++-v3/include/bits/formatfwd.h 
b/libstdc++-v3/include/bits/formatfwd.h
index 777e6290f744..314b55d50bcd 100644
--- a/libstdc++-v3/include/bits/formatfwd.h
+++ b/libstdc++-v3/include/bits/formatfwd.h
@@ -162,6 +162,32 @@ namespace __format
     using __maybe_const
       = __conditional_t<formattable<const _Tp, _CharT>, const _Tp, _Tp>;
 }
+
+  // [format.range], formatting of ranges
+  // [format.range.fmtkind], variable template format_kind
+  enum class range_format {
+    disabled,
+    map,
+    set,
+    sequence,
+    string,
+    debug_string
+  };
+
+  /** @brief A constant determining how a range should be formatted.
+   *
+   * The primary template of `std::format_kind` cannot be instantiated.
+   * There is a partial specialization for input ranges and you can
+   * specialize the variable template for your own cv-unqualified types
+   * that satisfy the `ranges::input_range` concept.
+   *
+   * @since C++23
+   */
+  template<typename _Rg>
+    constexpr auto format_kind = []{
+      static_assert(false, "cannot use primary template of 
'std::format_kind'");
+      return type_identity<_Rg>{};
+    }();
 #endif // format_ranges
 
 _GLIBCXX_END_NAMESPACE_VERSION
diff --git a/libstdc++-v3/include/bits/version.def 
b/libstdc++-v3/include/bits/version.def
index 7cf62e989aa7..880586e91260 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -851,6 +851,14 @@ ftms = {
   };
 };
 
+ftms = {
+  name = optional_range_support;
+  values = {
+    v = 202406;
+    cxxmin = 26;
+  };
+};
+
 ftms = {
   name = destroying_delete;
   values = {
diff --git a/libstdc++-v3/include/bits/version.h 
b/libstdc++-v3/include/bits/version.h
index 9f4cf9a3425e..4300adb22762 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -955,6 +955,16 @@
 #endif /* !defined(__cpp_lib_optional) && defined(__glibcxx_want_optional) */
 #undef __glibcxx_want_optional
 
+#if !defined(__cpp_lib_optional_range_support)
+# if (__cplusplus >  202302L)
+#  define __glibcxx_optional_range_support 202406L
+#  if defined(__glibcxx_want_all) || 
defined(__glibcxx_want_optional_range_support)
+#   define __cpp_lib_optional_range_support 202406L
+#  endif
+# endif
+#endif /* !defined(__cpp_lib_optional_range_support) && 
defined(__glibcxx_want_optional_range_support) */
+#undef __glibcxx_want_optional_range_support
+
 #if !defined(__cpp_lib_destroying_delete)
 # if (__cplusplus >= 202002L) && (__cpp_impl_destroying_delete)
 #  define __glibcxx_destroying_delete 201806L
diff --git a/libstdc++-v3/include/std/format b/libstdc++-v3/include/std/format
index 04fb23eb1367..46bd5d5ee6a0 100644
--- a/libstdc++-v3/include/std/format
+++ b/libstdc++-v3/include/std/format
@@ -5483,32 +5483,6 @@ namespace __format
 #endif
 
 #if __glibcxx_format_ranges // C++ >= 23 && HOSTED
-  // [format.range], formatting of ranges
-  // [format.range.fmtkind], variable template format_kind
-  enum class range_format {
-    disabled,
-    map,
-    set,
-    sequence,
-    string,
-    debug_string
-  };
-
-  /** @brief A constant determining how a range should be formatted.
-   *
-   * The primary template of `std::format_kind` cannot be instantiated.
-   * There is a partial specialization for input ranges and you can
-   * specialize the variable template for your own cv-unqualified types
-   * that satisfy the `ranges::input_range` concept.
-   *
-   * @since C++23
-   */
-  template<typename _Rg>
-    constexpr auto format_kind = []{
-      static_assert(false, "cannot use primary template of 
'std::format_kind'");
-      return type_identity<_Rg>{};
-    }();
-
   /// @cond undocumented
   template<typename _Tp>
     consteval range_format
diff --git a/libstdc++-v3/include/std/optional 
b/libstdc++-v3/include/std/optional
index a616dc07b107..2ae71f11e213 100644
--- a/libstdc++-v3/include/std/optional
+++ b/libstdc++-v3/include/std/optional
@@ -36,6 +36,7 @@
 
 #define __glibcxx_want_freestanding_optional
 #define __glibcxx_want_optional
+#define __glibcxx_want_optional_range_support
 #define __glibcxx_want_constrained_equality
 #include <bits/version.h>
 
@@ -57,6 +58,11 @@
 #if __cplusplus > 202002L
 # include <concepts>
 #endif
+#ifdef __cpp_lib_optional_range_support // C++ >= 26
+# include <bits/formatfwd.h>
+# include <bits/ranges_base.h>
+# include <bits/stl_iterator.h>
+#endif
 
 namespace std _GLIBCXX_VISIBILITY(default)
 {
@@ -858,6 +864,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
     public:
       using value_type = _Tp;
+#ifdef __cpp_lib_optional_range_support // >= C++26
+      using iterator = __gnu_cxx::__normal_iterator<_Tp*, optional>;
+      using const_iterator = __gnu_cxx::__normal_iterator<const _Tp*, 
optional>;
+#endif
 
       constexpr optional() noexcept { }
 
@@ -1158,6 +1168,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          }
       }
 
+#ifdef __cpp_lib_optional_range_support // >= C++26
+      // Iterator support.
+      constexpr iterator begin() noexcept
+      {
+       return iterator(
+         this->_M_is_engaged() ? std::addressof(this->_M_get()) : nullptr
+       );
+      }
+
+      constexpr const_iterator begin() const noexcept
+      {
+       return const_iterator(
+         this->_M_is_engaged() ? std::addressof(this->_M_get()) : nullptr
+       );
+      }
+
+      constexpr iterator end() noexcept
+      {
+       return begin() + has_value();
+      }
+
+      constexpr const_iterator end() const noexcept
+      {
+       return begin() + has_value();
+      }
+#endif // __cpp_lib_optional_range_support
+
       // Observers.
       constexpr const _Tp*
       operator->() const noexcept
@@ -1772,6 +1809,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template <typename _Tp> optional(_Tp) -> optional<_Tp>;
 #endif
 
+#ifdef __cpp_lib_optional_range_support // >= C++26
+  template<typename _Tp>
+    inline constexpr bool
+      ranges::enable_view<optional<_Tp>> = true;
+
+  template<typename _Tp>
+    inline constexpr auto
+      format_kind<optional<_Tp>> = range_format::disabled;
+#endif // __cpp_lib_optional_range_support
+
 #undef _GLIBCXX_USE_CONSTRAINTS_FOR_OPTIONAL
 
 _GLIBCXX_END_NAMESPACE_VERSION
diff --git a/libstdc++-v3/testsuite/20_util/optional/range.cc 
b/libstdc++-v3/testsuite/20_util/optional/range.cc
new file mode 100644
index 000000000000..e77dc21e22b3
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/optional/range.cc
@@ -0,0 +1,163 @@
+// { dg-do compile { target c++26 } }
+
+#include <concepts>
+#include <format>
+#include <iterator>
+#include <optional>
+#include <ranges>
+#include <string_view>
+#include <vector>
+
+#include <testsuite_hooks.h>
+
+template<typename O>
+constexpr
+void
+test_range_concepts()
+{
+  static_assert(std::ranges::contiguous_range<O>);
+  static_assert(std::ranges::sized_range<O>);
+  static_assert(std::ranges::common_range<O>);
+  static_assert(!std::ranges::borrowed_range<O>);
+
+  // an optional<const T> is not assignable, and therefore does not satisfy 
ranges::view
+  using T = typename O::value_type;
+  constexpr bool is_const_opt = std::is_const_v<T>;
+  static_assert(std::ranges::view<O> == !is_const_opt);
+  static_assert(std::ranges::viewable_range<O> == !is_const_opt);
+}
+
+template<typename O>
+constexpr
+void
+test_iterator_concepts()
+{
+  using T = typename O::value_type;
+  using iterator = typename O::iterator;
+  static_assert(std::contiguous_iterator<iterator>);
+  static_assert(std::is_same_v<typename 
std::iterator_traits<iterator>::value_type, std::remove_cv_t<T>>);
+  static_assert(std::is_same_v<std::iter_value_t<iterator>, 
std::remove_cv_t<T>>);
+  static_assert(std::is_same_v<typename 
std::iterator_traits<iterator>::reference, T&>);
+  static_assert(std::is_same_v<std::iter_reference_t<iterator>, T&>);
+
+  using const_iterator = typename O::const_iterator;
+  static_assert(std::contiguous_iterator<const_iterator>);
+  static_assert(std::is_same_v<typename 
std::iterator_traits<const_iterator>::value_type, std::remove_cv_t<T>>);
+  static_assert(std::is_same_v<std::iter_value_t<const_iterator>, 
std::remove_cv_t<T>>);
+  static_assert(std::is_same_v<typename 
std::iterator_traits<const_iterator>::reference, const T&>);
+  static_assert(std::is_same_v<std::iter_reference_t<const_iterator>, const 
T&>);
+}
+
+template<typename O>
+constexpr
+void
+test_empty()
+{
+  O empty;
+  VERIFY(!empty);
+  VERIFY(empty.begin() == empty.end());
+  VERIFY(std::as_const(empty).begin() == std::as_const(empty).end());
+  VERIFY(std::ranges::empty(empty));
+  VERIFY(std::ranges::empty(std::as_const(empty)));
+  VERIFY(std::ranges::empty(empty | std::views::as_const));
+  VERIFY(std::ranges::size(empty) == 0);
+  VERIFY(std::ranges::size(std::as_const(empty)) == 0);
+
+  size_t count = 0;
+  for (const auto& x : empty)
+    ++count;
+  VERIFY(count == 0);
+}
+
+template<typename O, typename T>
+constexpr
+void
+test_non_empty(const T& value)
+{
+  O non_empty = std::make_optional(value);
+  VERIFY(non_empty);
+  VERIFY(*non_empty == value);
+  VERIFY(non_empty.begin() != non_empty.end());
+  VERIFY(non_empty.begin() < non_empty.end());
+  VERIFY(std::as_const(non_empty).begin() != std::as_const(non_empty).end());
+  VERIFY(std::as_const(non_empty).begin() < std::as_const(non_empty).end());
+  VERIFY(!std::ranges::empty(non_empty));
+  VERIFY(!std::ranges::empty(std::as_const(non_empty)));
+  VERIFY(!std::ranges::empty(non_empty | std::views::as_const));
+  VERIFY(std::ranges::size(non_empty) == 1);
+  VERIFY(std::ranges::size(std::as_const(non_empty)) == 1);
+
+  size_t count = 0;
+  for (const auto& x : non_empty)
+    ++count;
+  VERIFY(count == 1);
+
+  if constexpr (!std::is_const_v<typename O::value_type>) {
+    for (auto& x : non_empty)
+      x = T{};
+    VERIFY(non_empty);
+    VERIFY(*non_empty == T{});
+  }
+}
+
+template<typename T>
+constexpr
+void
+test(const T& value)
+{
+  using O = std::optional<T>;
+  test_range_concepts<O>();
+  test_iterator_concepts<O>();
+  test_empty<O>();
+  test_non_empty<O>(value);
+  static_assert(!std::formattable<O, char>);
+  static_assert(!std::formattable<O, wchar_t>);
+  static_assert(std::format_kind<O> == std::range_format::disabled);
+}
+
+constexpr
+void
+range_chain_example() // from P3168
+{
+  std::vector<int> v{2, 3, 4, 5, 6, 7, 8, 9, 1};
+  auto test = [](int i) -> std::optional<int> {
+    switch(i) {
+    case 1:
+    case 3:
+    case 7:
+    case 9:
+      return i * 2;
+    default:
+      return {};
+    }
+  };
+
+  auto result = v
+    | std::views::transform(test)
+    | std::views::filter([](auto x) { return bool(x); })
+    | std::views::transform([](auto x){ return *x; })
+    | std::ranges::to<std::vector>();
+
+  bool ok = result == std::vector<int>{6, 14, 18, 2};
+  VERIFY(ok);
+}
+
+constexpr
+bool
+all_tests()
+{
+  test(42);
+  int i = 42;
+  test(&i);
+  test(std::string_view("test"));
+  test(std::vector<int>{1, 2, 3, 4});
+  test(std::optional<int>(42));
+  test<const int>(42);
+
+  range_chain_example();
+
+  return true;
+}
+
+static_assert(all_tests());
+
diff --git a/libstdc++-v3/testsuite/20_util/optional/version.cc 
b/libstdc++-v3/testsuite/20_util/optional/version.cc
index 657a3992422a..ba44aa525356 100644
--- a/libstdc++-v3/testsuite/20_util/optional/version.cc
+++ b/libstdc++-v3/testsuite/20_util/optional/version.cc
@@ -21,8 +21,17 @@
 #endif
 #endif
 
+#if __cplusplus > 202302L
+# ifndef __cpp_lib_optional_range_support
+#  error "Feature test macro for optional range support is missing in 
<version>"
+# elif __cpp_lib_optional_range_support != 202406L
+#  error "Feature test macro for optional range support has wrong value for 
C++26 in <version>"
+# endif
+#endif
+
 #undef __cpp_lib_optional
 #undef __cpp_lib_freestanding_optional
+#undef __cpp_lib_optional_range_support
 #include <optional>
 
 #if __cplusplus >= 202302L
@@ -32,3 +41,12 @@
 # error "Feature test macro for freestanding std::optional has wrong value in 
<optional>"
 #endif
 #endif
+
+#if __cplusplus > 202302L
+# ifndef __cpp_lib_optional_range_support
+#  error "Feature test macro for optional range support is missing in 
<optional>"
+# endif
+# if __cpp_lib_optional_range_support != 202406L
+#  error "Feature test macro for optional range support has wrong value for 
C++26 in <optional>"
+# endif
+#endif

Reply via email to