[PATCH 2/2] libstdc++: Some concat_view bugfixes [PR115215, PR115218, LWG 4082]

Patrick Palka ppalka@redhat.com
Wed Mar 5 15:35:08 GMT 2025


On Mon, 17 Feb 2025, Patrick Palka wrote:

> Tested on x86_64-pc-linux-gnu, does this look OK for trunk?
> 
> -- >8 --
> 
> - Use __builtin_unreachable to suppress a false-positive "control
>   reaches end of non-void function" warning in the recursive lambda
>   (which the existing tests failed to notice since test01 wasn't
>   being called at runtime)
> - Relax the constraints on views::concat in the single-argument case
>   as per PR115215
> - Add an input_range requirement to that same case as per LWG 4082
> - In the const-converting constructor of concat_view's iterator,
>   don't require the first iterator to be default constructible
> 
> 	PR libstdc++/115215
> 	PR libstdc++/115218
> 
> libstdc++-v3/ChangeLog:
> 
> 	* include/std/ranges
> 	(concat_view::iterator::_S_invoke_with_runtime_index): Use
> 	__builtin_unreachable in recursive lambda to certify it always
> 	exits via 'return'.
> 	(concat_view::iterator::iterator): In the const-converting
> 	constructor, direct initialize _M_it.
> 	(views::_Concat::operator()): Adjust constraints in the
> 	single-argument case as per LWG 4082.
> 	* testsuite/std/ranges/concat/1.cc (test01): Call it at runtime
> 	too.
> 	(test04): New test.
> ---
>  libstdc++-v3/include/std/ranges               | 28 +++++++++----------
>  libstdc++-v3/testsuite/std/ranges/concat/1.cc | 16 +++++++++++
>  2 files changed, 30 insertions(+), 14 deletions(-)
> 
> diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
> index 22e0c9cae44..a56dae43625 100644
> --- a/libstdc++-v3/include/std/ranges
> +++ b/libstdc++-v3/include/std/ranges
> @@ -9919,6 +9919,7 @@ namespace ranges
>  	    return __f.template operator()<_Idx>();
>  	  if constexpr (_Idx + 1 < sizeof...(_Vs))
>  	    return __self.template operator()<_Idx + 1>();
> +	  __builtin_unreachable();
>  	}.template operator()<0>();
>        }
>  
> @@ -9940,12 +9941,12 @@ namespace ranges
>      constexpr
>      iterator(iterator<!_Const> __it)
>        requires _Const && (convertible_to<iterator_t<_Vs>, iterator_t<const _Vs>> && ...)
> -    : _M_parent(__it._M_parent)
> -    {
> -      _M_invoke_with_runtime_index([this, &__it]<size_t _Idx>() {
> -	_M_it.template emplace<_Idx>(std::get<_Idx>(std::move(__it._M_it)));
> -      });
> -    }
> +    : _M_parent(__it._M_parent),
> +      _M_it(_S_invoke_with_runtime_index([this, &__it]<size_t _Idx>() {
> +	      return __base_iter(in_place_index<_Idx>,
> +				 std::get<_Idx>(std::move(__it._M_it)));
> +	    }, __it._M_it.index()))
> +    { }
>  
>      constexpr decltype(auto)
>      operator*() const
> @@ -10179,16 +10180,15 @@ namespace ranges
>  
>      struct _Concat
>      {
> -      template<typename... _Ts>
> -	requires __detail::__can_concat_view<_Ts...>
> +      template<__detail::__can_concat_view... _Ts>

As pointed out by Tomasz, this change is incorrect and unnecessary.
The former checks __can_concat_view<_Ts...> whereas the latter checks
(__can_concat_view<Ts> && ...).  Here's v2 with this change reverted:

-- >8 --

Subject: [PATCH v2] libstdc++: Some concat_view bugfixes [PR115215, PR115218, LWG
 4082]

- Use __builtin_unreachable to suppress a false-positive "control
  reaches end of non-void function" warning in the recursive lambda
  (which the existing tests failed to notice since test01 wasn't
  being called at runtime)
- Relax the constraints on views::concat in the single-argument case
  as per PR115215
- Add an input_range requirement to that same case as per LWG 4082
- In the const-converting constructor of concat_view's iterator,
  don't require the first iterator to be default constructible

	PR libstdc++/115215
	PR libstdc++/115218

libstdc++-v3/ChangeLog:

	* include/std/ranges
	(concat_view::iterator::_S_invoke_with_runtime_index): Use
	__builtin_unreachable in recursive lambda to certify it always
	exits via 'return'.
	(concat_view::iterator::iterator): In the const-converting
	constructor, direct initialize _M_it.
	(views::_Concat::operator()): Adjust constraints in the
	single-argument case as per LWG 4082.
	* testsuite/std/ranges/concat/1.cc (test01): Call it at runtime
	too.
	(test04): New test.
---
 libstdc++-v3/include/std/ranges               | 25 ++++++++++---------
 libstdc++-v3/testsuite/std/ranges/concat/1.cc | 16 ++++++++++++
 2 files changed, 29 insertions(+), 12 deletions(-)

diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
index 6c65722b687..2228ab8c5ee 100644
--- a/libstdc++-v3/include/std/ranges
+++ b/libstdc++-v3/include/std/ranges
@@ -9919,6 +9919,7 @@ namespace ranges
 	    return __f.template operator()<_Idx>();
 	  if constexpr (_Idx + 1 < sizeof...(_Vs))
 	    return __self.template operator()<_Idx + 1>();
+	  __builtin_unreachable();
 	}.template operator()<0>();
       }
 
