Skip to content

Commit bd0c0e5

Browse files
committed
[libc++] [ranges] SFINAE-friendly "write it three times" in views::counted.
Before this patch, the new test's `CountedInvocable<int*, int*>` would hard-error instead of SFINAEing and cleanly returning false. Notice that views::counted specifically does NOT work with pipes; `counted(42)` is ill-formed. This is because `counted`'s first argument is supposed to be an iterator, not a range. Also, mark `views::counted(it, n)` as [[nodiscard]], and test that. (We have a general policy now that range adaptors are consistently marked [[nodiscard]], so that people don't accidentally think that they have side effects. This matters mostly for `reverse` and `transform`, arguably `drop`, and just generally let's be consistent.) Differential Revision: https://reviews.llvm.org/D115177
1 parent 7a06a14 commit bd0c0e5

File tree

4 files changed

+241
-197
lines changed

4 files changed

+241
-197
lines changed

libcxx/include/__ranges/counted.h

Lines changed: 28 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,15 @@
99
#ifndef _LIBCPP___RANGES_COUNTED_H
1010
#define _LIBCPP___RANGES_COUNTED_H
1111

12+
#include <__concepts/convertible_to.h>
1213
#include <__config>
1314
#include <__iterator/concepts.h>
1415
#include <__iterator/counted_iterator.h>
1516
#include <__iterator/default_sentinel.h>
1617
#include <__iterator/incrementable_traits.h>
1718
#include <__iterator/iterator_traits.h>
1819
#include <__memory/pointer_traits.h>
19-
#include <__ranges/concepts.h>
2020
#include <__ranges/subrange.h>
21-
#include <__utility/decay_copy.h>
22-
#include <__utility/declval.h>
2321
#include <__utility/forward.h>
2422
#include <__utility/move.h>
2523
#include <span>
@@ -36,50 +34,39 @@ _LIBCPP_BEGIN_NAMESPACE_STD
3634
namespace ranges::views {
3735

3836
namespace __counted {
39-
template<class _From, class _To>
40-
concept __explicitly_convertible = requires {
41-
_To(_From{});
42-
};
4337

4438
struct __fn {
45-
template<class _Iter, class _Diff>
46-
requires contiguous_iterator<decay_t<_Iter>> &&
47-
__explicitly_convertible<_Diff, iter_difference_t<_Iter>>
39+
template<contiguous_iterator _It>
4840
_LIBCPP_HIDE_FROM_ABI
49-
constexpr auto operator()(_Iter&& __it, _Diff __c) const
50-
noexcept(noexcept(
51-
span(_VSTD::to_address(__it), static_cast<iter_difference_t<_Iter>>(__c))
52-
))
53-
{
54-
return span(_VSTD::to_address(__it), static_cast<iter_difference_t<_Iter>>(__c));
55-
}
56-
57-
template<class _Iter, class _Diff>
58-
requires random_access_iterator<decay_t<_Iter>> &&
59-
__explicitly_convertible<_Diff, iter_difference_t<_Iter>>
41+
static constexpr auto __go(_It __it, iter_difference_t<_It> __count)
42+
noexcept(noexcept(span(_VSTD::to_address(__it), static_cast<size_t>(__count))))
43+
// Deliberately omit return-type SFINAE, because to_address is not SFINAE-friendly
44+
{ return span(_VSTD::to_address(__it), static_cast<size_t>(__count)); }
45+
46+
template<random_access_iterator _It>
6047
_LIBCPP_HIDE_FROM_ABI
61-
constexpr auto operator()(_Iter&& __it, _Diff __c) const
62-
noexcept(
63-
noexcept(__it + static_cast<iter_difference_t<_Iter>>(__c)) &&
64-
noexcept(ranges::subrange(_VSTD::forward<_Iter>(__it), _VSTD::__decay_copy(__it)))
65-
)
66-
{
67-
auto __last = __it + static_cast<iter_difference_t<_Iter>>(__c);
68-
return ranges::subrange(_VSTD::forward<_Iter>(__it), _VSTD::move(__last));
69-
}
70-
71-
template<class _Iter, class _Diff>
72-
requires __explicitly_convertible<_Diff, iter_difference_t<_Iter>>
48+
static constexpr auto __go(_It __it, iter_difference_t<_It> __count)
49+
noexcept(noexcept(subrange(__it, __it + __count)))
50+
-> decltype( subrange(__it, __it + __count))
51+
{ return subrange(__it, __it + __count); }
52+
53+
template<class _It>
7354
_LIBCPP_HIDE_FROM_ABI
74-
constexpr auto operator()(_Iter&& __it, _Diff __c) const
75-
noexcept(noexcept(
76-
ranges::subrange(counted_iterator(_VSTD::forward<_Iter>(__it), __c), default_sentinel)
77-
))
78-
{
79-
return ranges::subrange(counted_iterator(_VSTD::forward<_Iter>(__it), __c), default_sentinel);
80-
}
55+
static constexpr auto __go(_It __it, iter_difference_t<_It> __count)
56+
noexcept(noexcept(subrange(counted_iterator(_VSTD::move(__it), __count), default_sentinel)))
57+
-> decltype( subrange(counted_iterator(_VSTD::move(__it), __count), default_sentinel))
58+
{ return subrange(counted_iterator(_VSTD::move(__it), __count), default_sentinel); }
59+
60+
template<class _It, convertible_to<iter_difference_t<_It>> _Diff>
61+
requires input_or_output_iterator<decay_t<_It>>
62+
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI
63+
constexpr auto operator()(_It&& __it, _Diff&& __count) const
64+
noexcept(noexcept(__go(_VSTD::forward<_It>(__it), _VSTD::forward<_Diff>(__count))))
65+
-> decltype( __go(_VSTD::forward<_It>(__it), _VSTD::forward<_Diff>(__count)))
66+
{ return __go(_VSTD::forward<_It>(__it), _VSTD::forward<_Diff>(__count)); }
8167
};
82-
}
68+
69+
} // namespace __counted
8370

8471
inline namespace __cpo {
8572
inline constexpr auto counted = __counted::__fn{};
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
// UNSUPPORTED: c++03, c++11, c++14, c++17
10+
// UNSUPPORTED: libcpp-no-concepts
11+
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
12+
13+
// Test the libc++ extension that std::views::counted is marked as [[nodiscard]].
14+
15+
#include <ranges>
16+
17+
void test() {
18+
int range[] = {1, 2, 3};
19+
20+
std::views::counted(range, 1); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
21+
}

0 commit comments

Comments
 (0)