[libcxx-commits] [libcxx] [libc++][test] Only `views::split(pattern)` can be piped (PR #74961)

via libcxx-commits libcxx-commits at lists.llvm.org
Sat Dec 9 19:02:59 PST 2023


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Stephan T. Lavavej (StephanTLavavej)

<details>
<summary>Changes</summary>

Found while running libc++'s tests with MSVC's STL.

* [N4964](https://isocpp.org/files/papers/N4964.pdf) \[range.split.overview\]/2:
  > The name `views::split` denotes a range adaptor object (\[range.adaptor.object\]). Given subexpressions `E` and `F`, the expression `views::split(E, F)` is expression-equivalent to `split_view(E, F)`.
* \[range.lazy.split.overview\]/2:
  > The name `views::lazy_split` denotes a range adaptor object (\[range.adaptor.object\]). Given subexpressions `E` and `F`, the expression `views::lazy_split(E, F)` is expression-equivalent to `lazy_split_view(E, F)`.
* \[range.adaptor.object\]/1:
  > A *range adaptor closure object* is a unary function object that accepts a range argument. For a range adaptor
closure object `C` and an expression `R` such that `decltype((R))` models `range`, the following expressions are
equivalent:  
  > `C(R)`  
  > `R | C`
* \[range.adaptor.object\]/6-8:
  > A *range adaptor object* is a customization point object (\[customization.point.object\]) that accepts a `viewable_range` as its first argument and returns a view.  
  > If a range adaptor object accepts only one argument, then it is a range adaptor closure object.  
  > If a range adaptor object `adaptor` accepts more than one argument, then let `range` be an expression such that `decltype((range))` models `viewable_range`, let `args...` be arguments such that `adaptor(range, args...)` is a well-formed expression as specified in the rest of subclause \[range.adaptors\], and let `BoundArgs` be a pack that denotes `decay_t<decltype((args))>...`. The expression `adaptor(args...)` produces a range adaptor closure object `f` that is a perfect forwarding call wrapper (\[func.require\]) with the following properties: \[...\]

To summarize: `views::split` and `views::lazy_split` aren't unary, aren't range adaptor **closure** objects, and can't be piped. However, \[range.adaptor.object\]/8 says that `views::split(pattern)` and `views::lazy_split(pattern)` produce unary, pipeable, range adaptor closure objects.

This PR adjusts the test coverage accordingly.

Note: The fact that the tests were previously passing may indicate the existence of a bug (or an extension?) in libc++'s product code. I looked at the machinery briefly but it was far beyond me (I barely understand microsoft/STL's ranges machinery). As this PR changes the tests to be portable, I would like to request that if changes to libc++'s product code are desired, that should be done in a followup PR (and I can file a followup issue if a reminder is desired).

<details><summary>Click to expand the Clang compiler error that discovered this:</summary>

```
D:\GitHub\STL\llvm-project\libcxx\test\std\ranges\range.adaptors\range.split\adaptor.pass.cpp(42,1): error: static assertion failed
static_assert( CanBePiped<SomeView&,    decltype(std::views::split)>);
^              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
D:\GitHub\STL\llvm-project\libcxx\test\std\ranges\range.adaptors\range.split\adaptor.pass.cpp(42,16): note: because 'CanBePiped<SomeView &, decltype(std::views::split)>' evaluated to false
static_assert( CanBePiped<SomeView&,    decltype(std::views::split)>);
                ^
D:\GitHub\STL\llvm-project\libcxx\test\std\ranges\range.adaptors\range.split\adaptor.pass.cpp(25,30): note: because 'std::forward<View>(view) | std::forward<T>(t)' would be invalid: invalid operands to binary expression ('SomeView' and 'const std::ranges::views::_Split_fn')
  { std::forward<View>(view) | std::forward<T>(t) };
                              ^
D:\GitHub\STL\llvm-project\libcxx\test\std\ranges\range.adaptors\range.split\adaptor.pass.cpp(43,1): error: static assertion failed
static_assert( CanBePiped<char(&)[10],  decltype(std::views::split)>);
^              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
D:\GitHub\STL\llvm-project\libcxx\test\std\ranges\range.adaptors\range.split\adaptor.pass.cpp(43,16): note: because 'CanBePiped<char (&)[10], decltype(std::views::split)>' evaluated to false
static_assert( CanBePiped<char(&)[10],  decltype(std::views::split)>);
                ^
D:\GitHub\STL\llvm-project\libcxx\test\std\ranges\range.adaptors\range.split\adaptor.pass.cpp(25,30): note: because 'std::forward<View>(view) | std::forward<T>(t)' would be invalid: invalid operands to binary expression ('char[10]' and 'const std::ranges::views::_Split_fn')
  { std::forward<View>(view) | std::forward<T>(t) };
                              ^
```
</details>


---
Full diff: https://github.com/llvm/llvm-project/pull/74961.diff


2 Files Affected:

- (modified) libcxx/test/std/ranges/range.adaptors/range.lazy.split/adaptor.pass.cpp (+4-4) 
- (modified) libcxx/test/std/ranges/range.adaptors/range.split/adaptor.pass.cpp (+4-4) 


``````````diff
diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/adaptor.pass.cpp
index da4bd9fbbe1794..58be3713263c73 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/adaptor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/adaptor.pass.cpp
@@ -40,10 +40,10 @@ static_assert(!std::is_invocable_v<decltype(std::views::lazy_split), SomeView, N
 static_assert(!std::is_invocable_v<decltype(std::views::lazy_split), NotAView, SomeView>);
 static_assert( std::is_invocable_v<decltype(std::views::lazy_split), SomeView, SomeView>);
 
-static_assert( CanBePiped<SomeView&,    decltype(std::views::lazy_split)>);
-static_assert( CanBePiped<char(&)[10],  decltype(std::views::lazy_split)>);
-static_assert(!CanBePiped<char(&&)[10], decltype(std::views::lazy_split)>);
-static_assert(!CanBePiped<NotAView,     decltype(std::views::lazy_split)>);
+static_assert( CanBePiped<SomeView&,    decltype(std::views::lazy_split('x'))>);
+static_assert( CanBePiped<char(&)[10],  decltype(std::views::lazy_split('x'))>);
+static_assert(!CanBePiped<char(&&)[10], decltype(std::views::lazy_split('x'))>);
+static_assert(!CanBePiped<NotAView,     decltype(std::views::lazy_split('x'))>);
 
 static_assert(std::same_as<decltype(std::views::lazy_split), decltype(std::ranges::views::lazy_split)>);
 
diff --git a/libcxx/test/std/ranges/range.adaptors/range.split/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.split/adaptor.pass.cpp
index cd12011daeab5d..4b13d1b361198f 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.split/adaptor.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.split/adaptor.pass.cpp
@@ -39,10 +39,10 @@ static_assert(!std::is_invocable_v<decltype(std::views::split), SomeView, NotAVi
 static_assert(!std::is_invocable_v<decltype(std::views::split), NotAView, SomeView>);
 static_assert( std::is_invocable_v<decltype(std::views::split), SomeView, SomeView>);
 
-static_assert( CanBePiped<SomeView&,    decltype(std::views::split)>);
-static_assert( CanBePiped<char(&)[10],  decltype(std::views::split)>);
-static_assert(!CanBePiped<char(&&)[10], decltype(std::views::split)>);
-static_assert(!CanBePiped<NotAView,     decltype(std::views::split)>);
+static_assert( CanBePiped<SomeView&,    decltype(std::views::split('x'))>);
+static_assert( CanBePiped<char(&)[10],  decltype(std::views::split('x'))>);
+static_assert(!CanBePiped<char(&&)[10], decltype(std::views::split('x'))>);
+static_assert(!CanBePiped<NotAView,     decltype(std::views::split('x'))>);
 
 static_assert(std::same_as<decltype(std::views::split), decltype(std::ranges::views::split)>);
 

``````````

</details>


https://github.com/llvm/llvm-project/pull/74961


More information about the libcxx-commits mailing list