@@ -9940,12 +9941,12 @@ namespace ranges
     constexpr
     _Iterator(_Iterator<!_Const> __it)
       requires _Const && (convertible_to<iterator_t<_Vs>, iterator_t<const _Vs>> && ...)
-    : _M_parent(__it._M_parent)
-    {
-      _M_invoke_with_runtime_index([this, &__it]<size_t _Idx>() {
-	_M_it.template emplace<_Idx>(std::get<_Idx>(std::move(__it._M_it)));
-      });
-    }
+    : _M_parent(__it._M_parent),
+      _M_it(_S_invoke_with_runtime_index([this, &__it]<size_t _Idx>() {
+	      return __base_iter(in_place_index<_Idx>,
+				 std::get<_Idx>(std::move(__it._M_it)));
+	    }, __it._M_it.index()))
+    { }
 
     constexpr decltype(auto)
     operator*() const
@@ -10183,12 +10184,12 @@ namespace ranges
 	requires __detail::__can_concat_view<_Ts...>
       constexpr auto
       operator() [[nodiscard]] (_Ts&&... __ts) const
-      {
-	if constexpr (sizeof...(_Ts) == 1)
-	  return views::all(std::forward<_Ts>(__ts)...);
-	else
-	  return concat_view(std::forward<_Ts>(__ts)...);
-      }
+      { return concat_view(std::forward<_Ts>(__ts)...); }
+
+      template<input_range _Range>
+      constexpr auto
+      operator() [[nodiscard]] (_Range&& __t) const
+      { return views::all(std::forward<_Range>(__t)); }
     };
 
     inline constexpr _Concat concat;
diff --git a/libstdc++-v3/testsuite/std/ranges/concat/1.cc b/libstdc++-v3/testsuite/std/ranges/concat/1.cc
index e5d10f476e9..16721912a37 100644
--- a/libstdc++-v3/testsuite/std/ranges/concat/1.cc
+++ b/libstdc++-v3/testsuite/std/ranges/concat/1.cc
@@ -85,10 +85,26 @@ test03()
   VERIFY( ranges::equal(view2, std::vector{4, 5, 6, 1, 2, 3}) );
 }
 
+void
+test04()
+{
+  // PR libstdc++/115215 - views::concat rejects non-movable reference
+  int x[] = {1,2,3};
+  struct nomove {
+    nomove() = default;
+    nomove(const nomove&) = delete;
+  };
+  auto v = x | views::transform([](int) { return nomove{}; });
+  using type = decltype(views::concat(v));
+  using type = decltype(v);
+}
+
 int
 main()
 {
   static_assert(test01());
+  test01();
   test02();
   test03();
+  test04();
 }
-- 
2.49.0.rc0.57.gdb91954e18



More information about the Libstdc++ mailing list